491 lines
18 KiB
C++
491 lines
18 KiB
C++
// 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
|