1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <set>
6 
7 #include "base/bind.h"
8 #include "base/containers/flat_set.h"
9 #include "base/logging.h"
10 #include "base/memory/singleton.h"
11 #include "base/stl_util.h"
12 #include "base/task_runner_util.h"
13 #include "base/threading/sequenced_task_runner_handle.h"
14 #include "chromeos/dbus/dbus_thread_manager.h"
15 #include "chromeos/dbus/power/power_manager_client.h"
16 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
17 #include "components/arc/arc_service_manager.h"
18 #include "components/arc/session/arc_bridge_service.h"
19 #include "components/arc/timer/arc_timer_bridge.h"
20 #include "components/arc/timer/arc_timer_mojom_traits.h"
21 #include "mojo/public/cpp/system/handle.h"
22 #include "mojo/public/cpp/system/platform_handle.h"
23 
24 namespace arc {
25 
26 namespace {
27 
28 // Tag to be used with the powerd timer API.
29 constexpr char kTag[] = "ARC";
30 
ConvertBoolResultToMojo(bool result)31 mojom::ArcTimerResult ConvertBoolResultToMojo(bool result) {
32   return result ? mojom::ArcTimerResult::SUCCESS
33                 : mojom::ArcTimerResult::FAILURE;
34 }
35 
36 // Callback for powerd API called in |StartTimer|.
OnStartTimer(mojom::TimerHost::StartTimerCallback callback,bool result)37 void OnStartTimer(mojom::TimerHost::StartTimerCallback callback, bool result) {
38   std::move(callback).Run(ConvertBoolResultToMojo(result));
39 }
40 
41 // Unwraps a mojo handle to a file descriptor on the system.
UnwrapScopedHandle(mojo::ScopedHandle handle)42 base::ScopedFD UnwrapScopedHandle(mojo::ScopedHandle handle) {
43   base::ScopedPlatformFile platform_file;
44   if (mojo::UnwrapPlatformFile(std::move(handle), &platform_file) !=
45       MOJO_RESULT_OK) {
46     LOG(ERROR) << "Failed to unwrap mojo handle";
47   }
48   return platform_file;
49 }
50 
51 // Returns true iff |arc_timer_requests| contains duplicate clock id values.
ContainsDuplicateClocks(const std::vector<arc::mojom::CreateTimerRequestPtr> & arc_timer_requests)52 bool ContainsDuplicateClocks(
53     const std::vector<arc::mojom::CreateTimerRequestPtr>& arc_timer_requests) {
54   std::set<clockid_t> seen_clock_ids;
55   for (const auto& request : arc_timer_requests) {
56     if (!seen_clock_ids.emplace(request->clock_id).second)
57       return true;
58   }
59   return false;
60 }
61 
62 // Singleton factory for ArcTimerBridge.
63 class ArcTimerBridgeFactory
64     : public internal::ArcBrowserContextKeyedServiceFactoryBase<
65           ArcTimerBridge,
66           ArcTimerBridgeFactory> {
67  public:
68   // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
69   static constexpr const char* kName = "ArcTimerBridgeFactory";
70 
GetInstance()71   static ArcTimerBridgeFactory* GetInstance() {
72     return base::Singleton<ArcTimerBridgeFactory>::get();
73   }
74 
75  private:
76   friend base::DefaultSingletonTraits<ArcTimerBridgeFactory>;
77   ArcTimerBridgeFactory() = default;
78   ~ArcTimerBridgeFactory() override = default;
79 };
80 
81 }  // namespace
82 
83 // static
GetFactory()84 BrowserContextKeyedServiceFactory* ArcTimerBridge::GetFactory() {
85   return ArcTimerBridgeFactory::GetInstance();
86 }
87 
88 // static
GetForBrowserContext(content::BrowserContext * context)89 ArcTimerBridge* ArcTimerBridge::GetForBrowserContext(
90     content::BrowserContext* context) {
91   return ArcTimerBridgeFactory::GetForBrowserContext(context);
92 }
93 
94 // static
GetForBrowserContextForTesting(content::BrowserContext * context)95 ArcTimerBridge* ArcTimerBridge::GetForBrowserContextForTesting(
96     content::BrowserContext* context) {
97   return ArcTimerBridgeFactory::GetForBrowserContextForTesting(context);
98 }
99 
ArcTimerBridge(content::BrowserContext * context,ArcBridgeService * bridge_service)100 ArcTimerBridge::ArcTimerBridge(content::BrowserContext* context,
101                                ArcBridgeService* bridge_service)
102     : arc_bridge_service_(bridge_service) {
103   arc_bridge_service_->timer()->SetHost(this);
104   arc_bridge_service_->timer()->AddObserver(this);
105 }
106 
~ArcTimerBridge()107 ArcTimerBridge::~ArcTimerBridge() {
108   arc_bridge_service_->timer()->RemoveObserver(this);
109   arc_bridge_service_->timer()->SetHost(nullptr);
110 }
111 
OnConnectionClosed()112 void ArcTimerBridge::OnConnectionClosed() {
113   DeleteArcTimers();
114 }
115 
CreateTimers(std::vector<arc::mojom::CreateTimerRequestPtr> arc_timer_requests,CreateTimersCallback callback)116 void ArcTimerBridge::CreateTimers(
117     std::vector<arc::mojom::CreateTimerRequestPtr> arc_timer_requests,
118     CreateTimersCallback callback) {
119   // Duplicate clocks are not allowed.
120   if (ContainsDuplicateClocks(arc_timer_requests)) {
121     std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
122     return;
123   }
124 
125   // Convert mojo arguments to D-Bus arguments required by powerd to create
126   // timers.
127   std::vector<std::pair<clockid_t, base::ScopedFD>> requests;
128   std::vector<clockid_t> clock_ids;
129   for (auto& request : arc_timer_requests) {
130     clockid_t clock_id = request->clock_id;
131     base::ScopedFD expiration_fd =
132         UnwrapScopedHandle(std::move(request->expiration_fd));
133     if (!expiration_fd.is_valid()) {
134       LOG(ERROR) << "Unwrapped expiration fd is invalid for clock=" << clock_id;
135       std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
136       return;
137     }
138     requests.emplace_back(clock_id, std::move(expiration_fd));
139     clock_ids.emplace_back(clock_id);
140   }
141   chromeos::PowerManagerClient::Get()->CreateArcTimers(
142       kTag, std::move(requests),
143       base::BindOnce(&ArcTimerBridge::OnCreateArcTimers,
144                      weak_ptr_factory_.GetWeakPtr(), std::move(clock_ids),
145                      std::move(callback)));
146 }
147 
StartTimer(clockid_t clock_id,base::TimeTicks absolute_expiration_time,StartTimerCallback callback)148 void ArcTimerBridge::StartTimer(clockid_t clock_id,
149                                 base::TimeTicks absolute_expiration_time,
150                                 StartTimerCallback callback) {
151   auto timer_id = GetTimerId(clock_id);
152   if (!timer_id.has_value()) {
153     LOG(ERROR) << "Timer for clock=" << clock_id << " not created";
154     std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
155     return;
156   }
157   chromeos::PowerManagerClient::Get()->StartArcTimer(
158       timer_id.value(), absolute_expiration_time,
159       base::BindOnce(&OnStartTimer, std::move(callback)));
160 }
161 
DeleteArcTimers()162 void ArcTimerBridge::DeleteArcTimers() {
163   chromeos::PowerManagerClient::Get()->DeleteArcTimers(
164       kTag, base::BindOnce(&ArcTimerBridge::OnDeleteArcTimers,
165                            weak_ptr_factory_.GetWeakPtr()));
166 }
167 
OnDeleteArcTimers(bool result)168 void ArcTimerBridge::OnDeleteArcTimers(bool result) {
169   if (!result) {
170     LOG(ERROR) << "Delete timers failed";
171     return;
172   }
173 
174   // If the delete call succeeded then delete any timer ids stored and make a
175   // create timers call.
176   DVLOG(1) << "Delete timers succeeded";
177   timer_ids_.clear();
178 }
179 
OnCreateArcTimers(std::vector<clockid_t> clock_ids,CreateTimersCallback callback,base::Optional<std::vector<TimerId>> timer_ids)180 void ArcTimerBridge::OnCreateArcTimers(
181     std::vector<clockid_t> clock_ids,
182     CreateTimersCallback callback,
183     base::Optional<std::vector<TimerId>> timer_ids) {
184   // Any old timers associated with the same tag are always cleared by the API
185   // regardless of the new timers being created successfully or not. Clear the
186   // cached timer ids in that case.
187   timer_ids_.clear();
188 
189   // The API returns a list of timer ids corresponding to each clock in
190   // |clock_ids|.
191   if (!timer_ids.has_value()) {
192     LOG(ERROR) << "Create timers failed";
193     std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
194     return;
195   }
196 
197   std::vector<TimerId> result = timer_ids.value();
198   if (result.size() != clock_ids.size()) {
199     std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
200     return;
201   }
202 
203   // Map clock id values to timer ids.
204   auto timer_id_iter = result.begin();
205   for (clockid_t clock_id : clock_ids) {
206     DVLOG(1) << "Storing clock=" << clock_id << " timer id=" << *timer_id_iter;
207     if (!timer_ids_.emplace(clock_id, *timer_id_iter).second) {
208       // This should never happen as any collision should have been detected on
209       // the powerd side and it should have returned an error.
210       LOG(ERROR) << "Can't store clock=" << clock_id;
211       timer_ids_.clear();
212       std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
213       return;
214     }
215     timer_id_iter++;
216   }
217   std::move(callback).Run(mojom::ArcTimerResult::SUCCESS);
218 }
219 
GetTimerId(clockid_t clock_id) const220 base::Optional<ArcTimerBridge::TimerId> ArcTimerBridge::GetTimerId(
221     clockid_t clock_id) const {
222   auto it = timer_ids_.find(clock_id);
223   return (it == timer_ids_.end()) ? base::nullopt
224                                   : base::make_optional<TimerId>(it->second);
225 }
226 
227 }  // namespace arc
228