普通文本  |  492行  |  18.09 KB

// Copyright 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 "buffet/manager.h"

#include <map>
#include <set>
#include <string>

#include <base/bind.h>
#include <base/bind_helpers.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_util.h>
#include <base/json/json_reader.h>
#include <base/json/json_writer.h>
#include <base/message_loop/message_loop.h>
#include <base/time/time.h>
#include <binderwrapper/binder_wrapper.h>
#include <cutils/properties.h>
#include <brillo/bind_lambda.h>
#include <brillo/errors/error.h>
#include <brillo/http/http_transport.h>
#include <brillo/http/http_utils.h>
#include <brillo/key_value_store.h>
#include <brillo/message_loops/message_loop.h>
#include <brillo/mime_utils.h>
#include <dbus/bus.h>
#include <dbus/object_path.h>
#include <dbus/values_util.h>
#include <weave/enum_to_string.h>

#include "brillo/weaved_system_properties.h"
#include "buffet/bluetooth_client.h"
#include "buffet/buffet_config.h"
#include "buffet/http_transport_client.h"
#include "buffet/mdns_client.h"
#include "buffet/shill_client.h"
#include "buffet/weave_error_conversion.h"
#include "buffet/webserv_client.h"
#include "common/binder_utils.h"

using brillo::dbus_utils::AsyncEventSequencer;
using NotificationListener =
    android::weave::IWeaveServiceManagerNotificationListener;

namespace buffet {

namespace {

const char kErrorDomain[] = "buffet";
const char kFileReadError[] = "file_read_error";
const char kBaseComponent[] = "base";
const char kRebootCommand[] = "base.reboot";

bool LoadFile(const base::FilePath& file_path,
              std::string* data,
              brillo::ErrorPtr* error) {
  if (!base::ReadFileToString(file_path, data)) {
    brillo::errors::system::AddSystemError(error, FROM_HERE, errno);
    brillo::Error::AddToPrintf(error, FROM_HERE, kErrorDomain, kFileReadError,
                               "Failed to read file '%s'",
                               file_path.value().c_str());
    return false;
  }
  return true;
}

void LoadTraitDefinitions(const BuffetConfig::Options& options,
                          weave::Device* device) {
  // Load component-specific device trait definitions.
  base::FilePath dir{options.definitions.Append("traits")};
  LOG(INFO) << "Looking for trait definitions in " << dir.value();
  base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES,
                                  FILE_PATH_LITERAL("*.json"));
  std::vector<std::string> result;
  for (base::FilePath path = enumerator.Next(); !path.empty();
       path = enumerator.Next()) {
    LOG(INFO) << "Loading trait definition from " << path.value();
    std::string json;
    CHECK(LoadFile(path, &json, nullptr));
    device->AddTraitDefinitionsFromJson(json);
  }
}

void LoadCommandDefinitions(const BuffetConfig::Options& options,
                            weave::Device* device) {
  auto load_packages = [device](const base::FilePath& root,
                                const base::FilePath::StringType& pattern) {
    base::FilePath dir{root.Append("commands")};
    LOG(INFO) << "Looking for command schemas in " << dir.value();
    base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES,
                                    pattern);
    for (base::FilePath path = enumerator.Next(); !path.empty();
         path = enumerator.Next()) {
      LOG(INFO) << "Loading command schema from " << path.value();
      std::string json;
      CHECK(LoadFile(path, &json, nullptr));
      device->AddCommandDefinitionsFromJson(json);
    }
  };
  load_packages(options.definitions, FILE_PATH_LITERAL("*.json"));
  if (!options.test_definitions.empty())
    load_packages(options.test_definitions, FILE_PATH_LITERAL("*test.json"));
}

void LoadStateDefinitions(const BuffetConfig::Options& options,
                          weave::Device* device) {
  // Load component-specific device state definitions.
  base::FilePath dir{options.definitions.Append("states")};
  LOG(INFO) << "Looking for state definitions in " << dir.value();
  base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES,
                                  FILE_PATH_LITERAL("*.schema.json"));
  std::vector<std::string> result;
  for (base::FilePath path = enumerator.Next(); !path.empty();
       path = enumerator.Next()) {
    LOG(INFO) << "Loading state definition from " << path.value();
    std::string json;
    CHECK(LoadFile(path, &json, nullptr));
    device->AddStateDefinitionsFromJson(json);
  }
}

