blob: 350aa51a073b38955f0000cef5752cc64257af92 [file] [log] [blame]
// Copyright 2016 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 "libweaved/service.h"
#include <algorithm>
#include <base/bind.h>
#include <base/memory/weak_ptr.h>
#include <base/strings/stringprintf.h>
#include <binderwrapper/binder_wrapper.h>
#include <brillo/message_loops/message_loop.h>
#include "android/weave/BnWeaveClient.h"
#include "android/weave/BnWeaveServiceManagerNotificationListener.h"
#include "android/weave/IWeaveCommand.h"
#include "android/weave/IWeaveService.h"
#include "android/weave/IWeaveServiceManager.h"
#include "common/binder_constants.h"
#include "common/binder_utils.h"
using weaved::binder_utils::StatusToError;
using weaved::binder_utils::ToString;
using weaved::binder_utils::ToString16;
// The semantic of weaved connection is a bit complicated and that's why we have
// the numerous classes defined here.
// When the client wants to connect to weaved they would call Service::Connect
// and provide a callback to be invoked when the connection is fully established
// and ready to be used.
//
// Service::Connect() creates an instance of ServiceImpl class and sets the only
// strong pointer into ServiceSubscription class which is returned to the client
// as std::unqiue_ptr<Service::Subscription>. This allows us to hide the actual
// service object from the client until the connection is fully ready to be
// used, and at the same time give the client an exclusive ownership of the
// connection. They are free to destroy the Subscription and abort the
// connection at any point.
//
// At the same time an asynchronous process to establish a connection to weaved
// over binder is initiated. ServiceImpl periodically tries to get hold of
// IWeaveServiceManager binder object from binder service manager. Once this
// succeeds, we know that weaved is running. We create a callback binder object,
// WeaveClient, which implements IWeaveClient binder interface and pass it to
// weaved in IWeaveServiceManager::connect() method. The weaved daemon keeps the
// list of all the clients registered with it for two reasons:
// 1. It watches each client for death notifications and cleans up the
// resources added by the client (e.g. weave components) when the client
// dies.
// 2. It notifies the client of weaved being ready to talk to (by calling
// onServiceConnected callback) and when new weave commands are available
// for the client (via onCommand callback).
// When weaved is fully initialized (which can take some time after the daemon
// physically starts up), it invokes IWeaveClient::onServiceConnection on each
// client and passes a unique copy of IWeaveService to each of the client.
// The clients will use its own IWeaveService interface to further interact with
// weaved. This allows weaved to distinguish binder calls from each client and
// maintain the track record of which client adds each resource.
// Once IWeaveClient::onServiceConnection is called, we have a fully-established
// service connection to weaved and we invoke the client callback provided in
// the original call to Service::Connect() and pass the weak pointer to the
// service as an argument.
// In case a connection to weaved is lost, the ServiceImpl class will be deleted
// and any weak pointers to it the client may have will be invalidated.
// A new instance of ServiceImpl is created and the strong reference in
// ServiceSubscription is replace to the new instance. A new re-connection cycle
// is started as if the client just invoked Service::Connect() again on the new
// instance of ServiceImpl.
namespace weaved {
namespace {
// An implementation for service subscription. This object keeps a reference to
// the actual instance of weaved service object. This is generally the only hard
// reference to the shared pointer to the service object. The client receives
// a weak pointer only.
class ServiceSubscription : public Service::Subscription {
public:
ServiceSubscription() = default;
~ServiceSubscription() override = default;
void SetService(const std::shared_ptr<Service>& service) {
service_ = service;
}
private:
std::shared_ptr<Service> service_;
DISALLOW_COPY_AND_ASSIGN(ServiceSubscription);
};
} // anonymous namespace
class ServiceImpl;
// Each system process wishing to expose functionality via weave establishes a
// connection to weaved via Binder. The communication channel is two-way.
// The client obtains a reference to weaved's android::weave::IWeaveService from
// the system service manager, and registers an instance of
// android::weave::IWeaveClient with weaved via IWeaveService.
// WeaveClient is an implementation of android::weave::IWeaveClient binder
// interface. Apart from providing callback methods (such as onCommand), it is
// used by weaved to track the life-time of this particular client. If the
// client exits, weaved automatically cleans up resources added by this client.
class WeaveClient : public android::weave::BnWeaveClient {
public:
explicit WeaveClient(const std::weak_ptr<ServiceImpl>& service);
private:
// Implementation for IWeaveClient interface.
// A notification that the service binder is successfully instantiated and
// weaved daemon is ready to process incoming request for component creation,
// device state updates and so on.
android::binder::Status onServiceConnected(
const android::sp<android::weave::IWeaveService>& service) override;
// A callback invoked when a new command for which a handler was registered
// is added to the command queue.
android::binder::Status onCommand(
const android::String16& componentName,
const android::String16& commandName,
const android::sp<android::weave::IWeaveCommand>& command) override;
std::weak_ptr<ServiceImpl> service_;
base::WeakPtrFactory<WeaveClient> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(WeaveClient);
};
class NotificationListener
: public android::weave::BnWeaveServiceManagerNotificationListener {
public:
explicit NotificationListener(const std::weak_ptr<ServiceImpl>& service);
private:
// Implementation for IWeaveServiceManagerNotificationListener interface.
android::binder::Status notifyServiceManagerChange(
const std::vector<int>& notificationIds) override;
std::weak_ptr<ServiceImpl> service_;
base::WeakPtrFactory<NotificationListener> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(NotificationListener);
};
// ServiceImpl is a concrete implementation of weaved::Service interface.
// This object is a wrapper around android::weave::IWeaveService binder
// interface to weaved daemon.
// This class is created as soon as Service::Connect() is called and it
// initiates connection attempts to IWeaveService binder. Only when the
// connection is successful and we receive callback notification from weaved
// that the service is ready, we invoke the client-provided callback and pass
// a weak pointer to Service fro the client to talk to weaved.
class ServiceImpl : public std::enable_shared_from_this<ServiceImpl>,
public Service {
public:
// A constructor. Client code never creates this instance directly, but rather
// uses Service::Connect which is responsible for creating a instance of this
// class.
ServiceImpl(android::BinderWrapper* binder_wrapper,
brillo::MessageLoop* message_loop,
ServiceSubscription* service_subscription,
const ConnectionCallback& connection_callback);
~ServiceImpl() override;
// Service interface methods.
bool AddComponent(const std::string& component,
const std::vector<std::string>& traits,
brillo::ErrorPtr* error) override;
void AddCommandHandler(const std::string& component,
const std::string& trait_name,
const std::string& command_name,
const CommandHandlerCallback& callback) override;
bool SetStateProperties(const std::string& component,
const base::DictionaryValue& dict,
brillo::ErrorPtr* error) override;
bool SetStateProperty(const std::string& component,
const std::string& trait_name,
const std::string& property_name,
const base::Value& value,
brillo::ErrorPtr* error) override;
void SetPairingInfoListener(const PairingInfoCallback& callback) override;
// Helper method called from Service::Connect() to initiate binder connection
// to weaved. This message just posts a task to the message loop to invoke
// TryConnecting() method.
void BeginConnect();
// A callback method for WeaveClient::onServiceConnected().
void OnServiceConnected(
const android::sp<android::weave::IWeaveService>& service);
// A callback method for WeaveClient::onCommand().
void OnCommand(const std::string& component_name,
const std::string& command_name,
const android::sp<android::weave::IWeaveCommand>& command);
// A callback method for NotificationListener::notifyServiceManagerChange().
void OnNotification(const std::vector<int>& notification_ids);
private:
// Connects to weaved daemon over binder if the service manager is available
// and weaved daemon itself is ready to accept connections. If not, schedules
// another retry after a delay (1 second).
void TryConnecting();
// A callback for weaved connection termination. When binder service manager
// notifies client of weaved binder object destruction (e.g. weaved quits),
// this callback is invoked and initiates re-connection process.
// Since the callback can happen synchronously from any call into the binder
// driver, this method just posts a message that just asynchronously invokes
// "ReconnectOnServiceDisconnection".
void OnWeaveServiceDisconnected();
// Asynchronous notification callback of binder service death. Tears down
// this instance of ServiceImpl class, creates a new one and re-initiates
// the binder connection to the service.
void ReconnectOnServiceDisconnection();
android::BinderWrapper* binder_wrapper_;
brillo::MessageLoop* message_loop_;
ServiceSubscription* service_subscription_;
ConnectionCallback connection_callback_;
android::sp<android::weave::IWeaveServiceManager> weave_service_manager_;
android::sp<android::weave::IWeaveService> weave_service_;
PairingInfoCallback pairing_info_callback_;
PairingInfo pairing_info_;
struct CommandHandlerEntry {
std::string component;
std::string command_name;
CommandHandlerCallback callback;
};
std::vector<CommandHandlerEntry> command_handlers_;
base::WeakPtrFactory<ServiceImpl> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(ServiceImpl);
};
WeaveClient::WeaveClient(const std::weak_ptr<ServiceImpl>& service)
: service_{service} {}
android::binder::Status WeaveClient::onServiceConnected(
const android::sp<android::weave::IWeaveService>& service) {
LOG(INFO) << "Weave service connection established successfully";
auto service_proxy = service_.lock();
if (service_proxy)
service_proxy->OnServiceConnected(service);
return android::binder::Status::ok();
}
android::binder::Status WeaveClient::onCommand(
const android::String16& componentName,
const android::String16& commandName,
const android::sp<android::weave::IWeaveCommand>& command) {
auto service_proxy = service_.lock();
if (service_proxy) {
service_proxy->OnCommand(ToString(componentName), ToString(commandName),
command);
} else {
command->abort(android::String16{"service_unavailable"},
android::String16{"Command handler is unavailable"});
}
return android::binder::Status::ok();
}
NotificationListener::NotificationListener(
const std::weak_ptr<ServiceImpl>& service)
: service_{service} {}
android::binder::Status NotificationListener::notifyServiceManagerChange(
const std::vector<int>& notificationIds) {
auto service_proxy = service_.lock();
if (service_proxy)
service_proxy->OnNotification(notificationIds);
return android::binder::Status::ok();
}
ServiceImpl::ServiceImpl(android::BinderWrapper* binder_wrapper,
brillo::MessageLoop* message_loop,
ServiceSubscription* service_subscription,
const ConnectionCallback& connection_callback)
: binder_wrapper_{binder_wrapper},
message_loop_{message_loop},
service_subscription_{service_subscription},
connection_callback_{connection_callback} {
}
ServiceImpl::~ServiceImpl() {
if (weave_service_.get()) {
android::sp<android::IBinder> binder =
android::IInterface::asBinder(weave_service_);
binder_wrapper_->UnregisterForDeathNotifications(binder);
}
}
bool ServiceImpl::AddComponent(const std::string& component,
const std::vector<std::string>& traits,
brillo::ErrorPtr* error) {
CHECK(weave_service_.get());
std::vector<android::String16> trait_list;
auto to_string16 = [](const std::string& name) {
return android::String16{name.c_str()};
};
std::transform(traits.begin(), traits.end(), std::back_inserter(trait_list),
to_string16);
return StatusToError(weave_service_->addComponent(to_string16(component),
trait_list),
error);
}
void ServiceImpl::AddCommandHandler(const std::string& component,
const std::string& trait_name,
const std::string& command_name,
const CommandHandlerCallback& callback) {
CHECK(!component.empty() && !command_name.empty());
CHECK(weave_service_.get());
std::string full_command_name =
base::StringPrintf("%s.%s", trait_name.c_str(), command_name.c_str());
CommandHandlerEntry entry;
entry.component = component;
entry.command_name = full_command_name;
entry.callback = callback;
command_handlers_.push_back(std::move(entry));
auto status = weave_service_->registerCommandHandler(
android::String16{component.c_str()},
android::String16{full_command_name.c_str()});
CHECK(status.isOk());
}
bool ServiceImpl::SetStateProperties(const std::string& component,
const base::DictionaryValue& dict,
brillo::ErrorPtr* error) {
CHECK(!component.empty());
CHECK(weave_service_.get());
return StatusToError(weave_service_->updateState(ToString16(component),
ToString16(dict)),
error);
}
bool ServiceImpl::SetStateProperty(const std::string& component,
const std::string& trait_name,
const std::string& property_name,
const base::Value& value,
brillo::ErrorPtr* error) {
std::string name =
base::StringPrintf("%s.%s", trait_name.c_str(), property_name.c_str());
base::DictionaryValue dict;
dict.Set(name, value.DeepCopy());
return SetStateProperties(component, dict, error);
}
void ServiceImpl::SetPairingInfoListener(const PairingInfoCallback& callback) {
pairing_info_callback_ = callback;
if (!pairing_info_callback_.is_null() &&
!pairing_info_.session_id.empty() &&
!pairing_info_.pairing_mode.empty() &&
!pairing_info_.pairing_code.empty()) {
callback.Run(&pairing_info_);
}
}
void ServiceImpl::BeginConnect() {
message_loop_->PostTask(FROM_HERE,
base::Bind(&ServiceImpl::TryConnecting,
weak_ptr_factory_.GetWeakPtr()));
}
void ServiceImpl::OnServiceConnected(
const android::sp<android::weave::IWeaveService>& service) {
weave_service_ = service;
connection_callback_.Run(shared_from_this());
}
void ServiceImpl::OnCommand(
const std::string& component_name,
const std::string& command_name,
const android::sp<android::weave::IWeaveCommand>& command) {
VLOG(2) << "Weave command received for component '" << component_name << "': "
<< command_name;
for (const auto& entry : command_handlers_) {
if (entry.component == component_name &&
entry.command_name == command_name) {
std::unique_ptr<Command> command_instance{new Command{command}};
return entry.callback.Run(std::move(command_instance));
}
}
LOG(WARNING) << "Unexpected command notification. Command = " << command_name
<< ", component = " << component_name;
}
void ServiceImpl::TryConnecting() {
LOG(INFO) << "Connecting to weave service over binder";
android::sp<android::IBinder> binder =
binder_wrapper_->GetService(weaved::binder::kWeaveServiceName);
if (!binder.get()) {
LOG(WARNING) << "Weave service is not available yet. Will try again later";
message_loop_->PostDelayedTask(
FROM_HERE,
base::Bind(&ServiceImpl::TryConnecting, weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromSeconds(1));
return;
}
bool register_success = binder_wrapper_->RegisterForDeathNotifications(
binder, base::Bind(&ServiceImpl::OnWeaveServiceDisconnected,
weak_ptr_factory_.GetWeakPtr()));
if (!register_success) {
// Something really bad happened here, restart the connection.
OnWeaveServiceDisconnected();
return;
}
weave_service_manager_ =
android::interface_cast<android::weave::IWeaveServiceManager>(binder);
android::sp<WeaveClient> weave_client = new WeaveClient{shared_from_this()};
weave_service_manager_->connect(weave_client);
android::sp<NotificationListener> notification_listener =
new NotificationListener{shared_from_this()};
weave_service_manager_->registerNotificationListener(notification_listener);
}
void ServiceImpl::OnWeaveServiceDisconnected() {
message_loop_->PostTask(
FROM_HERE,
base::Bind(&ServiceImpl::ReconnectOnServiceDisconnection,
weak_ptr_factory_.GetWeakPtr()));
}
void ServiceImpl::ReconnectOnServiceDisconnection() {
weave_service_.clear();
// Need to create a new instance of service to invalidate existing weak
// pointers.
auto service = std::make_shared<ServiceImpl>(
binder_wrapper_, message_loop_, service_subscription_,
connection_callback_);
service->BeginConnect();
// The subscription object owns this instance.
// Calling SetService() will destroy |this|.
service_subscription_->SetService(service);
// Do not call any methods or use resources of ServiceImpl after this point
// because the object is destroyed now.
}
void ServiceImpl::OnNotification(const std::vector<int>& notification_ids) {
bool pairing_info_changed = false;
using NotificationListener =
android::weave::IWeaveServiceManagerNotificationListener;
android::String16 string_value;
for (int id : notification_ids) {
switch (id) {
case NotificationListener::PAIRING_SESSION_ID:
if (weave_service_manager_->getPairingSessionId(&string_value).isOk()) {
pairing_info_changed = true;
pairing_info_.session_id = ToString(string_value);
}
break;
case NotificationListener::PAIRING_MODE:
if (weave_service_manager_->getPairingMode(&string_value).isOk()) {
pairing_info_changed = true;
pairing_info_.pairing_mode = ToString(string_value);
}
break;
case NotificationListener::PAIRING_CODE:
if (weave_service_manager_->getPairingCode(&string_value).isOk()) {
pairing_info_changed = true;
pairing_info_.pairing_code = ToString(string_value);
}
break;
}
}
if (!pairing_info_changed || pairing_info_callback_.is_null())
return;
if (pairing_info_.session_id.empty() || pairing_info_.pairing_mode.empty() ||
pairing_info_.pairing_code.empty()) {
pairing_info_callback_.Run(nullptr);
} else {
pairing_info_callback_.Run(&pairing_info_);
}
}
std::unique_ptr<Service::Subscription> Service::Connect(
brillo::MessageLoop* message_loop,
const ConnectionCallback& callback) {
std::unique_ptr<ServiceSubscription> subscription{new ServiceSubscription};
auto service = std::make_shared<ServiceImpl>(
android::BinderWrapper::GetOrCreateInstance(), message_loop,
subscription.get(), callback);
subscription->SetService(service);
service->BeginConnect();
return std::move(subscription);
}
} // namespace weaved