1//
2// Copyright (C) 2015 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "shill/dbus/chromeos_power_manager_proxy.h"
18
19#include <base/bind.h>
20#include <google/protobuf/message_lite.h>
21
22#include "power_manager/proto_bindings/suspend.pb.h"
23#include "shill/event_dispatcher.h"
24#include "shill/logging.h"
25
26using std::string;
27using std::vector;
28
29namespace shill {
30
31namespace {
32
33// Serializes |protobuf| to |out| and returns true on success.
34bool SerializeProtocolBuffer(const google::protobuf::MessageLite& protobuf,
35                             vector<uint8_t>* out) {
36  CHECK(out);
37  out->clear();
38  string serialized_protobuf;
39  if (!protobuf.SerializeToString(&serialized_protobuf))
40    return false;
41  out->assign(serialized_protobuf.begin(), serialized_protobuf.end());
42  return true;
43}
44
45// Deserializes |serialized_protobuf| to |protobuf_out| and returns true on
46// success.
47bool DeserializeProtocolBuffer(const vector<uint8_t>& serialized_protobuf,
48                               google::protobuf::MessageLite* protobuf_out) {
49  CHECK(protobuf_out);
50  if (serialized_protobuf.empty())
51    return false;
52  return protobuf_out->ParseFromArray(&serialized_protobuf.front(),
53                                      serialized_protobuf.size());
54}
55
56}  // namespace
57
58ChromeosPowerManagerProxy::ChromeosPowerManagerProxy(
59      EventDispatcher* dispatcher,
60      const scoped_refptr<dbus::Bus>& bus,
61      PowerManagerProxyDelegate* delegate,
62      const base::Closure& service_appeared_callback,
63      const base::Closure& service_vanished_callback)
64    : proxy_(new org::chromium::PowerManagerProxy(bus)),
65      dispatcher_(dispatcher),
66      delegate_(delegate),
67      service_appeared_callback_(service_appeared_callback),
68      service_vanished_callback_(service_vanished_callback) {
69  // Register signal handlers.
70  proxy_->RegisterSuspendImminentSignalHandler(
71      base::Bind(&ChromeosPowerManagerProxy::SuspendImminent,
72                 weak_factory_.GetWeakPtr()),
73      base::Bind(&ChromeosPowerManagerProxy::OnSignalConnected,
74                 weak_factory_.GetWeakPtr()));
75  proxy_->RegisterSuspendDoneSignalHandler(
76      base::Bind(&ChromeosPowerManagerProxy::SuspendDone,
77                 weak_factory_.GetWeakPtr()),
78      base::Bind(&ChromeosPowerManagerProxy::OnSignalConnected,
79                 weak_factory_.GetWeakPtr()));
80  proxy_->RegisterDarkSuspendImminentSignalHandler(
81      base::Bind(&ChromeosPowerManagerProxy::DarkSuspendImminent,
82                 weak_factory_.GetWeakPtr()),
83      base::Bind(&ChromeosPowerManagerProxy::OnSignalConnected,
84                 weak_factory_.GetWeakPtr()));
85
86  // One time callback when service becomes available.
87  proxy_->GetObjectProxy()->WaitForServiceToBeAvailable(
88      base::Bind(&ChromeosPowerManagerProxy::OnServiceAvailable,
89                 weak_factory_.GetWeakPtr()));
90}
91
92ChromeosPowerManagerProxy::~ChromeosPowerManagerProxy() {}
93
94bool ChromeosPowerManagerProxy::RegisterSuspendDelay(
95    base::TimeDelta timeout,
96    const string& description,
97    int* delay_id_out) {
98  if (!service_available_) {
99    LOG(ERROR) << "PowerManager service not available";
100    return false;
101  }
102  return RegisterSuspendDelayInternal(false,
103                                      timeout,
104                                      description,
105                                      delay_id_out);
106}
107
108bool ChromeosPowerManagerProxy::UnregisterSuspendDelay(int delay_id) {
109  if (!service_available_) {
110    LOG(ERROR) << "PowerManager service not available";
111    return false;
112  }
113  return UnregisterSuspendDelayInternal(false, delay_id);
114}
115
116bool ChromeosPowerManagerProxy::ReportSuspendReadiness(int delay_id,
117                                                       int suspend_id) {
118  if (!service_available_) {
119    LOG(ERROR) << "PowerManager service not available";
120    return false;
121  }
122  return ReportSuspendReadinessInternal(false, delay_id, suspend_id);
123}
124
125bool ChromeosPowerManagerProxy::RegisterDarkSuspendDelay(
126    base::TimeDelta timeout,
127    const string& description,
128    int* delay_id_out) {
129  if (!service_available_) {
130    LOG(ERROR) << "PowerManager service not available";
131    return false;
132  }
133  return RegisterSuspendDelayInternal(true,
134                                      timeout,
135                                      description,
136                                      delay_id_out);
137}
138
139bool ChromeosPowerManagerProxy::UnregisterDarkSuspendDelay(int delay_id) {
140  if (!service_available_) {
141    LOG(ERROR) << "PowerManager service not available";
142    return false;
143  }
144  return UnregisterSuspendDelayInternal(true, delay_id);
145}
146
147bool ChromeosPowerManagerProxy::ReportDarkSuspendReadiness(int delay_id,
148                                                           int suspend_id ) {
149  if (!service_available_) {
150    LOG(ERROR) << "PowerManager service not available";
151    return false;
152  }
153  return ReportSuspendReadinessInternal(true, delay_id, suspend_id);
154}
155
156bool ChromeosPowerManagerProxy::RecordDarkResumeWakeReason(
157    const string& wake_reason) {
158  LOG(INFO) << __func__;
159
160  if (!service_available_) {
161    LOG(ERROR) << "PowerManager service not available";
162    return false;
163  }
164
165  power_manager::DarkResumeWakeReason proto;
166  proto.set_wake_reason(wake_reason);
167  vector<uint8_t> serialized_proto;
168  CHECK(SerializeProtocolBuffer(proto, &serialized_proto));
169
170  brillo::ErrorPtr error;
171  if (!proxy_->RecordDarkResumeWakeReason(serialized_proto, &error)) {
172    LOG(ERROR) << "Failed tp record dark resume wake reason: "
173               << error->GetCode() << " " << error->GetMessage();
174    return false;
175  }
176  return true;
177}
178
179bool ChromeosPowerManagerProxy::RegisterSuspendDelayInternal(
180    bool is_dark,
181    base::TimeDelta timeout,
182    const string& description,
183    int* delay_id_out) {
184  const string is_dark_arg = (is_dark ? "dark=true" : "dark=false");
185  LOG(INFO) << __func__ << "(" << timeout.InMilliseconds()
186            << ", " << is_dark_arg <<")";
187
188  power_manager::RegisterSuspendDelayRequest request_proto;
189  request_proto.set_timeout(timeout.ToInternalValue());
190  request_proto.set_description(description);
191  vector<uint8_t> serialized_request;
192  CHECK(SerializeProtocolBuffer(request_proto, &serialized_request));
193
194  vector<uint8_t> serialized_reply;
195  brillo::ErrorPtr error;
196  if (is_dark) {
197    proxy_->RegisterDarkSuspendDelay(serialized_request,
198                                     &serialized_reply,
199                                     &error);
200  } else {
201    proxy_->RegisterSuspendDelay(serialized_request, &serialized_reply, &error);
202  }
203  if (error) {
204    LOG(ERROR) << "Failed to register suspend delay: "
205               << error->GetCode() << " " << error->GetMessage();
206    return false;
207  }
208
209  power_manager::RegisterSuspendDelayReply reply_proto;
210  if (!DeserializeProtocolBuffer(serialized_reply, &reply_proto)) {
211    LOG(ERROR) << "Failed to register "
212               << (is_dark ? "dark " : "")
213               << "suspend delay.  Couldn't parse response.";
214    return false;
215  }
216  *delay_id_out = reply_proto.delay_id();
217  return true;
218}
219
220bool ChromeosPowerManagerProxy::UnregisterSuspendDelayInternal(bool is_dark,
221                                                               int delay_id) {
222  const string is_dark_arg = (is_dark ? "dark=true" : "dark=false");
223  LOG(INFO) << __func__ << "(" << delay_id << ", " << is_dark_arg << ")";
224
225  power_manager::UnregisterSuspendDelayRequest request_proto;
226  request_proto.set_delay_id(delay_id);
227  vector<uint8_t> serialized_request;
228  CHECK(SerializeProtocolBuffer(request_proto, &serialized_request));
229
230  brillo::ErrorPtr error;
231  if (is_dark) {
232    proxy_->UnregisterDarkSuspendDelay(serialized_request, &error);
233  } else {
234    proxy_->UnregisterSuspendDelay(serialized_request, &error);
235  }
236  if (error) {
237    LOG(ERROR) << "Failed to unregister suspend delay: "
238               << error->GetCode() << " " << error->GetMessage();
239    return false;
240  }
241  return true;
242}
243
244bool ChromeosPowerManagerProxy::ReportSuspendReadinessInternal(
245    bool is_dark, int delay_id, int suspend_id) {
246  const string is_dark_arg = (is_dark ? "dark=true" : "dark=false");
247  LOG(INFO) << __func__
248            << "(" << delay_id
249            << ", " << suspend_id
250            << ", " << is_dark_arg << ")";
251
252  power_manager::SuspendReadinessInfo proto;
253  proto.set_delay_id(delay_id);
254  proto.set_suspend_id(suspend_id);
255  vector<uint8_t> serialized_proto;
256  CHECK(SerializeProtocolBuffer(proto, &serialized_proto));
257
258  brillo::ErrorPtr error;
259  if (is_dark) {
260    proxy_->HandleDarkSuspendReadiness(serialized_proto, &error);
261  } else {
262    proxy_->HandleSuspendReadiness(serialized_proto, &error);
263  }
264  if (error) {
265    LOG(ERROR) << "Failed to report suspend readiness: "
266               << error->GetCode() << " " << error->GetMessage();
267    return false;
268  }
269  return true;
270}
271
272void ChromeosPowerManagerProxy::SuspendImminent(
273    const vector<uint8_t>& serialized_proto) {
274  LOG(INFO) << __func__;
275  power_manager::SuspendImminent proto;
276  if (!DeserializeProtocolBuffer(serialized_proto, &proto)) {
277    LOG(ERROR) << "Failed to parse SuspendImminent signal.";
278    return;
279  }
280  delegate_->OnSuspendImminent(proto.suspend_id());
281}
282
283void ChromeosPowerManagerProxy::SuspendDone(
284    const vector<uint8_t>& serialized_proto) {
285  LOG(INFO) << __func__;
286  power_manager::SuspendDone proto;
287  if (!DeserializeProtocolBuffer(serialized_proto, &proto)) {
288    LOG(ERROR) << "Failed to parse SuspendDone signal.";
289    return;
290  }
291  delegate_->OnSuspendDone(proto.suspend_id());
292}
293
294void ChromeosPowerManagerProxy::DarkSuspendImminent(
295    const vector<uint8_t>& serialized_proto) {
296  LOG(INFO) << __func__;
297  power_manager::SuspendImminent proto;
298  if (!DeserializeProtocolBuffer(serialized_proto, &proto)) {
299    LOG(ERROR) << "Failed to parse DarkSuspendImminent signal.";
300    return;
301  }
302  delegate_->OnDarkSuspendImminent(proto.suspend_id());
303}
304
305void ChromeosPowerManagerProxy::OnServiceAvailable(bool available) {
306  // The only time this function will ever be invoked with |available| set to
307  // false is when we failed to connect the signals, either bus is not setup
308  // yet or we failed to add match rules, and both of these errors are
309  // considered fatal.
310  CHECK(available);
311
312  // Service is available now, continuously monitor the service owner changes.
313  proxy_->GetObjectProxy()->SetNameOwnerChangedCallback(
314      base::Bind(&ChromeosPowerManagerProxy::OnServiceOwnerChanged,
315                 weak_factory_.GetWeakPtr()));
316
317  // The callback might invoke calls to the ObjectProxy, so defer the callback
318  // to event loop.
319  if (!service_appeared_callback_.is_null()) {
320    dispatcher_->PostTask(service_appeared_callback_);
321  }
322
323  service_available_ = true;
324}
325
326void ChromeosPowerManagerProxy::OnServiceOwnerChanged(
327    const string& old_owner, const string& new_owner) {
328  LOG(INFO) << __func__ << "old: " << old_owner << " new: " << new_owner;
329
330  if (new_owner.empty()) {
331    // The callback might invoke calls to the ObjectProxy, so defer the
332    // callback to event loop.
333    if (!service_vanished_callback_.is_null()) {
334        dispatcher_->PostTask(service_vanished_callback_);
335    }
336    service_available_ = false;
337  } else {
338    // The callback might invoke calls to the ObjectProxy, so defer the
339    // callback to event loop.
340    if (!service_appeared_callback_.is_null()) {
341      dispatcher_->PostTask(service_appeared_callback_);
342    }
343    service_available_ = true;
344  }
345}
346
347void ChromeosPowerManagerProxy::OnSignalConnected(
348    const string& interface_name, const string& signal_name, bool success) {
349  LOG(INFO) << __func__ << " interface: " << interface_name
350            << " signal: " << signal_name << "success: " << success;
351  if (!success) {
352    LOG(ERROR) << "Failed to connect signal " << signal_name
353        << " to interface " << interface_name;
354  }
355}
356
357}  // namespace shill
358