void LoadStateDefaults(const BuffetConfig::Options& options,
                       weave::Device* device) {
  // Load component-specific device state defaults.
  base::FilePath dir{options.definitions.Append("states")};
  LOG(INFO) << "Looking for state defaults in " << dir.value();
  base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES,
                                  FILE_PATH_LITERAL("*.defaults.json"));
  std::vector<std::string> result;
  for (base::FilePath path = enumerator.Next(); !path.empty();
       path = enumerator.Next()) {
    LOG(INFO) << "Loading state defaults from " << path.value();
    std::string json;
    CHECK(LoadFile(path, &json, nullptr));
    CHECK(device->SetStatePropertiesFromJson(json, nullptr));
  }
}

// Updates the manager's state property if the new value is different from
// the current value. In this case also adds the appropriate notification ID
// to the array to record the state change for clients.
void UpdateValue(Manager* manager,
                 std::string Manager::* prop,
                 const std::string& new_value,
                 int notification,
                 std::vector<int>* notification_ids) {
  if (manager->*prop != new_value) {
    manager->*prop = new_value;
    notification_ids->push_back(notification);
  }
}

}  // anonymous namespace

class Manager::TaskRunner : public weave::provider::TaskRunner {
 public:
  void PostDelayedTask(const tracked_objects::Location& from_here,
                       const base::Closure& task,
                       base::TimeDelta delay) override {
    brillo::MessageLoop::current()->PostDelayedTask(from_here, task, delay);
  }
};

Manager::Manager(const Options& options,
                 const scoped_refptr<dbus::Bus>& bus)
    : options_{options}, bus_{bus} {}

Manager::~Manager() {
  android::BinderWrapper* binder_wrapper = android::BinderWrapper::Get();
  for (const auto& listener : notification_listeners_) {
    binder_wrapper->UnregisterForDeathNotifications(
        android::IInterface::asBinder(listener));
  }
  for (const auto& pair : services_) {
    binder_wrapper->UnregisterForDeathNotifications(
        android::IInterface::asBinder(pair.first));
  }
}

void Manager::Start(AsyncEventSequencer* sequencer) {
  power_manager_client_.Init();
  RestartWeave(sequencer);
}

void Manager::RestartWeave(AsyncEventSequencer* sequencer) {
  Stop();

  task_runner_.reset(new TaskRunner{});
  config_.reset(new BuffetConfig{options_.config_options});
  http_client_.reset(new HttpTransportClient);
  shill_client_.reset(new ShillClient{bus_,
                                      options_.device_whitelist,
                                      !options_.xmpp_enabled});
  weave::provider::HttpServer* http_server{nullptr};
#ifdef BUFFET_USE_WIFI_BOOTSTRAPPING
  if (!options_.disable_privet) {
    mdns_client_ = MdnsClient::CreateInstance();
    web_serv_client_.reset(new WebServClient{
        bus_, sequencer,
        base::Bind(&Manager::CreateDevice, weak_ptr_factory_.GetWeakPtr())});
    bluetooth_client_ = BluetoothClient::CreateInstance();
    http_server = web_serv_client_.get();

    if (options_.enable_ping) {
      auto ping_handler = base::Bind(
          [](std::unique_ptr<weave::provider::HttpServer::Request> request) {
            request->SendReply(brillo::http::status_code::Ok, "Hello, world!",
                               brillo::mime::text::kPlain);
          });
      http_server->AddHttpRequestHandler("/privet/ping", ping_handler);
      http_server->AddHttpsRequestHandler("/privet/ping", ping_handler);
    }
  }
#endif  // BUFFET_USE_WIFI_BOOTSTRAPPING

  if (!http_server)
    CreateDevice();
}

