// 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