//
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "shill/dbus/chromeos_power_manager_proxy.h"
#include <base/bind.h>
#include <google/protobuf/message_lite.h>
#include "power_manager/proto_bindings/suspend.pb.h"
#include "shill/event_dispatcher.h"
#include "shill/logging.h"
using std::string;
using std::vector;
namespace shill {
namespace {
// Serializes |protobuf| to |out| and returns true on success.
bool SerializeProtocolBuffer(const google::protobuf::MessageLite& protobuf,
vector<uint8_t>* out) {
CHECK(out);
out->clear();
string serialized_protobuf;
if (!protobuf.SerializeToString(&serialized_protobuf))
return false;
out->assign(serialized_protobuf.begin(), serialized_protobuf.end());
return true;
}
// Deserializes |serialized_protobuf| to |protobuf_out| and returns true on
// success.
bool DeserializeProtocolBuffer(const vector<uint8_t>& serialized_protobuf,
google::protobuf::MessageLite* protobuf_out) {
CHECK(protobuf_out);
if (serialized_protobuf.empty())
return false;
return protobuf_out->ParseFromArray(&serialized_protobuf.front(),
serialized_protobuf.size());
}
} // namespace
ChromeosPowerManagerProxy::ChromeosPowerManagerProxy(
EventDispatcher* dispatcher,
const scoped_refptr<dbus::Bus>& bus,
PowerManagerProxyDelegate* delegate,
const base::Closure& service_appeared_callback,
const base::Closure& service_vanished_callback)
: proxy_(new org::chromium::PowerManagerProxy(bus)),
dispatcher_(dispatcher),
delegate_(delegate),
service_appeared_callback_(service_appeared_callback),
service_vanished_callback_(service_vanished_callback) {
// Register signal handlers.
proxy_->RegisterSuspendImminentSignalHandler(
base::Bind(&ChromeosPowerManagerProxy::SuspendImminent,
weak_factory_.GetWeakPtr()),
base::Bind(&ChromeosPowerManagerProxy::OnSignalConnected,
weak_factory_.GetWeakPtr()));
proxy_->RegisterSuspendDoneSignalHandler(
base::Bind(&ChromeosPowerManagerProxy::SuspendDone,
weak_factory_.GetWeakPtr()),
base::Bind(&ChromeosPowerManagerProxy::OnSignalConnected,
weak_factory_.GetWeakPtr()));
proxy_->RegisterDarkSuspendImminentSignalHandler(
base::Bind(&ChromeosPowerManagerProxy::DarkSuspendImminent,
weak_factory_.GetWeakPtr()),
base::Bind(&ChromeosPowerManagerProxy::OnSignalConnected,
weak_factory_.GetWeakPtr()));
// One time callback when service becomes available.
proxy_->GetObjectProxy()->WaitForServiceToBeAvailable(
base::Bind(&ChromeosPowerManagerProxy::OnServiceAvailable,
weak_factory_.GetWeakPtr()));
}
ChromeosPowerManagerProxy::~ChromeosPowerManagerProxy() {}
bool ChromeosPowerManagerProxy::RegisterSuspendDelay(
base::TimeDelta timeout,
const string& description,
int* delay_id_out) {
if (!service_available_) {
LOG(ERROR) << "PowerManager service not available";
return false;
}
return RegisterSuspendDelayInternal(false,
timeout,
description,
delay_id_out);
}
bool ChromeosPowerManagerProxy::UnregisterSuspendDelay(int delay_id) {
if (!service_available_) {
LOG(ERROR) << "PowerManager service not available";
return false;
}
return UnregisterSuspendDelayInternal(false, delay_id);
}
bool ChromeosPowerManagerProxy::ReportSuspendReadiness(int delay_id,
int suspend_id) {
if (!service_available_) {
LOG(ERROR) << "PowerManager service not available";
return false;
}
return ReportSuspendReadinessInternal(false, delay_id, suspend_id);
}
bool ChromeosPowerManagerProxy::RegisterDarkSuspendDelay(
base::TimeDelta timeout,
const string& description,
int* delay_id_out) {
if (!service_available_) {
LOG(ERROR) << "PowerManager service not available";
return false;
}
return RegisterSuspendDelayInternal(true,
timeout,
description,
delay_id_out);
}
bool ChromeosPowerManagerProxy::UnregisterDarkSuspendDelay(int delay_id) {
if (!service_available_) {
LOG(ERROR) << "PowerManager service not available";
return false;
}
return UnregisterSuspendDelayInternal(true, delay_id);
}
bool ChromeosPowerManagerProxy::ReportDarkSuspendReadiness(int delay_id,
int suspend_id ) {
if (!service_available_) {
LOG(ERROR) << "PowerManager service not available";
return false;
}
return ReportSuspendReadinessInternal(true, delay_id, suspend_id);
}
bool ChromeosPowerManagerProxy::RecordDarkResumeWakeReason(
const string& wake_reason) {
LOG(INFO) << __func__;
if (!service_available_) {
LOG(ERROR) << "PowerManager service not available";
return false;
}
power_manager::DarkResumeWakeReason proto;
proto.set_wake_reason(wake_reason);
vector<uint8_t> serialized_proto;
CHECK(SerializeProtocolBuffer(proto, &serialized_proto));
brillo::ErrorPtr error;
if (!proxy_->RecordDarkResumeWakeReason(serialized_proto, &error)) {
LOG(ERROR) << "Failed tp record dark resume wake reason: "
<< error->GetCode() << " " << error->GetMessage();
return false;
}
return true;
}
bool ChromeosPowerManagerProxy::RegisterSuspendDelayInternal(
bool is_dark,
base::TimeDelta timeout,
const string& description,
int* delay_id_out) {
const string is_dark_arg = (is_dark ? "dark=true" : "dark=false");
LOG(INFO) << __func__ << "(" << timeout.InMilliseconds()
<< ", " << is_dark_arg <<")";
power_manager::RegisterSuspendDelayRequest request_proto;
request_proto.set_timeout(timeout.ToInternalValue());
request_proto.set_description(description);
vector<uint8_t> serialized_request;
CHECK(SerializeProtocolBuffer(request_proto, &serialized_request));
vector<uint8_t> serialized_reply;
brillo::ErrorPtr error;
if (is_dark) {
proxy_->RegisterDarkSuspendDelay(serialized_request,
&serialized_reply,
&error);
} else {
proxy_->RegisterSuspendDelay(serialized_request, &serialized_reply, &error);
}
if (error) {
LOG(ERROR) << "Failed to register suspend delay: "
<< error->GetCode() << " " << error->GetMessage();
return false;
}
power_manager::RegisterSuspendDelayReply reply_proto;
if (!DeserializeProtocolBuffer(serialized_reply, &reply_proto)) {
LOG(ERROR) << "Failed to register "
<< (is_dark ? "dark " : "")
<< "suspend delay. Couldn't parse response.";
return false;
}
*delay_id_out = reply_proto.delay_id();
return true;
}
bool ChromeosPowerManagerProxy::UnregisterSuspendDelayInternal(bool is_dark,
int delay_id) {
const string is_dark_arg = (is_dark ? "dark=true" : "dark=false");
LOG(INFO) << __func__ << "(" << delay_id << ", " << is_dark_arg << ")";
power_manager::UnregisterSuspendDelayRequest request_proto;
request_proto.set_delay_id(delay_id);
vector<uint8_t> serialized_request;
CHECK(SerializeProtocolBuffer(request_proto, &serialized_request));
brillo::ErrorPtr error;
if (is_dark) {
proxy_->UnregisterDarkSuspendDelay(serialized_request, &error);
} else {
proxy_->UnregisterSuspendDelay(serialized_request, &error);
}
if (error) {
LOG(ERROR) << "Failed to unregister suspend delay: "
<< error->GetCode() << " " << error->GetMessage();
return false;
}
return true;
}
bool ChromeosPowerManagerProxy::ReportSuspendReadinessInternal(
bool is_dark, int delay_id, int suspend_id) {
const string is_dark_arg = (is_dark ? "dark=true" : "dark=false");
LOG(INFO) << __func__
<< "(" << delay_id
<< ", " << suspend_id
<< ", " << is_dark_arg << ")";
power_manager::SuspendReadinessInfo proto;
proto.set_delay_id(delay_id);
proto.set_suspend_id(suspend_id);
vector<uint8_t> serialized_proto;
CHECK(SerializeProtocolBuffer(proto, &serialized_proto));
brillo::ErrorPtr error;
if (is_dark) {
proxy_->HandleDarkSuspendReadiness(serialized_proto, &error);
} else {
proxy_->HandleSuspendReadiness(serialized_proto, &error);
}
if (error) {
LOG(ERROR) << "Failed to report suspend readiness: "
<< error->GetCode() << " " << error->GetMessage();
return false;
}
return true;
}
void ChromeosPowerManagerProxy::SuspendImminent(
const vector<uint8_t>& serialized_proto) {
LOG(INFO) << __func__;
power_manager::SuspendImminent proto;
if (!DeserializeProtocolBuffer(serialized_proto, &proto)) {
LOG(ERROR) << "Failed to parse SuspendImminent signal.";
return;
}
delegate_->OnSuspendImminent(proto.suspend_id());
}
void ChromeosPowerManagerProxy::SuspendDone(
const vector<uint8_t>& serialized_proto) {
LOG(INFO) << __func__;
power_manager::SuspendDone proto;
if (!DeserializeProtocolBuffer(serialized_proto, &proto)) {
LOG(ERROR) << "Failed to parse SuspendDone signal.";
return;
}
delegate_->OnSuspendDone(proto.suspend_id());
}
void ChromeosPowerManagerProxy::DarkSuspendImminent(
const vector<uint8_t>& serialized_proto) {
LOG(INFO) << __func__;
power_manager::SuspendImminent proto;
if (!DeserializeProtocolBuffer(serialized_proto, &proto)) {
LOG(ERROR) << "Failed to parse DarkSuspendImminent signal.";
return;
}
delegate_->OnDarkSuspendImminent(proto.suspend_id());
}
void ChromeosPowerManagerProxy::OnServiceAvailable(bool available) {
// The only time this function will ever be invoked with |available| set to
// false is when we failed to connect the signals, either bus is not setup
// yet or we failed to add match rules, and both of these errors are
// considered fatal.
CHECK(available);
// Service is available now, continuously monitor the service owner changes.
proxy_->GetObjectProxy()->SetNameOwnerChangedCallback(
base::Bind(&ChromeosPowerManagerProxy::OnServiceOwnerChanged,
weak_factory_.GetWeakPtr()));
// The callback might invoke calls to the ObjectProxy, so defer the callback
// to event loop.
if (!service_appeared_callback_.is_null()) {
dispatcher_->PostTask(service_appeared_callback_);
}
service_available_ = true;
}
void ChromeosPowerManagerProxy::OnServiceOwnerChanged(
const string& old_owner, const string& new_owner) {
LOG(INFO) << __func__ << "old: " << old_owner << " new: " << new_owner;
if (new_owner.empty()) {
// The callback might invoke calls to the ObjectProxy, so defer the
// callback to event loop.
if (!service_vanished_callback_.is_null()) {
dispatcher_->PostTask(service_vanished_callback_);
}
service_available_ = false;
} else {
// The callback might invoke calls to the ObjectProxy, so defer the
// callback to event loop.
if (!service_appeared_callback_.is_null()) {
dispatcher_->PostTask(service_appeared_callback_);
}
service_available_ = true;
}
}
void ChromeosPowerManagerProxy::OnSignalConnected(
const string& interface_name, const string& signal_name, bool success) {
LOG(INFO) << __func__ << " interface: " << interface_name
<< " signal: " << signal_name << "success: " << success;
if (!success) {
LOG(ERROR) << "Failed to connect signal " << signal_name
<< " to interface " << interface_name;
}
}
} // namespace shill