普通文本  |  511行  |  20.83 KB

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