void Manager::CreateDevice() {
  if (device_)
    return;

  device_ = weave::Device::Create(config_.get(), task_runner_.get(),
                                  http_client_.get(), shill_client_.get(),
                                  mdns_client_.get(), web_serv_client_.get(),
                                  shill_client_.get(), bluetooth_client_.get());

  LoadTraitDefinitions(options_.config_options, device_.get());
  LoadCommandDefinitions(options_.config_options, device_.get());
  LoadStateDefinitions(options_.config_options, device_.get());
  LoadStateDefaults(options_.config_options, device_.get());

  device_->AddSettingsChangedCallback(
      base::Bind(&Manager::OnConfigChanged, weak_ptr_factory_.GetWeakPtr()));

  device_->AddTraitDefsChangedCallback(
      base::Bind(&Manager::OnTraitDefsChanged,
                 weak_ptr_factory_.GetWeakPtr()));
  device_->AddStateChangedCallback(
      base::Bind(&Manager::OnComponentTreeChanged,
                 weak_ptr_factory_.GetWeakPtr()));
  device_->AddComponentTreeChangedCallback(
      base::Bind(&Manager::OnComponentTreeChanged,
                 weak_ptr_factory_.GetWeakPtr()));

  device_->AddGcdStateChangedCallback(
      base::Bind(&Manager::OnGcdStateChanged, weak_ptr_factory_.GetWeakPtr()));

  device_->AddPairingChangedCallbacks(
      base::Bind(&Manager::OnPairingStart, weak_ptr_factory_.GetWeakPtr()),
      base::Bind(&Manager::OnPairingEnd, weak_ptr_factory_.GetWeakPtr()));

  device_->AddCommandHandler(kBaseComponent, kRebootCommand,
                             base::Bind(&Manager::OnRebootDevice,
                                        weak_ptr_factory_.GetWeakPtr()));

  CreateServicesForClients();
}

void Manager::Stop() {
  device_.reset();
#ifdef BUFFET_USE_WIFI_BOOTSTRAPPING
  web_serv_client_.reset();
  mdns_client_.reset();
#endif  // BUFFET_USE_WIFI_BOOTSTRAPPING
  shill_client_.reset();
  http_client_.reset();
  config_.reset();
  task_runner_.reset();
}

void Manager::OnTraitDefsChanged() {
  NotifyServiceManagerChange({NotificationListener::TRAITS});
}

void Manager::OnComponentTreeChanged() {
  NotifyServiceManagerChange({NotificationListener::COMPONENTS});
}

void Manager::OnGcdStateChanged(weave::GcdState state) {
  state_ = weave::EnumToString(state);
  NotifyServiceManagerChange({NotificationListener::STATE});
  property_set(weaved::system_properties::kState, state_.c_str());
}

void Manager::OnConfigChanged(const weave::Settings& settings) {
  std::vector<int> ids;
  UpdateValue(this, &Manager::cloud_id_, settings.cloud_id,
              NotificationListener::CLOUD_ID, &ids);
  UpdateValue(this, &Manager::device_id_, settings.device_id,
              NotificationListener::DEVICE_ID, &ids);
  UpdateValue(this, &Manager::device_name_, settings.name,
              NotificationListener::DEVICE_NAME, &ids);
  UpdateValue(this, &Manager::device_description_, settings.description,
              NotificationListener::DEVICE_DESCRIPTION, &ids);
  UpdateValue(this, &Manager::device_location_, settings.location,
              NotificationListener::DEVICE_LOCATION, &ids);
  UpdateValue(this, &Manager::oem_name_, settings.oem_name,
              NotificationListener::OEM_NAME, &ids);
  UpdateValue(this, &Manager::model_id_, settings.model_id,
              NotificationListener::MODEL_ID, &ids);
  UpdateValue(this, &Manager::model_name_, settings.model_name,
              NotificationListener::MODEL_NAME, &ids);
  NotifyServiceManagerChange(ids);
}

void Manager::OnPairingStart(const std::string& session_id,
                             weave::PairingType pairing_type,
                             const std::vector<uint8_t>& code) {
  // For now, just overwrite the exposed PairInfo with the most recent pairing
  // attempt.
  std::vector<int> ids;
  UpdateValue(this, &Manager::pairing_session_id_, session_id,
              NotificationListener::PAIRING_SESSION_ID, &ids);
  UpdateValue(this, &Manager::pairing_mode_, EnumToString(pairing_type),
              NotificationListener::PAIRING_MODE, &ids);
  std::string pairing_code{code.begin(), code.end()};
  UpdateValue(this, &Manager::pairing_code_, pairing_code,
              NotificationListener::PAIRING_CODE, &ids);
  NotifyServiceManagerChange(ids);
}

void Manager::OnPairingEnd(const std::string& session_id) {
  if (pairing_session_id_ != session_id)
    return;
  std::vector<int> ids;
  UpdateValue(this, &Manager::pairing_session_id_, "",
              NotificationListener::PAIRING_SESSION_ID, &ids);
  UpdateValue(this, &Manager::pairing_mode_, "",
              NotificationListener::PAIRING_MODE, &ids);
  UpdateValue(this, &Manager::pairing_code_, "",
              NotificationListener::PAIRING_CODE, &ids);
  NotifyServiceManagerChange(ids);
}

void Manager::OnRebootDevice(const std::weak_ptr<weave::Command>& cmd) {
  auto command = cmd.lock();
  if (!command || !command->Complete({}, nullptr))
    return;

  task_runner_->PostDelayedTask(
      FROM_HERE,
      base::Bind(&Manager::RebootDeviceNow, weak_ptr_factory_.GetWeakPtr()),
      base::TimeDelta::FromSeconds(2));
}

void Manager::RebootDeviceNow() {
  power_manager_client_.Reboot(android::RebootReason::DEFAULT);
}

android::binder::Status Manager::connect(
    const android::sp<android::weave::IWeaveClient>& client) {
  pending_clients_.push_back(client);
  if (device_)
    CreateServicesForClients();
  return android::binder::Status::ok();
}

android::binder::Status Manager::registerNotificationListener(
    const WeaveServiceManagerNotificationListener& listener) {
  notification_listeners_.insert(listener);
  android::BinderWrapper::Get()->RegisterForDeathNotifications(
      android::IInterface::asBinder(listener),
      base::Bind(&Manager::OnNotificationListenerDestroyed,
                 weak_ptr_factory_.GetWeakPtr(), listener));
  return android::binder::Status::ok();
}

android::binder::Status Manager::getCloudId(android::String16* id) {
  *id = weaved::binder_utils::ToString16(cloud_id_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getDeviceId(android::String16* id) {
  *id = weaved::binder_utils::ToString16(device_id_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getDeviceName(android::String16* name) {
  *name = weaved::binder_utils::ToString16(device_name_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getDeviceDescription(
    android::String16* description) {
  *description = weaved::binder_utils::ToString16(device_description_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getDeviceLocation(
    android::String16* location) {
  *location = weaved::binder_utils::ToString16(device_location_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getOemName(android::String16* name) {
  *name = weaved::binder_utils::ToString16(oem_name_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getModelName(android::String16* name) {
  *name = weaved::binder_utils::ToString16(model_name_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getModelId(android::String16* id) {
  *id = weaved::binder_utils::ToString16(model_id_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getPairingSessionId(android::String16* id) {
  *id = weaved::binder_utils::ToString16(pairing_session_id_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getPairingMode(android::String16* mode) {
  *mode = weaved::binder_utils::ToString16(pairing_mode_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getPairingCode(android::String16* code) {
  *code = weaved::binder_utils::ToString16(pairing_code_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getState(android::String16* state) {
  *state = weaved::binder_utils::ToString16(state_);
  return android::binder::Status::ok();
}

android::binder::Status Manager::getTraits(android::String16* traits) {
  *traits = weaved::binder_utils::ToString16(device_->GetTraits());
  return android::binder::Status::ok();
}

android::binder::Status Manager::getComponents(android::String16* components) {
  *components = weaved::binder_utils::ToString16(device_->GetComponents());
  return android::binder::Status::ok();
}

void Manager::CreateServicesForClients() {
  CHECK(device_);
  // For safety, iterate over a copy of |pending_clients_| and clear the
  // original vector before performing the iterations.
  std::vector<android::sp<android::weave::IWeaveClient>> pending_clients_copy;
  std::swap(pending_clients_copy, pending_clients_);
  for (const auto& client : pending_clients_copy) {
    android::sp<BinderWeaveService> service =
        new BinderWeaveService{device_.get(), client};
    services_.emplace(client, service);
    client->onServiceConnected(service);
    android::BinderWrapper::Get()->RegisterForDeathNotifications(
        android::IInterface::asBinder(client),
        base::Bind(&Manager::OnClientDisconnected,
                   weak_ptr_factory_.GetWeakPtr(),
                   client));
  }
}

void Manager::OnClientDisconnected(
    const android::sp<android::weave::IWeaveClient>& client) {
  services_.erase(client);
}

void Manager::OnNotificationListenerDestroyed(
    const WeaveServiceManagerNotificationListener& notification_listener) {
  notification_listeners_.erase(notification_listener);
}

void Manager::NotifyServiceManagerChange(
    const std::vector<int>& notification_ids) {
  if (notification_ids.empty())
    return;
  for (const auto& listener : notification_listeners_)
    listener->notifyServiceManagerChange(notification_ids);
}

}  // namespace buffet