diff options
41 files changed, 2002 insertions, 1725 deletions
@@ -30,7 +30,6 @@ buffetCommonCppFlags := \ buffetCommonCIncludes := \ $(LOCAL_PATH)/.. \ - $(LOCAL_PATH)/dbus-proxies \ external/cros/system_api \ external/gtest/include \ @@ -38,7 +37,10 @@ buffetSharedLibraries := \ libapmanager-client \ libavahi-common \ libavahi-client \ + libbinder \ + libbinderwrapper \ libbrillo \ + libbrillo-binder \ libbrillo-dbus \ libbrillo-http \ libbrillo-stream \ @@ -47,6 +49,7 @@ buffetSharedLibraries := \ libcutils \ libdbus \ libshill-client \ + libutils \ libweave \ libwebserv \ @@ -58,10 +61,38 @@ buffetSharedLibraries += \ endif -# buffet-common +# weave-common +# Code shared between weaved daemon and libweaved client library # ======================================================== include $(CLEAR_VARS) -LOCAL_MODULE := buffet-common +LOCAL_MODULE := weave-common +LOCAL_CPP_EXTENSION := $(buffetCommonCppExtension) +LOCAL_CFLAGS := $(buffetCommonCFlags) +LOCAL_CPPFLAGS := $(buffetCommonCppFlags) +LOCAL_C_INCLUDES := $(buffetCommonCIncludes) +LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/brillo +LOCAL_SHARED_LIBRARIES := $(buffetSharedLibraries) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) +LOCAL_CLANG := true + +LOCAL_SRC_FILES := \ + brillo/android/weave/IWeaveClient.aidl \ + brillo/android/weave/IWeaveCommand.aidl \ + brillo/android/weave/IWeaveService.aidl \ + brillo/android/weave/IWeaveServiceManager.aidl \ + brillo/android/weave/IWeaveServiceManagerNotificationListener.aidl \ + common/binder_constants.cc \ + common/binder_utils.cc \ + common/data_conversion.cc \ + +include $(BUILD_STATIC_LIBRARY) + +# weave-daemon-common +# Code shared between weaved daemon and unit test runner. +# This is essentially the implementation of weaved in a static library format. +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE := weave-daemon-common LOCAL_CPP_EXTENSION := $(buffetCommonCppExtension) LOCAL_CFLAGS := $(buffetCommonCFlags) # TODO(avakulenko): Remove -Wno-deprecated-declarations when legacy libweave @@ -69,17 +100,17 @@ LOCAL_CFLAGS := $(buffetCommonCFlags) LOCAL_CPPFLAGS := $(buffetCommonCppFlags) -Wno-deprecated-declarations LOCAL_C_INCLUDES := $(buffetCommonCIncludes) LOCAL_SHARED_LIBRARIES := $(buffetSharedLibraries) -LOCAL_STATIC_LIBRARIES := +LOCAL_STATIC_LIBRARIES := weave-common LOCAL_CLANG := true LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) LOCAL_SRC_FILES := \ + brillo/weaved_system_properties.cc \ buffet/ap_manager_client.cc \ buffet/avahi_mdns_client.cc \ + buffet/binder_command_proxy.cc \ + buffet/binder_weave_service.cc \ buffet/buffet_config.cc \ - buffet/dbus_command_dispatcher.cc \ - buffet/dbus_command_proxy.cc \ - buffet/dbus_conversion.cc \ buffet/dbus_constants.cc \ buffet/flouride_socket_bluetooth_client.cc \ buffet/http_transport_client.cc \ @@ -87,9 +118,6 @@ LOCAL_SRC_FILES := \ buffet/shill_client.cc \ buffet/socket_stream.cc \ buffet/webserv_client.cc \ - buffet/dbus_bindings/dbus-service-config.json \ - buffet/dbus_bindings/com.android.Weave.Command.dbus-xml \ - buffet/dbus_bindings/com.android.Weave.Manager.dbus-xml \ ifdef BRILLO LOCAL_SRC_FILES += buffet/keystore_encryptor.cc @@ -99,28 +127,13 @@ endif include $(BUILD_STATIC_LIBRARY) -# weaved-brillo-api -# ======================================================== -include $(CLEAR_VARS) -LOCAL_MODULE := weaved-brillo-api -LOCAL_CPP_EXTENSION := $(buffetCommonCppExtension) -LOCAL_CFLAGS := $(buffetCommonCFlags) -LOCAL_CPPFLAGS := $(buffetCommonCppFlags) -LOCAL_CLANG := true -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) - -LOCAL_SRC_FILES := \ - brillo/weaved_system_properties.cc \ - -include $(BUILD_STATIC_LIBRARY) - # weaved +# The main binary of the weave daemon. # ======================================================== include $(CLEAR_VARS) LOCAL_MODULE := weaved LOCAL_REQUIRED_MODULES := \ avahi-daemon \ - com.android.Weave.conf \ libweaved \ webservd \ @@ -130,8 +143,9 @@ LOCAL_CPPFLAGS := $(buffetCommonCppFlags) LOCAL_C_INCLUDES := $(buffetCommonCIncludes) LOCAL_INIT_RC := weaved.rc LOCAL_SHARED_LIBRARIES := $(buffetSharedLibraries) -LOCAL_STATIC_LIBRARIES := weaved-brillo-api -LOCAL_WHOLE_STATIC_LIBRARIES := buffet-common +LOCAL_STATIC_LIBRARIES := weave-common \ + +LOCAL_WHOLE_STATIC_LIBRARIES := weave-daemon-common LOCAL_CLANG := true LOCAL_SRC_FILES := \ @@ -139,22 +153,9 @@ LOCAL_SRC_FILES := \ include $(BUILD_EXECUTABLE) -# libweaved-internal -# ======================================================== -# You do not want to depend on this. Depend on libweaved instead. -# libweaved abstracts and helps you consume this interface. -include $(CLEAR_VARS) -LOCAL_MODULE := libweaved-internal -LOCAL_DBUS_PROXY_PREFIX := buffet - -LOCAL_SRC_FILES := \ - buffet/dbus_bindings/dbus-service-config.json \ - buffet/dbus_bindings/com.android.Weave.Command.dbus-xml \ - buffet/dbus_bindings/com.android.Weave.Manager.dbus-xml \ - -include $(BUILD_SHARED_LIBRARY) - # libweaved +# The client library for the weave daemon. You should link to libweaved, +# if you need to communicate with weaved. # ======================================================== include $(CLEAR_VARS) LOCAL_MODULE := libweaved @@ -163,16 +164,14 @@ LOCAL_CFLAGS := $(buffetCommonCFlags) LOCAL_CPPFLAGS := $(buffetCommonCppFlags) LOCAL_C_INCLUDES := external/gtest/include LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) -LOCAL_SHARED_LIBRARIES := \ - $(buffetSharedLibraries) \ - libweaved-internal \ +LOCAL_SHARED_LIBRARIES := $(buffetSharedLibraries) +LOCAL_STATIC_LIBRARIES := weave-common -LOCAL_STATIC_LIBRARIES := LOCAL_CLANG := true LOCAL_SRC_FILES := \ libweaved/command.cc \ - libweaved/device.cc \ + libweaved/service.cc \ include $(BUILD_SHARED_LIBRARY) @@ -192,30 +191,20 @@ LOCAL_SHARED_LIBRARIES := \ $(buffetSharedLibraries) \ LOCAL_STATIC_LIBRARIES := \ - buffet-common \ libbrillo-test-helpers \ - libchrome_dbus_test_helpers \ libchrome_test_helpers \ libgtest \ libgmock \ libweave-test \ - weaved-brillo-api \ + weave-daemon-common \ + weave-common \ LOCAL_CLANG := true LOCAL_SRC_FILES := \ + buffet/binder_command_proxy_unittest.cc \ buffet/buffet_config_unittest.cc \ buffet/buffet_testrunner.cc \ - buffet/dbus_command_proxy_unittest.cc \ - buffet/dbus_conversion_unittest.cc \ + common/data_conversion_unittest.cc \ include $(BUILD_NATIVE_TEST) - -# DBus config files for /etc/dbus-1 -# ======================================================== -include $(CLEAR_VARS) -LOCAL_MODULE := com.android.Weave.conf -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/dbus-1 -LOCAL_SRC_FILES := buffet/etc/dbus-1/com.android.Weave.conf -include $(BUILD_PREBUILT) diff --git a/brillo/android/weave/IWeaveClient.aidl b/brillo/android/weave/IWeaveClient.aidl new file mode 100644 index 0000000..7712c9d --- /dev/null +++ b/brillo/android/weave/IWeaveClient.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 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. + */ + +package android.weave; + +import android.weave.IWeaveCommand; +import android.weave.IWeaveService; + +interface IWeaveClient { + oneway void onServiceConnected(in IWeaveService service); + oneway void onCommand(in String componentName, + in String commandName, + in IWeaveCommand command); +} diff --git a/brillo/android/weave/IWeaveCommand.aidl b/brillo/android/weave/IWeaveCommand.aidl new file mode 100644 index 0000000..b445fb0 --- /dev/null +++ b/brillo/android/weave/IWeaveCommand.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 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. + */ + +package android.weave; + +interface IWeaveCommand { + String getId(); + String getName(); + String getComponent(); + String getState(); + String getOrigin(); + String getParameters(); + String getProgress(); + String getResults(); + + void setProgress(in String progress); + void complete(in String results); + void abort(in String errorCode, in String errorMessage); + void cancel(); + void pause(); + void setError(in String errorCode, in String errorMessage); +} diff --git a/brillo/android/weave/IWeaveService.aidl b/brillo/android/weave/IWeaveService.aidl new file mode 100644 index 0000000..0fcc422 --- /dev/null +++ b/brillo/android/weave/IWeaveService.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 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. + */ + +package android.weave; + +interface IWeaveService { + void addComponent(in String name, in List<String> traits); + void registerCommandHandler(in String component, in String command); + void updateState(in String component, in String state); +} diff --git a/brillo/android/weave/IWeaveServiceManager.aidl b/brillo/android/weave/IWeaveServiceManager.aidl new file mode 100644 index 0000000..ff8a4c6 --- /dev/null +++ b/brillo/android/weave/IWeaveServiceManager.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 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. + */ + +package android.weave; + +import android.weave.IWeaveClient; +import android.weave.IWeaveServiceManagerNotificationListener; + +interface IWeaveServiceManager { + oneway void connect(in IWeaveClient client); + oneway void registerNotificationListener( + in IWeaveServiceManagerNotificationListener listener); + + String getCloudId(); + String getDeviceId(); + String getDeviceName(); + String getDeviceDescription(); + String getDeviceLocation(); + String getOemName(); + String getModelName(); + String getModelId(); + String getPairingSessionId(); + String getPairingMode(); + String getPairingCode(); + String getState(); + String getTraits(); + String getComponents(); +} diff --git a/brillo/android/weave/IWeaveServiceManagerNotificationListener.aidl b/brillo/android/weave/IWeaveServiceManagerNotificationListener.aidl new file mode 100644 index 0000000..a3b91a3 --- /dev/null +++ b/brillo/android/weave/IWeaveServiceManagerNotificationListener.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 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. + */ + +package android.weave; + +oneway interface IWeaveServiceManagerNotificationListener { + const int CLOUD_ID = 1; + const int DEVICE_ID = 2; + const int DEVICE_NAME = 3; + const int DEVICE_DESCRIPTION = 4; + const int DEVICE_LOCATION = 5; + const int OEM_NAME = 6; + const int MODEL_NAME = 7; + const int MODEL_ID = 8; + const int PAIRING_SESSION_ID = 9; + const int PAIRING_MODE = 10; + const int PAIRING_CODE = 11; + const int TRAITS = 12; + const int COMPONENTS = 13; + const int STATE = 14; + + void notifyServiceManagerChange(in int[] notificationIds); +} diff --git a/buffet/binder_command_proxy.cc b/buffet/binder_command_proxy.cc new file mode 100644 index 0000000..487f6cc --- /dev/null +++ b/buffet/binder_command_proxy.cc @@ -0,0 +1,182 @@ +// 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 "buffet/binder_command_proxy.h" + +#include <weave/enum_to_string.h> + +#include "buffet/weave_error_conversion.h" +#include "common/binder_utils.h" + +using weaved::binder_utils::ParseDictionary; +using weaved::binder_utils::ToStatus; +using weaved::binder_utils::ToString; +using weaved::binder_utils::ToString16; + +namespace buffet { + +namespace { + +const char kErrorDomain[] = "weaved"; + +android::binder::Status ReportDestroyedError() { + return android::binder::Status::fromServiceSpecificError( + 1, android::String8{"Command has been destroyed"}); +} + +} // anonymous namespace + +BinderCommandProxy::BinderCommandProxy( + const std::weak_ptr<weave::Command>& command) : command_{command} {} + +android::binder::Status BinderCommandProxy::getId(android::String16* id) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + *id = ToString16(command->GetID()); + return android::binder::Status::ok(); +} + +android::binder::Status BinderCommandProxy::getName(android::String16* name) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + *name = ToString16(command->GetName()); + return android::binder::Status::ok(); +} + +android::binder::Status BinderCommandProxy::getComponent( + android::String16* component) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + *component = ToString16(command->GetComponent()); + return android::binder::Status::ok(); +} + +android::binder::Status BinderCommandProxy::getState(android::String16* state) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + *state = ToString16(EnumToString(command->GetState())); + return android::binder::Status::ok(); +} + +android::binder::Status BinderCommandProxy::getOrigin( + android::String16* origin) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + *origin = ToString16(EnumToString(command->GetOrigin())); + return android::binder::Status::ok(); +} + +android::binder::Status BinderCommandProxy::getParameters( + android::String16* parameters) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + *parameters = ToString16(command->GetParameters()); + return android::binder::Status::ok(); +} + +android::binder::Status BinderCommandProxy::getProgress( + android::String16* progress) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + *progress = ToString16(command->GetProgress()); + return android::binder::Status::ok(); +} + +android::binder::Status BinderCommandProxy::getResults( + android::String16* results) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + *results = ToString16(command->GetResults()); + return android::binder::Status::ok(); +} + +android::binder::Status BinderCommandProxy::setProgress( + const android::String16& progress) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + std::unique_ptr<base::DictionaryValue> dict; + auto status = ParseDictionary(progress, &dict); + if (status.isOk()) { + weave::ErrorPtr error; + status = ToStatus(command->SetProgress(*dict, &error), &error); + } + return status; +} + +android::binder::Status BinderCommandProxy::complete( + const android::String16& results) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + std::unique_ptr<base::DictionaryValue> dict; + auto status = ParseDictionary(results, &dict); + if (status.isOk()) { + weave::ErrorPtr error; + status = ToStatus(command->Complete(*dict, &error), &error); + } + return status; +} + +android::binder::Status BinderCommandProxy::abort( + const android::String16& errorCode, + const android::String16& errorMessage) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + weave::ErrorPtr command_error; + weave::Error::AddTo(&command_error, FROM_HERE, kErrorDomain, + ToString(errorCode), ToString(errorMessage)); + weave::ErrorPtr error; + return ToStatus(command->Abort(command_error.get(), &error), &error); +} + +android::binder::Status BinderCommandProxy::cancel() { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + weave::ErrorPtr error; + return ToStatus(command->Cancel(&error), &error); +} + +android::binder::Status BinderCommandProxy::pause() { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + weave::ErrorPtr error; + return ToStatus(command->Pause(&error), &error); +} + +android::binder::Status BinderCommandProxy::setError( + const android::String16& errorCode, + const android::String16& errorMessage) { + auto command = command_.lock(); + if (!command) + return ReportDestroyedError(); + weave::ErrorPtr command_error; + weave::Error::AddTo(&command_error, FROM_HERE, kErrorDomain, + ToString(errorCode), ToString(errorMessage)); + weave::ErrorPtr error; + return ToStatus(command->SetError(command_error.get(), &error), &error); +} + +} // namespace buffet diff --git a/buffet/binder_command_proxy.h b/buffet/binder_command_proxy.h new file mode 100644 index 0000000..4c05b89 --- /dev/null +++ b/buffet/binder_command_proxy.h @@ -0,0 +1,62 @@ +// 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. + +#ifndef BUFFET_BINDER_COMMAND_PROXY_H_ +#define BUFFET_BINDER_COMMAND_PROXY_H_ + +#include <string> + +#include <base/macros.h> +#include <weave/command.h> + +#include "android/weave/BnWeaveCommand.h" + +namespace buffet { + +// Implementation of android::weave::IWeaveCommand binder object. +// This class simply redirects binder calls to the underlying weave::Command +// object (and performs necessary parameter/result type conversions). +class BinderCommandProxy : public android::weave::BnWeaveCommand { + public: + explicit BinderCommandProxy(const std::weak_ptr<weave::Command>& command); + ~BinderCommandProxy() override = default; + + android::binder::Status getId(android::String16* id) override; + android::binder::Status getName(android::String16* name) override; + android::binder::Status getComponent(android::String16* component) override; + android::binder::Status getState(android::String16* state) override; + android::binder::Status getOrigin(android::String16* origin) override; + android::binder::Status getParameters(android::String16* parameters) override; + android::binder::Status getProgress(android::String16* progress) override; + android::binder::Status getResults(android::String16* results) override; + android::binder::Status setProgress( + const android::String16& progress) override; + android::binder::Status complete(const android::String16& results) override; + android::binder::Status abort(const android::String16& errorCode, + const android::String16& errorMessage) override; + android::binder::Status cancel() override; + android::binder::Status pause() override; + android::binder::Status setError( + const android::String16& errorCode, + const android::String16& errorMessage) override; + + private: + std::weak_ptr<weave::Command> command_; + + DISALLOW_COPY_AND_ASSIGN(BinderCommandProxy); +}; + +} // namespace buffet + +#endif // BUFFET_BINDER_COMMAND_PROXY_H_ diff --git a/buffet/binder_command_proxy_unittest.cc b/buffet/binder_command_proxy_unittest.cc new file mode 100644 index 0000000..0905440 --- /dev/null +++ b/buffet/binder_command_proxy_unittest.cc @@ -0,0 +1,163 @@ +// 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 "buffet/binder_command_proxy.h" + +#include <memory> + +#include <gtest/gtest.h> +#include <weave/command.h> +#include <weave/enum_to_string.h> +#include <weave/test/mock_command.h> +#include <weave/test/unittest_utils.h> + +#include "common/binder_utils.h" + +using weaved::binder_utils::ToString; +using weaved::binder_utils::ToString16; + +namespace buffet { + +using ::testing::_; +using ::testing::Return; +using ::testing::ReturnRef; +using ::testing::ReturnRefOfCopy; +using ::testing::StrictMock; + +using weave::test::CreateDictionaryValue; +using weave::test::IsEqualValue; + +namespace { + +const char kTestCommandId[] = "cmd_1"; + +MATCHER_P(EqualToJson, json, "") { + auto json_value = CreateDictionaryValue(json); + return IsEqualValue(*json_value, arg); +} + +MATCHER_P2(ExpectError, code, message, "") { + return arg->GetCode() == code && arg->GetMessage() == message; +} + +} // namespace + +class BinderCommandProxyTest : public ::testing::Test { + public: + void SetUp() override { + command_ = std::make_shared<StrictMock<weave::test::MockCommand>>(); + + expected_result_dict_.SetInteger("height", 53); + expected_result_dict_.SetString("_jumpType", "_withKick"); + EXPECT_CALL(*command_, GetID()) + .WillRepeatedly(ReturnRefOfCopy<std::string>(kTestCommandId)); + EXPECT_CALL(*command_, GetName()) + .WillRepeatedly(ReturnRefOfCopy<std::string>("robot.jump")); + EXPECT_CALL(*command_, GetComponent()) + .WillRepeatedly(ReturnRefOfCopy<std::string>("myComponent")); + EXPECT_CALL(*command_, GetState()) + .WillRepeatedly(Return(weave::Command::State::kQueued)); + EXPECT_CALL(*command_, GetOrigin()) + .WillRepeatedly(Return(weave::Command::Origin::kLocal)); + EXPECT_CALL(*command_, GetParameters()) + .WillRepeatedly(ReturnRef(expected_result_dict_)); + EXPECT_CALL(*command_, GetProgress()) + .WillRepeatedly(ReturnRef(empty_dict_)); + EXPECT_CALL(*command_, GetResults()) + .WillRepeatedly(ReturnRef(empty_dict_)); + + proxy_.reset( + new BinderCommandProxy{std::weak_ptr<weave::Command>{command_}}); + } + + BinderCommandProxy* GetCommandProxy() const { return proxy_.get(); } + + weave::Command::State GetCommandState() const { + weave::Command::State state; + android::String16 state_string; + EXPECT_TRUE(GetCommandProxy()->getState(&state_string).isOk()); + EXPECT_TRUE(StringToEnum(ToString(state_string), &state)); + return state; + } + + weave::Command::Origin GetCommandOrigin() const { + weave::Command::Origin origin; + android::String16 origin_string; + EXPECT_TRUE(GetCommandProxy()->getOrigin(&origin_string).isOk()); + EXPECT_TRUE(StringToEnum(ToString(origin_string), &origin)); + return origin; + } + + base::DictionaryValue empty_dict_; + base::DictionaryValue expected_result_dict_; + + std::shared_ptr<StrictMock<weave::test::MockCommand>> command_; + std::unique_ptr<BinderCommandProxy> proxy_; +}; + +TEST_F(BinderCommandProxyTest, Init) { + android::String16 result; + EXPECT_EQ(weave::Command::State::kQueued, GetCommandState()); + EXPECT_EQ(weave::Command::Origin::kLocal, GetCommandOrigin()); + EXPECT_TRUE(GetCommandProxy()->getParameters(&result).isOk()); + EXPECT_EQ(R"({"_jumpType":"_withKick","height":53})", ToString(result)); + EXPECT_TRUE(GetCommandProxy()->getProgress(&result).isOk()); + EXPECT_EQ("{}", ToString(result)); + EXPECT_TRUE(GetCommandProxy()->getResults(&result).isOk()); + EXPECT_EQ("{}", ToString(result)); + EXPECT_TRUE(GetCommandProxy()->getName(&result).isOk()); + EXPECT_EQ("robot.jump", ToString(result)); + EXPECT_TRUE(GetCommandProxy()->getComponent(&result).isOk()); + EXPECT_EQ("myComponent", ToString(result)); + EXPECT_TRUE(GetCommandProxy()->getId(&result).isOk()); + EXPECT_EQ(kTestCommandId, ToString(result)); +} + +TEST_F(BinderCommandProxyTest, SetProgress) { + EXPECT_CALL(*command_, SetProgress(EqualToJson("{'progress': 10}"), _)) + .WillOnce(Return(true)); + EXPECT_TRUE( + GetCommandProxy()->setProgress(ToString16(R"({"progress": 10})")).isOk()); +} + +TEST_F(BinderCommandProxyTest, Complete) { + EXPECT_CALL( + *command_, + Complete( + EqualToJson("{'foo': 42, 'bar': 'foobar', 'resultList': [1, 2, 3]}"), + _)) + .WillOnce(Return(true)); + const android::String16 result{ + R"({"foo": 42, "bar": "foobar", "resultList": [1, 2, 3]})"}; + EXPECT_TRUE(GetCommandProxy()->complete(result).isOk()); +} + +TEST_F(BinderCommandProxyTest, Abort) { + EXPECT_CALL(*command_, Abort(ExpectError("foo", "bar"), _)) + .WillOnce(Return(true)); + EXPECT_TRUE( + GetCommandProxy()->abort(ToString16("foo"), ToString16("bar")).isOk()); +} + +TEST_F(BinderCommandProxyTest, Cancel) { + EXPECT_CALL(*command_, Cancel(_)).WillOnce(Return(true)); + EXPECT_TRUE(GetCommandProxy()->cancel().isOk()); +} + +TEST_F(BinderCommandProxyTest, Pause) { + EXPECT_CALL(*command_, Pause(_)).WillOnce(Return(true)); + EXPECT_TRUE(GetCommandProxy()->pause().isOk()); +} + +} // namespace buffet diff --git a/buffet/binder_weave_service.cc b/buffet/binder_weave_service.cc new file mode 100644 index 0000000..adbad11 --- /dev/null +++ b/buffet/binder_weave_service.cc @@ -0,0 +1,90 @@ +// 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 "buffet/binder_weave_service.h" + +#include <algorithm> + +#include <base/bind.h> +#include <weave/command.h> +#include <weave/device.h> + +#include "buffet/binder_command_proxy.h" +#include "common/binder_utils.h" + +using weaved::binder_utils::ToStatus; +using weaved::binder_utils::ToString; +using weaved::binder_utils::ToString16; + +namespace buffet { + +BinderWeaveService::BinderWeaveService( + weave::Device* device, + android::sp<android::weave::IWeaveClient> client) + : device_{device}, client_{client} {} + +BinderWeaveService::~BinderWeaveService() { + // TODO(avakulenko): Make it possible to remove components from the tree in + // libweave and enable the following code. + // for (const std::string& component : components_) + // device_->RemoveComponent(component, nullptr); +} + +android::binder::Status BinderWeaveService::addComponent( + const android::String16& name, + const std::vector<android::String16>& traits) { + std::string component_name = ToString(name); + weave::ErrorPtr error; + std::vector<std::string> supported_traits; + std::transform(traits.begin(), traits.end(), + std::back_inserter(supported_traits), ToString); + if (!device_->AddComponent(component_name, supported_traits, &error)) + return ToStatus(false, &error); + components_.push_back(component_name); + return android::binder::Status::ok(); +} + +android::binder::Status BinderWeaveService::registerCommandHandler( + const android::String16& component, + const android::String16& command) { + std::string component_name = ToString(component); + std::string command_name = ToString(command); + device_->AddCommandHandler(component_name, command_name, + base::Bind(&BinderWeaveService::OnCommand, + weak_ptr_factory_.GetWeakPtr(), + component_name, command_name)); + return android::binder::Status::ok(); +} + +android::binder::Status BinderWeaveService::updateState( + const android::String16& component, + const android::String16& state) { + weave::ErrorPtr error; + return ToStatus(device_->SetStatePropertiesFromJson(ToString(component), + ToString(state), + &error), + &error); +} + +void BinderWeaveService::OnCommand( + const std::string& component_name, + const std::string& command_name, + const std::weak_ptr<weave::Command>& command) { + android::sp<android::weave::IWeaveCommand> command_proxy = + new BinderCommandProxy{command}; + client_->onCommand(ToString16(component_name), ToString16(command_name), + command_proxy); +} + +} // namespace buffet diff --git a/buffet/binder_weave_service.h b/buffet/binder_weave_service.h new file mode 100644 index 0000000..c033366 --- /dev/null +++ b/buffet/binder_weave_service.h @@ -0,0 +1,72 @@ +// 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. + +#ifndef BUFFET_BINDER_WEAVE_SERVICE_H_ +#define BUFFET_BINDER_WEAVE_SERVICE_H_ + +#include <memory> +#include <vector> +#include <string> + +#include <base/macros.h> +#include <base/memory/weak_ptr.h> + +#include "android/weave/IWeaveClient.h" +#include "android/weave/BnWeaveService.h" + +namespace weave { +class Command; +class Device; +} + +namespace buffet { + +// An implementation of android::weave::IWeaveService binder. +// This object is a proxy for weave::Device. A new instance of weave service is +// created for each connected client. As soon as the client disconnects, this +// object takes care of cleaning up that client's resources (e.g. it removes +// the components and their state added by the client). +class BinderWeaveService final : public android::weave::BnWeaveService { + public: + BinderWeaveService(weave::Device* device, + android::sp<android::weave::IWeaveClient> client); + ~BinderWeaveService() override; + + private: + // Binder methods for android::weave::IWeaveService: + android::binder::Status addComponent( + const android::String16& name, + const std::vector<android::String16>& traits) override; + android::binder::Status registerCommandHandler( + const android::String16& component, + const android::String16& command) override; + android::binder::Status updateState( + const android::String16& component, + const android::String16& state) override; + + void OnCommand(const std::string& component_name, + const std::string& command_name, + const std::weak_ptr<weave::Command>& command); + + weave::Device* device_; + android::sp<android::weave::IWeaveClient> client_; + std::vector<std::string> components_; + + base::WeakPtrFactory<BinderWeaveService> weak_ptr_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(BinderWeaveService); +}; + +} // namespace buffet + +#endif // BUFFET_BINDER_WEAVE_SERVICE_H_ diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp deleted file mode 100644 index 3eeaa44..0000000 --- a/buffet/buffet.gyp +++ /dev/null @@ -1,162 +0,0 @@ -{ - 'target_defaults': { - 'variables': { - 'deps': [ - 'libbrillo-<(libbase_ver)', - 'libchrome-<(libbase_ver)', - 'system_api', - ], - }, - 'include_dirs': [ - '.', - ], - }, - 'targets': [ - { - 'target_name': 'buffet_common', - 'type': 'static_library', - 'variables': { - 'dbus_adaptors_out_dir': 'include/buffet/dbus_bindings', - 'dbus_service_config': 'dbus_bindings/dbus-service-config.json', - 'exported_deps': [ - 'libweave-<(libbase_ver)', - ], - 'deps': ['>@(exported_deps)'], - }, - 'all_dependent_settings': { - 'variables': { - 'deps': [ - '<@(exported_deps)', - ], - }, - }, - 'sources': [ - 'ap_manager_client.cc', - 'buffet_config.cc', - 'dbus_bindings/com.android.Weave.Command.dbus-xml', - 'dbus_bindings/com.android.Weave.Manager.dbus-xml', - 'dbus_command_dispatcher.cc', - 'dbus_command_proxy.cc', - 'dbus_conversion.cc', - 'dbus_constants.cc', - 'http_transport_client.cc', - 'manager.cc', - 'shill_client.cc', - 'socket_stream.cc', - ], - 'conditions': [ - ['USE_wifi_bootstrapping == 1', { - 'variables': { - 'exported_deps': [ - 'libpeerd-client', - 'libwebserv-<(libbase_ver)', - ], - }, - 'all_dependent_settings': { - 'defines': [ 'BUFFET_USE_WIFI_BOOTSTRAPPING' ], - }, - 'defines': [ 'BUFFET_USE_WIFI_BOOTSTRAPPING' ], - 'sources': [ - 'webserv_client.cc', - 'peerd_client.cc', - ], - }], - ], - 'includes': ['../common-mk/generate-dbus-adaptors.gypi'], - 'actions': [ - { - 'action_name': 'generate-buffet-proxies', - 'variables': { - 'dbus_service_config': 'dbus_bindings/dbus-service-config.json', - 'proxy_output_file': 'include/buffet/dbus-proxies.h' - }, - 'sources': [ - 'dbus_bindings/com.android.Weave.Command.dbus-xml', - 'dbus_bindings/com.android.Weave.Manager.dbus-xml', - ], - 'includes': ['../common-mk/generate-dbus-proxies.gypi'], - }, - { - # Import D-Bus bindings from shill. - 'action_name': 'generate-shill-proxies', - 'variables': { - 'dbus_service_config': '../shill/dbus_bindings/dbus-service-config.json', - 'proxy_output_file': 'include/shill/dbus-proxies.h' - }, - 'sources': [ - '../shill/dbus_bindings/com.chromium.flimflam.Device.xml', - '../shill/dbus_bindings/com.chromium.flimflam.Manager.xml', - '../shill/dbus_bindings/com.chromium.flimflam.Service.xml', - ], - 'includes': ['../common-mk/generate-dbus-proxies.gypi'], - }, - { - # Import D-Bus bindings from apmanager. - 'action_name': 'generate-apmanager-proxies', - 'variables': { - 'dbus_service_config': '../apmanager/dbus_bindings/dbus-service-config.json', - 'proxy_output_file': 'include/apmanager/dbus-proxies.h' - }, - 'sources': [ - '../apmanager/dbus_bindings/com.chromium.apmanager.Config.xml', - '../apmanager/dbus_bindings/com.chromium.apmanager.Device.xml', - '../apmanager/dbus_bindings/com.chromium.apmanager.Manager.xml', - '../apmanager/dbus_bindings/com.chromium.apmanager.Service.xml', - ], - 'includes': ['../common-mk/generate-dbus-proxies.gypi'], - }, - ], - }, - { - 'target_name': 'buffet', - 'type': 'executable', - 'dependencies': [ - 'buffet_common', - ], - 'sources': [ - 'main.cc', - ], - }, - { - 'target_name': 'buffet_test_daemon', - 'type': 'executable', - 'sources': [ - 'test_daemon/main.cc', - ], - }, - { - 'target_name': 'buffet_client', - 'type': 'executable', - 'sources': [ - 'buffet_client.cc', - ], - }, - ], - 'conditions': [ - ['USE_test == 1', { - 'targets': [ - { - 'target_name': 'buffet_testrunner', - 'type': 'executable', - 'dependencies': [ - 'buffet_common', - ], - 'variables': { - 'deps': [ - 'libbrillo-test-<(libbase_ver)', - 'libchrome-test-<(libbase_ver)', - 'libweave-test-<(libbase_ver)', - ], - }, - 'includes': ['../common-mk/common_test.gypi'], - 'sources': [ - 'buffet_config_unittest.cc', - 'buffet_testrunner.cc', - 'dbus_command_proxy_unittest.cc', - 'dbus_conversion_unittest.cc', - ], - }, - ], - }], - ], -} diff --git a/buffet/dbus_bindings/com.android.Weave.Command.dbus-xml b/buffet/dbus_bindings/com.android.Weave.Command.dbus-xml deleted file mode 100644 index 3602135..0000000 --- a/buffet/dbus_bindings/com.android.Weave.Command.dbus-xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> - -<node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - <interface name="com.android.Weave.Command"> - <method name="SetProgress"> - <arg name="progress" type="a{sv}" direction="in"/> - <annotation name="org.chromium.DBus.Method.Kind" value="normal"/> - </method> - <method name="Complete"> - <tp:docstring> - Mark the command as successfully completed and specifies the command - results to be returned to the caller. - </tp:docstring> - <arg name="results" type="a{sv}" direction="in"/> - <annotation name="org.chromium.DBus.Method.Kind" value="normal"/> - </method> - <method name="Abort"> - <tp:docstring> - Mark the command as aborted. This tells the cloud that the device did - not successfully complete executing the command. The error information - is provided in |code| and |message| parameters. - </tp:docstring> - <arg name="code" type="s" direction="in"/> - <arg name="message" type="s" direction="in"/> - <annotation name="org.chromium.DBus.Method.Kind" value="normal"/> - </method> - <method name="Cancel"> - <tp:docstring> - Mark the command as cancelled. Unlike Abort() this should be used when - the device detects a user request to cancel a command. - </tp:docstring> - <annotation name="org.chromium.DBus.Method.Kind" value="normal"/> - </method> - <property name="Name" type="s" access="read"/> - <property name="Id" type="s" access="read"/> - <property name="Component" type="s" access="read"/> - <property name="State" type="s" access="read"/> - <property name="Parameters" type="a{sv}" access="read"/> - <property name="Progress" type="a{sv}" access="read"/> - <property name="Results" type="a{sv}" access="read"/> - <property name="Origin" type="s" access="read"> - <tp:docstring> - Specifies the origin of the command. This is a string containing - "cloud" or "local" indicating the method of delivery of the command. - </tp:docstring> - </property> - </interface> -</node> diff --git a/buffet/dbus_bindings/com.android.Weave.Manager.dbus-xml b/buffet/dbus_bindings/com.android.Weave.Manager.dbus-xml deleted file mode 100644 index ecbb511..0000000 --- a/buffet/dbus_bindings/com.android.Weave.Manager.dbus-xml +++ /dev/null @@ -1,133 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> - -<node name="/com/android/Weave/Manager" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - <interface name="com.android.Weave.Manager"> - <tp:docstring> - The Manager is responsible for global state of Buffet. It exposes - interfaces which affect the entire device such as device registration and - device state. - </tp:docstring> - <method name="RegisterDevice"> - <arg name="ticket_id" type="s" direction="in"/> - <arg name="device_id" type="s" direction="out"/> - <annotation name="org.chromium.DBus.Method.Kind" value="async"/> - </method> - <method name="AddComponent"> - <arg name="name" type="s" direction="in"/> - <arg name="traits" type="as" direction="in"/> - <annotation name="org.chromium.DBus.Method.Kind" value="async"/> - </method> - <method name="UpdateState"> - <arg name="component" type="s" direction="in"/> - <arg name="property_set" type="a{sv}" direction="in"/> - <annotation name="org.chromium.DBus.Method.Kind" value="async"/> - </method> - <method name="AddCommand"> - <arg name="json_command" type="s" direction="in"/> - <arg name="id" type="s" direction="out"/> - <annotation name="org.chromium.DBus.Method.Kind" value="async"/> - </method> - <method name="TestMethod"> - <arg name="message" type="s" direction="in"/> - <arg name="echoed_message" type="s" direction="out"/> - <annotation name="org.chromium.DBus.Method.Kind" value="simple"/> - </method> - <property name="Status" type="s" access="read"> - <tp:docstring> - State of Buffet's cloud registration. - Possible values include: - "unconfigured": Buffet has no credentials, either from an out of box - state, or because device was unregistered. - - "connecting": Buffet is registered and attempting to connect to the - cloud. - - "connected": Buffet is online and connected to the cloud. Note that - only this state requires internet connectivity. - - "invalid_credentials": Buffet has credentials, but they are no longer - valid. - </tp:docstring> - </property> - <property name="DeviceId" type="s" access="read"> - <tp:docstring> - GCD ID if the device is registered or empty otherwise. - </tp:docstring> - </property> - <property name="Components" type="s" access="read"> - <tp:docstring> - JSON with device component tree. - </tp:docstring> - </property> - <property name="Traits" type="s" access="read"> - <tp:docstring> - JSON with device trait definitions. - </tp:docstring> - </property> - <property name="OemName" type="s" access="read"> - <tp:docstring> - Name of the device maker. - </tp:docstring> - </property> - <property name="ModelName" type="s" access="read"> - <tp:docstring> - Name of the device model. - </tp:docstring> - </property> - <property name="ModelId" type="s" access="read"> - <tp:docstring> - Five character code assigned by the cloud registry of device models. - </tp:docstring> - </property> - <property name="Name" type="s" access="read"> - <tp:docstring> - Human readable name of the device. Must not be empty. - </tp:docstring> - </property> - <property name="Description" type="s" access="read"> - <tp:docstring> - Human readable description of the device. - </tp:docstring> - </property> - <property name="Location" type="s" access="read"> - <tp:docstring> - Location of the device. - </tp:docstring> - </property> - <property name="GCDBootstrapState" type="s" access="read"> - <tp:docstring> - Contains one of the following values describing the state of GCD - bootstrapping: - “disabled” - GCD registration has been disabled in the config file. - “offline” - GCD registration is unknown because the device is offline. - “connecting” - GCD registration is unknown because the device is still - connecting to the cloud. - “waiting” - Waiting to be configured with GCD credentials. - “registering” - Registering the device with the GCD servers. - “online” - Device is online and registered with GCD servers. - - Note: more values may be added later to this list. - - Clients that wish to present a single linear bootstrapping flow to users - may treat GCD bootstrapping states as a suffix to WiFi bootstrapping - states. If we have no cloud connectivity, we cannot possibly do GCD - registration/credential verification. - </tp:docstring> - </property> - <property name="PairingInfo" type="a{sv}" access="read"> - <tp:docstring> - Describes the state of device pairing. While no pairing attempt is in - progress, this dictionary will be empty. When a client initiates a - pairing transaction via /privet/v3/pairing/start, dictionary will - contain the following keys: - “sessionId” - ID of the pairing session; generated by device - “pairingMode” - Selected type of pairing from /privet/v3/pairing/start - (e.g. “pinCode” or “embeddedCode”) - “code” - The pin code or embedded code as appropriate to the - “pairingMode” value. See design document. - This value will be a string. - </tp:docstring> - </property> - </interface> -</node> diff --git a/buffet/dbus_bindings/dbus-service-config.json b/buffet/dbus_bindings/dbus-service-config.json deleted file mode 100644 index c5fa946..0000000 --- a/buffet/dbus_bindings/dbus-service-config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "service_name": "com.android.Weave", - "object_manager": { - "name": "com.android.Weave.ObjectManager", - "object_path": "/com/android/Weave" - } -} diff --git a/buffet/dbus_command_dispatcher.cc b/buffet/dbus_command_dispatcher.cc deleted file mode 100644 index d23d071..0000000 --- a/buffet/dbus_command_dispatcher.cc +++ /dev/null @@ -1,54 +0,0 @@ -// 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/dbus_command_dispatcher.h" - -#include <brillo/dbus/exported_object_manager.h> -#include <weave/command.h> -#include <weave/device.h> - -#include "buffet/dbus_command_proxy.h" -#include "buffet/dbus_constants.h" - -using brillo::dbus_utils::AsyncEventSequencer; -using brillo::dbus_utils::ExportedObjectManager; - -namespace buffet { - -DBusCommandDispacher::DBusCommandDispacher( - const base::WeakPtr<ExportedObjectManager>& object_manager, - weave::Device* device) - : object_manager_{object_manager} { - device->AddCommandHandler("", "", - base::Bind(&DBusCommandDispacher::OnCommandAdded, - weak_ptr_factory_.GetWeakPtr())); -} - -void DBusCommandDispacher::OnCommandAdded( - const std::weak_ptr<weave::Command>& cmd) { - auto command = cmd.lock(); - if (!object_manager_ || !command) - return; - std::unique_ptr<DBusCommandProxy> proxy{new DBusCommandProxy( - object_manager_.get(), object_manager_->GetBus(), command, - buffet::dbus_constants::kCommandServicePathPrefix + - std::to_string(++next_id_))}; - proxy->RegisterAsync(AsyncEventSequencer::GetDefaultCompletionAction()); - // DBusCommandProxy::DBusCommandProxy() subscribe itself to weave::Command - // notifications. When weave::Command is being destroyed it sends - // ::OnCommandDestroyed() and DBusCommandProxy deletes itself. - proxy.release(); -} - -} // namespace buffet diff --git a/buffet/dbus_command_dispatcher.h b/buffet/dbus_command_dispatcher.h deleted file mode 100644 index ea78520..0000000 --- a/buffet/dbus_command_dispatcher.h +++ /dev/null @@ -1,67 +0,0 @@ -// 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. - -#ifndef BUFFET_DBUS_COMMAND_DISPATCHER_H_ -#define BUFFET_DBUS_COMMAND_DISPATCHER_H_ - -#include <map> -#include <string> - -#include <base/macros.h> -#include <base/memory/weak_ptr.h> - -namespace weave { -class Command; -class Device; -} - -namespace brillo { -namespace dbus_utils { -class ExportedObjectManager; -} // namespace dbus_utils -} // namespace brillo - -namespace buffet { - -// Implements D-Bus dispatch of commands. When OnCommandAdded is called, -// DBusCommandDispacher creates an instance of DBusCommandProxy object and -// advertises it through ExportedObjectManager on D-Bus. Command handling -// processes can watch the new D-Bus object appear and communicate with it to -// update the command handling progress. Once command is handled, -// DBusCommandProxy::Done() is called and the command is removed from the -// command queue and D-Bus ExportedObjectManager. -class DBusCommandDispacher final { - public: - explicit DBusCommandDispacher( - const base::WeakPtr<brillo::dbus_utils::ExportedObjectManager>& - object_manager, - weave::Device* device); - - private: - void OnCommandAdded(const std::weak_ptr<weave::Command>& cmd); - - base::WeakPtr<brillo::dbus_utils::ExportedObjectManager> object_manager_; - int next_id_{0}; - - // Default constructor is used in special circumstances such as for testing. - DBusCommandDispacher() = default; - - base::WeakPtrFactory<DBusCommandDispacher> weak_ptr_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(DBusCommandDispacher); -}; - -} // namespace buffet - -#endif // BUFFET_DBUS_COMMAND_DISPATCHER_H_ diff --git a/buffet/dbus_command_proxy.cc b/buffet/dbus_command_proxy.cc deleted file mode 100644 index 011ba01..0000000 --- a/buffet/dbus_command_proxy.cc +++ /dev/null @@ -1,160 +0,0 @@ -// 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/dbus_command_proxy.h" - -#include <brillo/dbus/async_event_sequencer.h> -#include <brillo/dbus/exported_object_manager.h> -#include <weave/enum_to_string.h> - -#include "buffet/dbus_conversion.h" -#include "buffet/weave_error_conversion.h" - -using brillo::dbus_utils::AsyncEventSequencer; -using brillo::dbus_utils::ExportedObjectManager; - -namespace errors { -namespace commands { -const char kDomain[] = "weaved"; -const char kCommandDestroyed[] = "command_destroyed"; -} // namespace commands -} // namespace errors - -namespace buffet { - -namespace { - -bool ReportDestroyedError(brillo::ErrorPtr* error) { - brillo::Error::AddTo(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kCommandDestroyed, - "Command has been destroyed"); - return false; -} - -} // anonymous namespace - -DBusCommandProxy::DBusCommandProxy(ExportedObjectManager* object_manager, - const scoped_refptr<dbus::Bus>& bus, - const std::weak_ptr<weave::Command>& command, - std::string object_path) - : command_{command}, - dbus_object_{object_manager, bus, dbus::ObjectPath{object_path}} {} - -void DBusCommandProxy::RegisterAsync( - const AsyncEventSequencer::CompletionAction& completion_callback) { - auto command = command_.lock(); - if (!command) - return; - - dbus_adaptor_.RegisterWithDBusObject(&dbus_object_); - - // Set the initial property values before registering the DBus object. - dbus_adaptor_.SetName(command->GetName()); - dbus_adaptor_.SetId(command->GetID()); - dbus_adaptor_.SetComponent(command->GetComponent()); - dbus_adaptor_.SetState(EnumToString(command->GetState())); - dbus_adaptor_.SetProgress( - DictionaryToDBusVariantDictionary(command->GetProgress())); - dbus_adaptor_.SetOrigin(EnumToString(command->GetOrigin())); - dbus_adaptor_.SetParameters( - DictionaryToDBusVariantDictionary(command->GetParameters())); - dbus_adaptor_.SetResults( - DictionaryToDBusVariantDictionary(command->GetResults())); - - // Register the command DBus object and expose its methods and properties. - dbus_object_.RegisterAsync(completion_callback); -} - -bool DBusCommandProxy::SetProgress( - brillo::ErrorPtr* error, - const brillo::VariantDictionary& progress) { - auto command = command_.lock(); - if (!command) - return ReportDestroyedError(error); - - LOG(INFO) << "Received call to Command<" << command->GetName() - << ">::SetProgress()"; - auto dictionary = DictionaryFromDBusVariantDictionary(progress, error); - if (!dictionary) - return false; - weave::ErrorPtr weave_error; - if (!command->SetProgress(*dictionary, &weave_error)) { - ConvertError(*weave_error, error); - return false; - } - dbus_adaptor_.SetProgress( - DictionaryToDBusVariantDictionary(command->GetProgress())); - dbus_adaptor_.SetState(EnumToString(command->GetState())); - return true; -} - -bool DBusCommandProxy::Complete(brillo::ErrorPtr* error, - const brillo::VariantDictionary& results) { - auto command = command_.lock(); - if (!command) - return ReportDestroyedError(error); - - LOG(INFO) << "Received call to Command<" << command->GetName() - << ">::Complete()"; - auto dictionary = DictionaryFromDBusVariantDictionary(results, error); - if (!dictionary) - return false; - weave::ErrorPtr weave_error; - if (!command->Complete(*dictionary, &weave_error)) { - ConvertError(*weave_error, error); - return false; - } - dbus_adaptor_.SetResults( - DictionaryToDBusVariantDictionary(command->GetResults())); - dbus_adaptor_.SetState(EnumToString(command->GetState())); - return true; -} - -bool DBusCommandProxy::Abort(brillo::ErrorPtr* error, - const std::string& code, - const std::string& message) { - auto command = command_.lock(); - if (!command) - return ReportDestroyedError(error); - - LOG(INFO) << "Received call to Command<" << command->GetName() - << ">::Abort()"; - weave::ErrorPtr cmd_error; - weave::Error::AddTo(&cmd_error, FROM_HERE, "command_error", code, message); - weave::ErrorPtr weave_error; - if (!command->Abort(cmd_error.get(), &weave_error)) { - ConvertError(*weave_error, error); - return false; - } - dbus_adaptor_.SetState(EnumToString(command->GetState())); - return true; -} - -bool DBusCommandProxy::Cancel(brillo::ErrorPtr* error) { - auto command = command_.lock(); - if (!command) - return ReportDestroyedError(error); - - LOG(INFO) << "Received call to Command<" << command->GetName() - << ">::Cancel()"; - weave::ErrorPtr weave_error; - if (!command->Cancel(&weave_error)) { - ConvertError(*weave_error, error); - return false; - } - dbus_adaptor_.SetState(EnumToString(command->GetState())); - return true; -} - -} // namespace buffet diff --git a/buffet/dbus_command_proxy.h b/buffet/dbus_command_proxy.h deleted file mode 100644 index 759c74a..0000000 --- a/buffet/dbus_command_proxy.h +++ /dev/null @@ -1,69 +0,0 @@ -// 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. - -#ifndef BUFFET_DBUS_COMMAND_PROXY_H_ -#define BUFFET_DBUS_COMMAND_PROXY_H_ - -#include <string> - -#include <base/macros.h> -#include <base/scoped_observer.h> -#include <brillo/dbus/data_serialization.h> -#include <brillo/dbus/dbus_object.h> -#include <weave/command.h> - -#include "buffet/dbus_bindings/com.android.Weave.Command.h" - -namespace brillo { -namespace dbus_utils { -class ExportedObjectManager; -} // namespace dbus_utils -} // namespace brillo - -namespace buffet { - -class DBusCommandProxy : public com::android::Weave::CommandInterface { - public: - DBusCommandProxy(brillo::dbus_utils::ExportedObjectManager* object_manager, - const scoped_refptr<dbus::Bus>& bus, - const std::weak_ptr<weave::Command>& command, - std::string object_path); - ~DBusCommandProxy() override = default; - - void RegisterAsync( - const brillo::dbus_utils::AsyncEventSequencer::CompletionAction& - completion_callback); - - private: - bool SetProgress(brillo::ErrorPtr* error, - const brillo::VariantDictionary& progress) override; - bool Complete(brillo::ErrorPtr* error, - const brillo::VariantDictionary& results) override; - bool Abort(brillo::ErrorPtr* error, - const std::string& code, - const std::string& message) override; - bool Cancel(brillo::ErrorPtr* error) override; - - std::weak_ptr<weave::Command> command_; - com::android::Weave::CommandAdaptor dbus_adaptor_{this}; - brillo::dbus_utils::DBusObject dbus_object_; - - friend class DBusCommandProxyTest; - friend class DBusCommandDispacherTest; - DISALLOW_COPY_AND_ASSIGN(DBusCommandProxy); -}; - -} // namespace buffet - -#endif // BUFFET_DBUS_COMMAND_PROXY_H_ diff --git a/buffet/dbus_command_proxy_unittest.cc b/buffet/dbus_command_proxy_unittest.cc deleted file mode 100644 index 5225f11..0000000 --- a/buffet/dbus_command_proxy_unittest.cc +++ /dev/null @@ -1,189 +0,0 @@ -// 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/dbus_command_proxy.h" - -#include <functional> -#include <memory> -#include <vector> - -#include <dbus/mock_bus.h> -#include <dbus/mock_exported_object.h> -#include <dbus/property.h> -#include <brillo/dbus/dbus_object.h> -#include <brillo/dbus/dbus_object_test_helpers.h> -#include <gtest/gtest.h> -#include <weave/command.h> -#include <weave/enum_to_string.h> -#include <weave/test/mock_command.h> -#include <weave/test/unittest_utils.h> - -#include "buffet/dbus_constants.h" - -namespace buffet { - -using ::testing::_; -using ::testing::AnyNumber; -using ::testing::Return; -using ::testing::ReturnRef; -using ::testing::ReturnRefOfCopy; -using ::testing::StrictMock; - -using brillo::VariantDictionary; -using brillo::dbus_utils::AsyncEventSequencer; -using weave::test::CreateDictionaryValue; -using weave::test::IsEqualValue; - -namespace { - -const char kTestCommandId[] = "cmd_1"; - -MATCHER_P(EqualToJson, json, "") { - auto json_value = CreateDictionaryValue(json); - return IsEqualValue(*json_value, arg); -} - -MATCHER_P2(ExpectError, code, message, "") { - return arg->GetCode() == code && arg->GetMessage() == message; -} - -} // namespace - -class DBusCommandProxyTest : public ::testing::Test { - public: - void SetUp() override { - command_ = std::make_shared<StrictMock<weave::test::MockCommand>>(); - // Set up a mock DBus bus object. - dbus::Bus::Options options; - options.bus_type = dbus::Bus::SYSTEM; - bus_ = new dbus::MockBus(options); - // By default, don't worry about threading assertions. - EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber()); - EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber()); - - expected_result_dict_.SetInteger("height", 53); - expected_result_dict_.SetString("_jumpType", "_withKick"); - EXPECT_CALL(*command_, GetID()) - .WillOnce(ReturnRefOfCopy<std::string>(kTestCommandId)); - // Use WillRepeatedly because GetName is used for logging. - EXPECT_CALL(*command_, GetName()) - .WillRepeatedly(ReturnRefOfCopy<std::string>("robot.jump")); - EXPECT_CALL(*command_, GetComponent()) - .WillRepeatedly(ReturnRefOfCopy<std::string>("myComponent")); - EXPECT_CALL(*command_, GetState()) - .WillRepeatedly(Return(weave::Command::State::kQueued)); - EXPECT_CALL(*command_, GetOrigin()) - .WillOnce(Return(weave::Command::Origin::kLocal)); - EXPECT_CALL(*command_, GetParameters()) - .WillOnce(ReturnRef(expected_result_dict_)); - EXPECT_CALL(*command_, GetProgress()) - .WillRepeatedly(ReturnRef(empty_dict_)); - EXPECT_CALL(*command_, GetResults()) - .WillRepeatedly(ReturnRef(empty_dict_)); - - // Set up a mock ExportedObject to be used with the DBus command proxy. - std::string cmd_path = buffet::dbus_constants::kCommandServicePathPrefix; - cmd_path += kTestCommandId; - const dbus::ObjectPath kCmdObjPath(cmd_path); - // Use a mock exported object for the exported object manager. - mock_exported_object_command_ = - new dbus::MockExportedObject(bus_.get(), kCmdObjPath); - EXPECT_CALL(*bus_, GetExportedObject(kCmdObjPath)) - .Times(AnyNumber()) - .WillRepeatedly(Return(mock_exported_object_command_.get())); - EXPECT_CALL(*mock_exported_object_command_, ExportMethod(_, _, _, _)) - .Times(AnyNumber()); - - proxy_.reset(new DBusCommandProxy{ - nullptr, bus_, std::weak_ptr<weave::Command>{command_}, cmd_path}); - GetCommandProxy()->RegisterAsync( - AsyncEventSequencer::GetDefaultCompletionAction()); - } - - void TearDown() override { - EXPECT_CALL(*mock_exported_object_command_, Unregister()).Times(1); - bus_ = nullptr; - } - - DBusCommandProxy* GetCommandProxy() const { return proxy_.get(); } - - com::android::Weave::CommandAdaptor* GetCommandAdaptor() const { - return &GetCommandProxy()->dbus_adaptor_; - } - - com::android::Weave::CommandInterface* GetCommandInterface() const { - // DBusCommandProxy also implements CommandInterface. - return GetCommandProxy(); - } - - weave::Command::State GetCommandState() const { - weave::Command::State state; - EXPECT_TRUE(StringToEnum(GetCommandAdaptor()->GetState(), &state)); - return state; - } - - scoped_refptr<dbus::MockExportedObject> mock_exported_object_command_; - scoped_refptr<dbus::MockBus> bus_; - base::DictionaryValue empty_dict_; - base::DictionaryValue expected_result_dict_; - - std::shared_ptr<StrictMock<weave::test::MockCommand>> command_; - std::unique_ptr<DBusCommandProxy> proxy_; -}; - -TEST_F(DBusCommandProxyTest, Init) { - VariantDictionary params = { - {"height", int32_t{53}}, {"_jumpType", std::string{"_withKick"}}, - }; - EXPECT_EQ(weave::Command::State::kQueued, GetCommandState()); - EXPECT_EQ(params, GetCommandAdaptor()->GetParameters()); - EXPECT_EQ(VariantDictionary{}, GetCommandAdaptor()->GetProgress()); - EXPECT_EQ(VariantDictionary{}, GetCommandAdaptor()->GetResults()); - EXPECT_EQ("robot.jump", GetCommandAdaptor()->GetName()); - EXPECT_EQ("myComponent", GetCommandAdaptor()->GetComponent()); - EXPECT_EQ(kTestCommandId, GetCommandAdaptor()->GetId()); -} - -TEST_F(DBusCommandProxyTest, SetProgress) { - EXPECT_CALL(*command_, SetProgress(EqualToJson("{'progress': 10}"), _)) - .WillOnce(Return(true)); - EXPECT_TRUE( - GetCommandInterface()->SetProgress(nullptr, {{"progress", int32_t{10}}})); -} - -TEST_F(DBusCommandProxyTest, Complete) { - EXPECT_CALL( - *command_, - Complete( - EqualToJson("{'foo': 42, 'bar': 'foobar', 'resultList': [1, 2, 3]}"), - _)) - .WillOnce(Return(true)); - EXPECT_TRUE(GetCommandInterface()->Complete( - nullptr, VariantDictionary{{"foo", int32_t{42}}, - {"bar", std::string{"foobar"}}, - {"resultList", std::vector<int>{1, 2, 3}}})); -} - -TEST_F(DBusCommandProxyTest, Abort) { - EXPECT_CALL(*command_, Abort(ExpectError("foo", "bar"), _)) - .WillOnce(Return(true)); - EXPECT_TRUE(GetCommandInterface()->Abort(nullptr, "foo", "bar")); -} - -TEST_F(DBusCommandProxyTest, Cancel) { - EXPECT_CALL(*command_, Cancel(_)).WillOnce(Return(true)); - EXPECT_TRUE(GetCommandInterface()->Cancel(nullptr)); -} - -} // namespace buffet diff --git a/buffet/dbus_constants.cc b/buffet/dbus_constants.cc index 2a8b884..61eaf5c 100644 --- a/buffet/dbus_constants.cc +++ b/buffet/dbus_constants.cc @@ -20,43 +20,6 @@ namespace dbus_constants { const char kServiceName[] = "com.android.Weave"; const char kRootServicePath[] = "/com/android/Weave"; -const char kCommandServicePathPrefix[] = "/com/android/Weave/commands/"; - -namespace avahi { - -const char kServiceName[] = "org.freedesktop.Avahi"; - -const char kServerInterface[] = "org.freedesktop.Avahi.Server"; -const char kServerPath[] = "/"; -const char kServerMethodEntryGroupNew[] = "EntryGroupNew"; -const char kServerMethodServiceBrowserNew[] = "ServiceBrowserNew"; -const char kServerMethodServiceResolverNew[] = "ServiceResolverNew"; -const char kServerMethodGetHostName[] = "GetHostName"; -const char kServerMethodGetState[] = "GetState"; -const char kServerSignalStateChanged[] = "StateChanged"; - -const char kGroupInterface[] = "org.freedesktop.Avahi.EntryGroup"; -const char kGroupMethodAddRecord[] = "AddRecord"; -const char kGroupMethodAddService[] = "AddService"; -const char kGroupMethodCommit[] = "Commit"; -const char kGroupMethodFree[] = "Free"; -const char kGroupMethodUpdateServiceTxt[] = "UpdateServiceTxt"; -const char kGroupMethodReset[]= "Reset"; -const char kGroupSignalStateChanged[] = "StateChanged"; - -const char kServiceBrowserInterface[] = "org.freedesktop.Avahi.ServiceBrowser"; -const char kServiceBrowserMethodFree[] = "Free"; -const char kServiceBrowserSignalItemNew[] = "ItemNew"; -const char kServiceBrowserSignalItemRemove[] = "ItemRemove"; -const char kServiceBrowserSignalFailure[] = "Failure"; - -const char kServiceResolverInterface[] = - "org.freedesktop.Avahi.ServiceResolver"; -const char kServiceResolverMethodFree[] = "Free"; -const char kServiceResolverSignalFound[] = "Found"; -const char kServiceResolverSignalFailure[] = "Failure"; - -} // namespace avahi } // namespace dbus constants diff --git a/buffet/dbus_constants.h b/buffet/dbus_constants.h index bc74dac..ae91891 100644 --- a/buffet/dbus_constants.h +++ b/buffet/dbus_constants.h @@ -25,44 +25,6 @@ extern const char kServiceName[]; // The object at this path implements the ObjectManager interface. extern const char kRootServicePath[]; -// D-Bus object path prefix for Command objects. -extern const char kCommandServicePathPrefix[]; - -namespace avahi { - -extern const char kServiceName[]; - -extern const char kServerInterface[]; -extern const char kServerPath[]; -extern const char kServerMethodEntryGroupNew[]; -extern const char kServerMethodServiceBrowserNew[]; -extern const char kServerMethodServiceResolverNew[]; -extern const char kServerMethodGetHostName[]; -extern const char kServerMethodGetState[]; -extern const char kServerSignalStateChanged[]; - -extern const char kGroupInterface[]; -extern const char kGroupMethodAddRecord[]; -extern const char kGroupMethodAddService[]; -extern const char kGroupMethodCommit[]; -extern const char kGroupMethodFree[]; -extern const char kGroupMethodReset[]; -extern const char kGroupSignalStateChanged[]; - -extern const char kServiceBrowserInterface[]; -extern const char kServiceBrowserMethodFree[]; -extern const char kServiceBrowserSignalItemNew[]; -extern const char kServiceBrowserSignalItemRemove[]; -extern const char kServiceBrowserSignalFailure[]; - -extern const char kServiceResolverInterface[]; -extern const char kServiceResolverMethodFree[]; -extern const char kGroupMethodUpdateServiceTxt[]; -extern const char kServiceResolverSignalFound[]; -extern const char kServiceResolverSignalFailure[]; - -} // namespace avahi - } // namespace dbus_constants } // namespace buffet diff --git a/buffet/etc/dbus-1/com.android.Weave.conf b/buffet/etc/dbus-1/com.android.Weave.conf deleted file mode 100644 index c9f77e0..0000000 --- a/buffet/etc/dbus-1/com.android.Weave.conf +++ /dev/null @@ -1,21 +0,0 @@ -<!DOCTYPE busconfig PUBLIC - "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" - "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> -<busconfig> - <policy user="root"> - <allow send_destination="com.android.Weave" /> - </policy> - - <policy group="weaved"> - <allow send_destination="com.android.Weave" /> - </policy> - - <policy user="weaved"> - <allow own="com.android.Weave" /> - </policy> - - <policy user="webservd"> - <allow send_destination="com.android.Weave" - send_interface="com.chromium.WebServer.RequestHandler"/> - </policy> -</busconfig> diff --git a/buffet/etc/init/buffet.conf b/buffet/etc/init/buffet.conf deleted file mode 100644 index 7feef4a..0000000 --- a/buffet/etc/init/buffet.conf +++ /dev/null @@ -1,54 +0,0 @@ -# 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. - -description "Brillo Buffet Service" -author "chromium-os-dev@chromium.org" - -start on starting system-services -stop on stopping system-services -respawn - -env BUFFET_LOG_LEVEL=0 -env BUFFET_ENABLE_XMPP= -env BUFFET_STATE_PATH= -env BUFFET_CONFIG_PATH= -env BUFFET_ENABLE_PING=false -env BUFFET_DEVICE_WHITELIST= -env BUFFET_DISABLE_PRIVET=false -env BUFFET_TEST_DEFINITIONS_PATH= -env BUFFET_DISABLE_SECURITY=false -env BUFFET_TEST_PRIVET_SSID= - -pre-start script - mkdir -m 0755 -p /var/lib/buffet - chown -R buffet:buffet /var/lib/buffet -end script - -# Minijail actually forks off our desired process. -expect fork - -exec minijail0 -i -g buffet -u buffet /usr/bin/buffet \ - --v="${BUFFET_LOG_LEVEL}" \ - --config_path="${BUFFET_CONFIG_PATH}" \ - --state_path="${BUFFET_STATE_PATH}" \ - --disable_security="${BUFFET_DISABLE_SECURITY}" \ - --enable_ping="${BUFFET_ENABLE_PING}" \ - --device_whitelist="${BUFFET_DEVICE_WHITELIST}" \ - --disable_privet="${BUFFET_DISABLE_PRIVET}" \ - --test_definitions_path="${BUFFET_TEST_DEFINITIONS_PATH}" \ - --enable_xmpp="${BUFFET_ENABLE_XMPP}" \ - --test_privet_ssid="${BUFFET_TEST_PRIVET_SSID}" - -# Wait for daemon to claim its D-Bus name before transitioning to started. -post-start exec gdbus wait --system --timeout 30 org.chromium.Buffet diff --git a/buffet/main.cc b/buffet/main.cc index 58e24da..04a5134 100644 --- a/buffet/main.cc +++ b/buffet/main.cc @@ -15,8 +15,11 @@ #include <string> #include <signal.h> +#include <sysexits.h> #include <base/files/file_path.h> +#include <binderwrapper/binder_wrapper.h> +#include <brillo/binder_watcher.h> #include <brillo/daemons/dbus_daemon.h> #include <brillo/dbus/async_event_sequencer.h> #include <brillo/dbus/exported_object_manager.h> @@ -27,6 +30,7 @@ #include "buffet/buffet_config.h" #include "buffet/dbus_constants.h" #include "buffet/manager.h" +#include "common/binder_constants.h" using brillo::dbus_utils::AsyncEventSequencer; using brillo::DBusServiceDaemon; @@ -41,8 +45,19 @@ class Daemon final : public DBusServiceDaemon { : DBusServiceDaemon(kServiceName, kRootServicePath), options_{options} {} protected: + int OnInit() override { + android::BinderWrapper::Create(); + if (!binder_watcher_.Init()) + return EX_OSERR; + + return brillo::DBusServiceDaemon::OnInit(); + } + void RegisterDBusObjectsAsync(AsyncEventSequencer* sequencer) override { - manager_.reset(new Manager(options_, object_manager_->AsWeakPtr())); + manager_ = new Manager{options_, bus_}; + android::BinderWrapper::Get()->RegisterService( + weaved::binder::kWeaveServiceName, + android::IInterface::asBinder(manager_)); manager_->Start(sequencer); } @@ -50,8 +65,9 @@ class Daemon final : public DBusServiceDaemon { private: Manager::Options options_; + brillo::BinderWatcher binder_watcher_; + android::sp<buffet::Manager> manager_; - std::unique_ptr<buffet::Manager> manager_; DISALLOW_COPY_AND_ASSIGN(Daemon); }; diff --git a/buffet/manager.cc b/buffet/manager.cc index 2e53c62..27610ec 100644 --- a/buffet/manager.cc +++ b/buffet/manager.cc @@ -26,10 +26,9 @@ #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/dbus/async_event_sequencer.h> -#include <brillo/dbus/exported_object_manager.h> #include <brillo/errors/error.h> #include <brillo/http/http_transport.h> #include <brillo/http/http_utils.h> @@ -44,26 +43,21 @@ #include "brillo/weaved_system_properties.h" #include "buffet/bluetooth_client.h" #include "buffet/buffet_config.h" -#include "buffet/dbus_command_dispatcher.h" -#include "buffet/dbus_conversion.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 brillo::dbus_utils::DBusMethodResponse; -using brillo::dbus_utils::ExportedObjectManager; +using NotificationListener = + android::weave::IWeaveServiceManagerNotificationListener; namespace buffet { namespace { -const char kPairingSessionIdKey[] = "sessionId"; -const char kPairingModeKey[] = "mode"; -const char kPairingCodeKey[] = "code"; - const char kErrorDomain[] = "buffet"; const char kFileReadError[] = "file_read_error"; @@ -152,6 +146,20 @@ void LoadStateDefaults(const BuffetConfig::Options& options, } } +// 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 { @@ -164,21 +172,23 @@ class Manager::TaskRunner : public weave::provider::TaskRunner { }; Manager::Manager(const Options& options, - const base::WeakPtr<ExportedObjectManager>& object_manager) - : options_{options}, - dbus_object_(object_manager.get(), - object_manager->GetBus(), - com::android::Weave::ManagerAdaptor::GetObjectPath()) {} + 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) { RestartWeave(sequencer); - - dbus_adaptor_.RegisterWithDBusObject(&dbus_object_); - dbus_registration_handler_ = - sequencer->GetHandler("Manager.RegisterAsync() failed.", true); } void Manager::RestartWeave(AsyncEventSequencer* sequencer) { @@ -187,7 +197,7 @@ void Manager::RestartWeave(AsyncEventSequencer* sequencer) { task_runner_.reset(new TaskRunner{}); config_.reset(new BuffetConfig{options_.config_options}); http_client_.reset(new HttpTransportClient); - shill_client_.reset(new ShillClient{dbus_object_.GetBus(), + shill_client_.reset(new ShillClient{bus_, options_.device_whitelist, !options_.xmpp_enabled}); weave::provider::HttpServer* http_server{nullptr}; @@ -195,7 +205,7 @@ void Manager::RestartWeave(AsyncEventSequencer* sequencer) { if (!options_.disable_privet) { mdns_client_ = MdnsClient::CreateInstance(); web_serv_client_.reset(new WebServClient{ - dbus_object_.GetBus(), sequencer, + bus_, sequencer, base::Bind(&Manager::CreateDevice, weak_ptr_factory_.GetWeakPtr())}); bluetooth_client_ = BluetoothClient::CreateInstance(); http_server = web_serv_client_.get(); @@ -233,9 +243,6 @@ void Manager::CreateDevice() { device_->AddSettingsChangedCallback( base::Bind(&Manager::OnConfigChanged, weak_ptr_factory_.GetWeakPtr())); - command_dispatcher_.reset( - new DBusCommandDispacher{dbus_object_.GetObjectManager(), device_.get()}); - device_->AddTraitDefsChangedCallback( base::Bind(&Manager::OnTraitDefsChanged, weak_ptr_factory_.GetWeakPtr())); @@ -253,15 +260,10 @@ void Manager::CreateDevice() { base::Bind(&Manager::OnPairingStart, weak_ptr_factory_.GetWeakPtr()), base::Bind(&Manager::OnPairingEnd, weak_ptr_factory_.GetWeakPtr())); - auto handler = dbus_registration_handler_; - if (handler.is_null()) - handler = AsyncEventSequencer::GetDefaultCompletionAction(); - dbus_object_.RegisterAsync(handler); - dbus_registration_handler_.Reset(); + CreateServicesForClients(); } void Manager::Stop() { - command_dispatcher_.reset(); device_.reset(); #ifdef BUFFET_USE_WIFI_BOOTSTRAPPING web_serv_client_.reset(); @@ -273,139 +275,195 @@ void Manager::Stop() { task_runner_.reset(); } -void Manager::RegisterDevice(DBusMethodResponsePtr<std::string> response, - const std::string& ticket_id) { - LOG(INFO) << "Received call to Manager.RegisterDevice()"; - - device_->Register(ticket_id, base::Bind(&Manager::RegisterDeviceDone, - weak_ptr_factory_.GetWeakPtr(), - base::Passed(&response))); -} - -void Manager::RegisterDeviceDone(DBusMethodResponsePtr<std::string> response, - weave::ErrorPtr error) { - if (error) { - brillo::ErrorPtr brillo_error; - ConvertError(*error, &brillo_error); - return response->ReplyWithError(brillo_error.get()); - } - LOG(INFO) << "Device registered: " << device_->GetSettings().cloud_id; - response->Return(device_->GetSettings().cloud_id); -} - -void Manager::AddComponent(DBusMethodResponsePtr<> response, - const std::string& name, - const std::vector<std::string>& traits) { - brillo::ErrorPtr brillo_error; - weave::ErrorPtr error; - if (!device_->AddComponent(name, traits, &error)) { - ConvertError(*error, &brillo_error); - return response->ReplyWithError(brillo_error.get()); - } - response->Return(); -} - -void Manager::UpdateState(DBusMethodResponsePtr<> response, - const std::string& component, - const brillo::VariantDictionary& property_set) { - brillo::ErrorPtr brillo_error; - auto properties = - DictionaryFromDBusVariantDictionary(property_set, &brillo_error); - if (!properties) - return response->ReplyWithError(brillo_error.get()); - - weave::ErrorPtr error; - if (!device_->SetStateProperties(component, *properties, &error)) { - ConvertError(*error, &brillo_error); - return response->ReplyWithError(brillo_error.get()); - } - response->Return(); -} - -void Manager::AddCommand(DBusMethodResponsePtr<std::string> response, - const std::string& json_command) { - std::string error_message; - std::unique_ptr<base::Value> value( - base::JSONReader::ReadAndReturnError(json_command, base::JSON_PARSE_RFC, - nullptr, &error_message) - .release()); - const base::DictionaryValue* command{nullptr}; - if (!value || !value->GetAsDictionary(&command)) { - return response->ReplyWithError(FROM_HERE, brillo::errors::json::kDomain, - brillo::errors::json::kParseError, - error_message); - } - - std::string id; - weave::ErrorPtr error; - if (!device_->AddCommand(*command, &id, &error)) { - brillo::ErrorPtr brillo_error; - ConvertError(*error, &brillo_error); - return response->ReplyWithError(brillo_error.get()); - } - - response->Return(id); -} - -std::string Manager::TestMethod(const std::string& message) { - LOG(INFO) << "Received call to test method: " << message; - return message; -} - void Manager::OnTraitDefsChanged() { - const base::DictionaryValue& state = device_->GetTraits(); - std::string json; - base::JSONWriter::WriteWithOptions( - state, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json); - dbus_adaptor_.SetTraits(json); + NotifyServiceManagerChange({NotificationListener::TRAITS}); } void Manager::OnComponentTreeChanged() { - const base::DictionaryValue& state = device_->GetComponents(); - std::string json; - base::JSONWriter::WriteWithOptions( - state, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json); - dbus_adaptor_.SetComponents(json); + NotifyServiceManagerChange({NotificationListener::COMPONENTS}); } void Manager::OnGcdStateChanged(weave::GcdState state) { - std::string state_string = weave::EnumToString(state); - dbus_adaptor_.SetStatus(state_string); - property_set(weaved::system_properties::kState, state_string.c_str()); + state_ = weave::EnumToString(state); + NotifyServiceManagerChange({NotificationListener::STATE}); + property_set(weaved::system_properties::kState, state_.c_str()); } void Manager::OnConfigChanged(const weave::Settings& settings) { - dbus_adaptor_.SetDeviceId(settings.cloud_id); - dbus_adaptor_.SetOemName(settings.oem_name); - dbus_adaptor_.SetModelName(settings.model_name); - dbus_adaptor_.SetModelId(settings.model_id); - dbus_adaptor_.SetName(settings.name); - dbus_adaptor_.SetDescription(settings.description); - dbus_adaptor_.SetLocation(settings.location); + 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. - dbus_adaptor_.SetPairingInfo(brillo::VariantDictionary{ - {kPairingSessionIdKey, session_id}, - {kPairingModeKey, weave::EnumToString(pairing_type)}, - {kPairingCodeKey, 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) { - auto exposed_pairing_attempt = dbus_adaptor_.GetPairingInfo(); - auto it = exposed_pairing_attempt.find(kPairingSessionIdKey); - if (it == exposed_pairing_attempt.end()) { + 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); +} + +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)); } - std::string exposed_session{it->second.TryGet<std::string>()}; - if (exposed_session == session_id) { - dbus_adaptor_.SetPairingInfo(brillo::VariantDictionary{}); - } +} + +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 diff --git a/buffet/manager.h b/buffet/manager.h index 9522340..18e1aea 100644 --- a/buffet/manager.h +++ b/buffet/manager.h @@ -24,42 +24,26 @@ #include <base/macros.h> #include <base/memory/weak_ptr.h> #include <base/values.h> -#include <brillo/dbus/data_serialization.h> -#include <brillo/dbus/dbus_object.h> -#include <brillo/dbus/exported_property_set.h> +#include <brillo/dbus/async_event_sequencer.h> #include <brillo/errors/error.h> #include <weave/device.h> +#include "android/weave/BnWeaveServiceManager.h" +#include "buffet/binder_weave_service.h" #include "buffet/buffet_config.h" -#include "buffet/dbus_bindings/com.android.Weave.Manager.h" - -namespace brillo { -namespace dbus_utils { -class ExportedObjectManager; -} // namespace dbus_utils -} // namespace chromeos namespace buffet { class BluetoothClient; -class DBusCommandDispacher; class HttpTransportClient; class MdnsClient; class ShillClient; class WebServClient; -template<typename... Types> -using DBusMethodResponsePtr = - std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<Types...>>; - -template<typename... Types> -using DBusMethodResponse = - brillo::dbus_utils::DBusMethodResponse<Types...>; - // The Manager is responsible for global state of Buffet. It exposes // interfaces which affect the entire device such as device registration and // device state. -class Manager final : public com::android::Weave::ManagerInterface { +class Manager final : public android::weave::BnWeaveServiceManager { public: struct Options { bool xmpp_enabled = true; @@ -70,35 +54,39 @@ class Manager final : public com::android::Weave::ManagerInterface { BuffetConfig::Options config_options; }; - explicit Manager( - const Options& options, - const base::WeakPtr<brillo::dbus_utils::ExportedObjectManager>& - object_manager); - ~Manager(); + Manager(const Options& options, const scoped_refptr<dbus::Bus>& bus); + ~Manager() override; void Start(brillo::dbus_utils::AsyncEventSequencer* sequencer); - void Stop(); private: void RestartWeave(brillo::dbus_utils::AsyncEventSequencer* sequencer); void CreateDevice(); - // DBus methods: - void RegisterDevice(DBusMethodResponsePtr<std::string> response, - const std::string& ticket_id) override; - void AddComponent(DBusMethodResponsePtr<> response, - const std::string& name, - const std::vector<std::string>& traits) override; - void UpdateState(DBusMethodResponsePtr<> response, - const std::string& component, - const brillo::VariantDictionary& property_set) override; - void AddCommand(DBusMethodResponsePtr<std::string> response, - const std::string& json_command) override; - std::string TestMethod(const std::string& message) override; - - void StartPrivet(const Options& options, - brillo::dbus_utils::AsyncEventSequencer* sequencer); + // Binder methods for IWeaveServiceManager: + using WeaveServiceManagerNotificationListener = + android::sp<android::weave::IWeaveServiceManagerNotificationListener>; + android::binder::Status connect( + const android::sp<android::weave::IWeaveClient>& client) override; + android::binder::Status registerNotificationListener( + const WeaveServiceManagerNotificationListener& listener) override; + android::binder::Status getDeviceId(android::String16* id) override; + android::binder::Status getCloudId(android::String16* id) override; + android::binder::Status getDeviceName(android::String16* name) override; + android::binder::Status getDeviceDescription( + android::String16* description) override; + android::binder::Status getDeviceLocation( + android::String16* location) override; + android::binder::Status getOemName(android::String16* name) override; + android::binder::Status getModelName(android::String16* name) override; + android::binder::Status getModelId(android::String16* id) override; + android::binder::Status getPairingSessionId(android::String16* id) override; + android::binder::Status getPairingMode(android::String16* mode) override; + android::binder::Status getPairingCode(android::String16* code) override; + android::binder::Status getState(android::String16* state) override; + android::binder::Status getTraits(android::String16* traits) override; + android::binder::Status getComponents(android::String16* components) override; void OnTraitDefsChanged(); void OnComponentTreeChanged(); @@ -109,13 +97,15 @@ class Manager final : public com::android::Weave::ManagerInterface { const std::vector<uint8_t>& code); void OnPairingEnd(const std::string& session_id); - void RegisterDeviceDone(DBusMethodResponsePtr<std::string> response, - weave::ErrorPtr error); + void CreateServicesForClients(); + void OnClientDisconnected( + const android::sp<android::weave::IWeaveClient>& client); + void OnNotificationListenerDestroyed( + const WeaveServiceManagerNotificationListener& notification_listener); + void NotifyServiceManagerChange(const std::vector<int>& notification_ids); Options options_; - - com::android::Weave::ManagerAdaptor dbus_adaptor_{this}; - brillo::dbus_utils::DBusObject dbus_object_; + scoped_refptr<dbus::Bus> bus_; class TaskRunner; std::unique_ptr<TaskRunner> task_runner_; @@ -126,8 +116,25 @@ class Manager final : public com::android::Weave::ManagerInterface { std::unique_ptr<MdnsClient> mdns_client_; std::unique_ptr<WebServClient> web_serv_client_; std::unique_ptr<weave::Device> device_; - std::unique_ptr<DBusCommandDispacher> command_dispatcher_; - brillo::dbus_utils::AsyncEventSequencer::Handler dbus_registration_handler_; + + std::vector<android::sp<android::weave::IWeaveClient>> pending_clients_; + std::map<android::sp<android::weave::IWeaveClient>, + android::sp<BinderWeaveService>> services_; + std::set<WeaveServiceManagerNotificationListener> notification_listeners_; + + // State properties. + std::string cloud_id_; + std::string device_id_; + std::string device_name_; + std::string device_description_; + std::string device_location_; + std::string oem_name_; + std::string model_name_; + std::string model_id_; + std::string pairing_session_id_; + std::string pairing_mode_; + std::string pairing_code_; + std::string state_; base::WeakPtrFactory<Manager> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(Manager); diff --git a/common/binder_constants.cc b/common/binder_constants.cc new file mode 100644 index 0000000..b17962b --- /dev/null +++ b/common/binder_constants.cc @@ -0,0 +1,23 @@ +// 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 "common/binder_constants.h" + +namespace weaved { +namespace binder { + +const char kWeaveServiceName[] = "weave_service"; + +} // namespace binder +} // namespace weaved diff --git a/common/binder_constants.h b/common/binder_constants.h new file mode 100644 index 0000000..82935cd --- /dev/null +++ b/common/binder_constants.h @@ -0,0 +1,26 @@ +// 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. + +#ifndef COMMON_BINDER_CONSTANTS_H_ +#define COMMON_BINDER_CONSTANTS_H_ + +namespace weaved { +namespace binder { + +extern const char kWeaveServiceName[]; + +} // namespace binder +} // namespace weaved + +#endif // COMMON_BINDER_CONSTANTS_H_ diff --git a/common/binder_utils.cc b/common/binder_utils.cc new file mode 100644 index 0000000..6f66040 --- /dev/null +++ b/common/binder_utils.cc @@ -0,0 +1,65 @@ +// 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 "common/binder_utils.h" + +#include <base/json/json_reader.h> +#include <base/json/json_writer.h> + +namespace weaved { +namespace binder_utils { + +android::binder::Status ToStatus(bool success, weave::ErrorPtr* error) { + if (success) + return android::binder::Status::ok(); + return android::binder::Status::fromServiceSpecificError( + 1, android::String8{error->get()->GetMessage().c_str()}); +} + +bool StatusToError(android::binder::Status status, brillo::ErrorPtr* error) { + if (status.isOk()) + return true; + brillo::Error::AddTo(error, FROM_HERE, "binder", + std::to_string(status.exceptionCode()), + status.exceptionMessage().string()); + return false; +} + +android::String16 ToString16(const base::Value& value) { + std::string json; + base::JSONWriter::Write(value, &json); + return ToString16(json); +} + +android::binder::Status ParseDictionary( + const android::String16& json, + std::unique_ptr<base::DictionaryValue>* dict) { + int error = 0; + std::string message; + std::unique_ptr<base::Value> value{ + base::JSONReader::ReadAndReturnError(ToString(json), base::JSON_PARSE_RFC, + &error, &message) + .release()}; + base::DictionaryValue* dict_value = nullptr; + if (!value || !value->GetAsDictionary(&dict_value)) { + return android::binder::Status::fromServiceSpecificError( + error, android::String8{message.c_str()}); + } + dict->reset(dict_value); + value.release(); // |dict| now owns the object. + return android::binder::Status::ok(); +} + +} // namespace binder_utils +} // namespace weaved diff --git a/common/binder_utils.h b/common/binder_utils.h new file mode 100644 index 0000000..65b462d --- /dev/null +++ b/common/binder_utils.h @@ -0,0 +1,63 @@ +// 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. + +#ifndef COMMON_BINDER_UTILS_H_ +#define COMMON_BINDER_UTILS_H_ + +#include <memory> +#include <string> + +#include <base/values.h> +#include <binder/Status.h> +#include <utils/String8.h> +#include <utils/String16.h> +#include <weave/error.h> +#include <brillo/errors/error.h> + +namespace weaved { +namespace binder_utils { + +// Converts the result of weave API call into a binder Status object. +// If |success| is true, return binder::Status::ok(), otherwise the method +// constructs a service-specific failure status with an error message obtained +// from the |error| object. +android::binder::Status ToStatus(bool success, weave::ErrorPtr* error); + +// Converts a binder status code to a Brillo error object. Returns true if the +// status was isOk(), otherwise returns false and provides error information +// in the |error| object. +bool StatusToError(android::binder::Status status, brillo::ErrorPtr* error); + +// Converts binder's UTF16 string into a regular UTF8-encoded standard string. +inline std::string ToString(const android::String16& value) { + return android::String8{value}.string(); +} + +// Converts regular UTF8-encoded standard string into a binder's UTF16 string. +inline android::String16 ToString16(const std::string& value) { + return android::String16{value.c_str()}; +} + +// Serializes a dictionary to a string for transferring over binder. +android::String16 ToString16(const base::Value& value); + +// De-serializes a dictionary from a binder string. +android::binder::Status ParseDictionary( + const android::String16& json, + std::unique_ptr<base::DictionaryValue>* dict); + +} // namespace binder_utils +} // namespace weaved + +#endif // COMMON_BINDER_UTILS_H_ diff --git a/buffet/dbus_conversion.cc b/common/data_conversion.cc index 13f2c87..fd20ad4 100644 --- a/buffet/dbus_conversion.cc +++ b/common/data_conversion.cc @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "buffet/dbus_conversion.h" +#include "common/data_conversion.h" -#include <set> #include <string> #include <vector> #include <brillo/type_name_undecorate.h> -namespace buffet { +namespace weaved { namespace { @@ -54,7 +53,7 @@ brillo::Any DictListToAny(const base::ListValue& list) { for (const base::Value* v : list) { const base::DictionaryValue* dict = nullptr; CHECK(v->GetAsDictionary(&dict)); - result.push_back(DictionaryToDBusVariantDictionary(*dict)); + result.push_back(details::DictionaryValueToVariantDictionary(*dict)); } return result; } @@ -87,7 +86,7 @@ brillo::Any ValueToAny(const base::Value& json) { case base::Value::TYPE_DICTIONARY: { const base::DictionaryValue* dict = nullptr; CHECK(json.GetAsDictionary(&dict)); - prop_value = DictionaryToDBusVariantDictionary(*dict); + prop_value = details::DictionaryValueToVariantDictionary(*dict); break; } case base::Value::TYPE_LIST: { @@ -144,10 +143,13 @@ std::unique_ptr<base::Value> CreateValue(const T& value, return std::unique_ptr<base::Value>{new base::FundamentalValue{value}}; } -template <> -std::unique_ptr<base::Value> CreateValue<std::string>( - const std::string& value, - brillo::ErrorPtr* error) { +std::unique_ptr<base::Value> CreateValue(const std::string& value, + brillo::ErrorPtr* error) { + return std::unique_ptr<base::Value>{new base::StringValue{value}}; +} + +std::unique_ptr<base::Value> CreateValue(const char* value, + brillo::ErrorPtr* error) { return std::unique_ptr<base::Value>{new base::StringValue{value}}; } @@ -155,7 +157,7 @@ template <> std::unique_ptr<base::Value> CreateValue<brillo::VariantDictionary>( const brillo::VariantDictionary& value, brillo::ErrorPtr* error) { - return DictionaryFromDBusVariantDictionary(value, error); + return details::VariantDictionaryToDictionaryValue(value, error); } template <typename T> @@ -209,6 +211,9 @@ std::unique_ptr<base::Value> CreateValue<brillo::Any>( if (!TryCreateValue<std::string>(any, &result, error) || result) return result; + if (any.IsTypeCompatible<const char*>()) + return CreateValue(any.Get<const char*>(), error); + if (!TryCreateValue<brillo::VariantDictionary>(any, &result, error) || result) { return result; @@ -227,8 +232,9 @@ std::unique_ptr<base::Value> CreateValue<brillo::Any>( } // namespace -// TODO(vitalybuka): Use in buffet_client. -brillo::VariantDictionary DictionaryToDBusVariantDictionary( +namespace details { + +brillo::VariantDictionary DictionaryValueToVariantDictionary( const base::DictionaryValue& object) { brillo::VariantDictionary result; @@ -238,7 +244,7 @@ brillo::VariantDictionary DictionaryToDBusVariantDictionary( return result; } -std::unique_ptr<base::DictionaryValue> DictionaryFromDBusVariantDictionary( +std::unique_ptr<base::DictionaryValue> VariantDictionaryToDictionaryValue( const brillo::VariantDictionary& object, brillo::ErrorPtr* error) { std::unique_ptr<base::DictionaryValue> result{new base::DictionaryValue}; @@ -253,4 +259,5 @@ std::unique_ptr<base::DictionaryValue> DictionaryFromDBusVariantDictionary( return result; } -} // namespace buffet +} // namespace details +} // namespace weaved diff --git a/buffet/dbus_conversion.h b/common/data_conversion.h index b71e823..accc520 100644 --- a/buffet/dbus_conversion.h +++ b/common/data_conversion.h @@ -12,25 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef BUFFET_DBUS_CONVERSION_H_ -#define BUFFET_DBUS_CONVERSION_H_ +#ifndef COMMON_DATA_CONVERSION_H_ +#define COMMON_DATA_CONVERSION_H_ #include <base/values.h> #include <brillo/any.h> #include <brillo/errors/error.h> #include <brillo/variant_dictionary.h> -namespace buffet { +namespace weaved { +namespace details { -// Converts DictionaryValue to D-Bus variant dictionary. -brillo::VariantDictionary DictionaryToDBusVariantDictionary( +// Converts DictionaryValue to variant dictionary. +brillo::VariantDictionary DictionaryValueToVariantDictionary( const base::DictionaryValue& object); -// Converts D-Bus variant dictionary to DictionaryValue. -std::unique_ptr<base::DictionaryValue> DictionaryFromDBusVariantDictionary( +// Converts variant dictionary to DictionaryValue. +std::unique_ptr<base::DictionaryValue> VariantDictionaryToDictionaryValue( const brillo::VariantDictionary& object, brillo::ErrorPtr* error); -} // namespace buffet +} // namespace details +} // namespace weaved -#endif // BUFFET_DBUS_CONVERSION_H_ +#endif // COMMON_DATA_CONVERSION_H_ diff --git a/buffet/dbus_conversion_unittest.cc b/common/data_conversion_unittest.cc index 84db100..832fffb 100644 --- a/buffet/dbus_conversion_unittest.cc +++ b/common/data_conversion_unittest.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "buffet/dbus_conversion.h" +#include "common/data_conversion.h" #include <limits> #include <memory> @@ -26,7 +26,7 @@ #include <gtest/gtest.h> #include <weave/test/unittest_utils.h> -namespace buffet { +namespace weaved { namespace { @@ -35,14 +35,14 @@ using brillo::VariantDictionary; using weave::test::CreateDictionaryValue; using weave::test::IsEqualValue; -brillo::VariantDictionary ToDBus(const base::DictionaryValue& object) { - return DictionaryToDBusVariantDictionary(object); +brillo::VariantDictionary ToVariant(const base::DictionaryValue& object) { + return details::DictionaryValueToVariantDictionary(object); } -std::unique_ptr<base::DictionaryValue> FromDBus( +std::unique_ptr<base::DictionaryValue> FromVariant( const brillo::VariantDictionary& object) { brillo::ErrorPtr error; - auto result = DictionaryFromDBusVariantDictionary(object, &error); + auto result = details::VariantDictionaryToDictionaryValue(object, &error); EXPECT_TRUE(result || error); return result; } @@ -133,64 +133,67 @@ std::unique_ptr<base::Value> CreateRandomValue(int children) { TEST(DBusConversionTest, DictionaryToDBusVariantDictionary) { EXPECT_EQ((VariantDictionary{{"bool", true}}), - ToDBus(*CreateDictionaryValue("{'bool': true}"))); + ToVariant(*CreateDictionaryValue("{'bool': true}"))); EXPECT_EQ((VariantDictionary{{"int", 5}}), - ToDBus(*CreateDictionaryValue("{'int': 5}"))); + ToVariant(*CreateDictionaryValue("{'int': 5}"))); EXPECT_EQ((VariantDictionary{{"double", 6.7}}), - ToDBus(*CreateDictionaryValue("{'double': 6.7}"))); + ToVariant(*CreateDictionaryValue("{'double': 6.7}"))); EXPECT_EQ((VariantDictionary{{"string", std::string{"abc"}}}), - ToDBus(*CreateDictionaryValue("{'string': 'abc'}"))); + ToVariant(*CreateDictionaryValue("{'string': 'abc'}"))); EXPECT_EQ((VariantDictionary{{"object", VariantDictionary{{"bool", true}}}}), - ToDBus(*CreateDictionaryValue("{'object': {'bool': true}}"))); + ToVariant(*CreateDictionaryValue("{'object': {'bool': true}}"))); EXPECT_EQ((VariantDictionary{{"emptyList", std::vector<Any>{}}}), - ToDBus(*CreateDictionaryValue("{'emptyList': []}"))); + ToVariant(*CreateDictionaryValue("{'emptyList': []}"))); EXPECT_EQ((VariantDictionary{{"intList", std::vector<int>{5}}}), - ToDBus(*CreateDictionaryValue("{'intList': [5]}"))); + ToVariant(*CreateDictionaryValue("{'intList': [5]}"))); EXPECT_EQ((VariantDictionary{ {"intListList", std::vector<Any>{std::vector<int>{5}, std::vector<int>{6, 7}}}}), - ToDBus(*CreateDictionaryValue("{'intListList': [[5], [6, 7]]}"))); + ToVariant(*CreateDictionaryValue( + "{'intListList': [[5], [6, 7]]}"))); EXPECT_EQ((VariantDictionary{{"objList", std::vector<VariantDictionary>{ {{"string", std::string{"abc"}}}}}}), - ToDBus(*CreateDictionaryValue("{'objList': [{'string': 'abc'}]}"))); + ToVariant(*CreateDictionaryValue( + "{'objList': [{'string': 'abc'}]}"))); } -TEST(DBusConversionTest, DictionaryFromDBusVariantDictionary) { - EXPECT_JSON_EQ("{'bool': true}", *FromDBus({{"bool", true}})); - EXPECT_JSON_EQ("{'int': 5}", *FromDBus({{"int", 5}})); - EXPECT_JSON_EQ("{'double': 6.7}", *FromDBus({{"double", 6.7}})); +TEST(DBusConversionTest, VariantDictionaryToDictionaryValue) { + EXPECT_JSON_EQ("{'bool': true}", *FromVariant({{"bool", true}})); + EXPECT_JSON_EQ("{'int': 5}", *FromVariant({{"int", 5}})); + EXPECT_JSON_EQ("{'double': 6.7}", *FromVariant({{"double", 6.7}})); EXPECT_JSON_EQ("{'string': 'abc'}", - *FromDBus({{"string", std::string{"abc"}}})); + *FromVariant({{"string", std::string{"abc"}}})); EXPECT_JSON_EQ("{'object': {'bool': true}}", - *FromDBus({{"object", VariantDictionary{{"bool", true}}}})); + *FromVariant({{"object", VariantDictionary{{"bool", true}}}})); EXPECT_JSON_EQ("{'emptyList': []}", - *FromDBus({{"emptyList", std::vector<bool>{}}})); + *FromVariant({{"emptyList", std::vector<bool>{}}})); EXPECT_JSON_EQ("{'intList': [5]}", - *FromDBus({{"intList", std::vector<int>{5}}})); + *FromVariant({{"intList", std::vector<int>{5}}})); EXPECT_JSON_EQ( "{'intListList': [[5], [6, 7]]}", - *FromDBus({{"intListList", std::vector<Any>{std::vector<int>{5}, - std::vector<int>{6, 7}}}})); + *FromVariant({{"intListList", + std::vector<Any>{std::vector<int>{5}, + std::vector<int>{6, 7}}}})); EXPECT_JSON_EQ( "{'objList': [{'string': 'abc'}]}", - *FromDBus({{"objList", std::vector<VariantDictionary>{ - {{"string", std::string{"abc"}}}}}})); - EXPECT_JSON_EQ("{'int': 5}", *FromDBus({{"int", Any{Any{5}}}})); + *FromVariant({{"objList", std::vector<VariantDictionary>{ + {{"string", std::string{"abc"}}}}}})); + EXPECT_JSON_EQ("{'int': 5}", *FromVariant({{"int", Any{Any{5}}}})); } -TEST(DBusConversionTest, DictionaryFromDBusVariantDictionary_Errors) { - EXPECT_FALSE(FromDBus({{"cString", "abc"}})); - EXPECT_FALSE(FromDBus({{"float", 1.0f}})); - EXPECT_FALSE(FromDBus({{"listList", std::vector<std::vector<int>>{}}})); - EXPECT_FALSE(FromDBus({{"any", Any{}}})); - EXPECT_FALSE(FromDBus({{"null", nullptr}})); +TEST(DBusConversionTest, VariantDictionaryToDictionaryValueErrors) { + EXPECT_FALSE(FromVariant({{"cString", "abc"}})); + EXPECT_FALSE(FromVariant({{"float", 1.0f}})); + EXPECT_FALSE(FromVariant({{"listList", std::vector<std::vector<int>>{}}})); + EXPECT_FALSE(FromVariant({{"any", Any{}}})); + EXPECT_FALSE(FromVariant({{"null", nullptr}})); } TEST(DBusConversionTest, DBusRandomDictionaryConversion) { auto dict = CreateRandomDictionary(10000); - auto varian_dict = ToDBus(*dict); - auto dict_restored = FromDBus(varian_dict); + auto varian_dict = ToVariant(*dict); + auto dict_restored = FromVariant(varian_dict); EXPECT_PRED2(IsEqualValue, *dict, *dict_restored); } diff --git a/libweaved/README.md b/libweaved/README.md new file mode 100644 index 0000000..efc564b --- /dev/null +++ b/libweaved/README.md @@ -0,0 +1,76 @@ +For system daemons which need to interface with the weave daemon (weaved), these +daemons will need to link to **libweaved**. + +The `weaved::Service` class is an entry point into weave daemon interface. +This class maintains an IPC connection to the daemon and allows clients to +register weave command handlers and update the device state they are +responsible for. + +In order to create an instance of `Service`, call asynchronous +`Service::Connect` static method. This method initiates a connection to weaved +and once established invokes the provided `callback`. When the callback is +invoked, the connection to the weave daemon is available and the client should +create their component, register command handlers and update the state. +If connection is lost (e.g. the weave daemon exist), the provided weak +pointer to the `Service` object becomes invalidated. As soon as weaved is +restarted and the connection is restored, the `callback` is invoked again and +the client can re-register command handlers, update the state again. + +A simple client daemon that works with weaved could be as follows: + +``` +class Daemon final : public brillo::Daemon { + public: + Daemon() = default; + + protected: + int OnInit() override; + + private: + void OnConnected(const std::weak_ptr<weaved::Service>& service); + void OnCommand1(std::unique_ptr<weaved::Command> command); + void UpdateDeviceState(); + + std::unique_ptr<weaved::Service::Token> weave_service_token_; + std::weak_ptr<weaved::Service> weave_service_; + brillo::BinderWatcher binder_watcher_; + base::WeakPtrFactory<Daemon> weak_ptr_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(Daemon); +}; + +int Daemon::OnInit() { + android::BinderWrapper::Create(); + if (!binder_watcher_.Init()) + return EX_OSERR; + + weave_service_token_ = weaved::Service::Connect( + brillo::MessageLoop::current(), + base::Bind(&Daemon::OnConnected, weak_ptr_factory_.GetWeakPtr())); + return brillo::Daemon::OnInit(); +} + +void Daemon::OnConnected(const std::weak_ptr<weaved::Service>& service) { + weave_service_ = service; + auto weave_service = weave_service_.lock(); + if (!weave_service) + return; + + weave_service->AddComponent("myComponent", {"_myTrait"}, nullptr); + weave_service->AddCommandHandler( + "myComponent", "_myTrait.command1", + base::Bind(&Daemon::OnCommand1, base::Unretained(this))); + UpdateDeviceState(); +} + +void Daemon::UpdateDeviceState() { + auto weave_service = weave_service_.lock(); + if (!weave_service) + return; + + brillo::VariantDictionary state_change{ + {"_myTrait.state1", 12}, + {"_myTrait.state2", std::string{"foo"}}, + }; + weave_service->SetStateProperties("myComponent", state_change, nullptr); +} +```
\ No newline at end of file diff --git a/libweaved/command.cc b/libweaved/command.cc index caee8b0..9950673 100644 --- a/libweaved/command.cc +++ b/libweaved/command.cc @@ -1,42 +1,64 @@ -/* - * 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 "command.h" - -#include "buffet/dbus-proxies.h" +// 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 "libweaved/command.h" + +#include "android/weave/IWeaveCommand.h" +#include "common/binder_utils.h" +#include "common/data_conversion.h" + +using weaved::binder_utils::ParseDictionary; +using weaved::binder_utils::ToString; +using weaved::binder_utils::ToString16; +using weaved::binder_utils::StatusToError; namespace weaved { -Command::Command(com::android::Weave::CommandProxyInterface* proxy) - : proxy_{proxy} {} +Command::Command(const android::sp<android::weave::IWeaveCommand>& proxy) + : binder_proxy_{proxy} {} + +Command::~Command() {} -const std::string& Command::GetID() const { - return proxy_->id(); +std::string Command::GetID() const { + std::string id; + android::String16 id16; + if (binder_proxy_->getId(&id16).isOk()) + id.assign(ToString(id16)); + return id; } -const std::string& Command::GetName() const { - return proxy_->name(); +std::string Command::GetName() const { + std::string name; + android::String16 name16; + if (binder_proxy_->getId(&name16).isOk()) + name.assign(ToString(name16)); + return name; } -const std::string& Command::GetComponent() const { - return proxy_->component(); +std::string Command::GetComponent() const { + std::string component; + android::String16 component16; + if (binder_proxy_->getId(&component16).isOk()) + component.assign(ToString(component16)); + return component; } Command::State Command::GetState() const { - std::string state = proxy_->state(); + std::string state; + android::String16 state16; + if (binder_proxy_->getState(&state16).isOk()) + state.assign(ToString(state16)); if (state == "queued") return Command::State::kQueued; else if (state == "inProgress") @@ -58,7 +80,10 @@ Command::State Command::GetState() const { } Command::Origin Command::GetOrigin() const { - std::string origin = proxy_->origin(); + std::string origin; + android::String16 origin16; + if (binder_proxy_->getState(&origin16).isOk()) + origin.assign(ToString(origin16)); if (origin == "local") return Command::Origin::kLocal; else if (origin == "cloud") @@ -67,28 +92,41 @@ Command::Origin Command::GetOrigin() const { return Command::Origin::kLocal; } -const brillo::VariantDictionary& Command::GetParameters() const { - return proxy_->parameters(); +brillo::VariantDictionary Command::GetParameters() const { + brillo::VariantDictionary params; + android::String16 params_string16; + if (binder_proxy_->getParameters(¶ms_string16).isOk()) { + std::unique_ptr<base::DictionaryValue> dict; + if (ParseDictionary(params_string16, &dict).isOk()) + params = details::DictionaryValueToVariantDictionary(*dict); + } + return params; } bool Command::SetProgress(const brillo::VariantDictionary& progress, brillo::ErrorPtr* error) { - return proxy_->SetProgress(progress, error); + auto dict = details::VariantDictionaryToDictionaryValue(progress, error); + return dict && StatusToError(binder_proxy_->setProgress(ToString16(*dict)), + error); } bool Command::Complete(const brillo::VariantDictionary& results, brillo::ErrorPtr* error) { - return proxy_->Complete(results, error); + auto dict = details::VariantDictionaryToDictionaryValue(results, error); + return dict && StatusToError(binder_proxy_->complete(ToString16(*dict)), + error); } bool Command::Abort(const std::string& error_code, const std::string& error_message, brillo::ErrorPtr* error) { - return proxy_->Abort(error_code, error_message, error); + return StatusToError(binder_proxy_->abort(ToString16(error_code), + ToString16(error_message)), + error); } bool Command::Cancel(brillo::ErrorPtr* error) { - return proxy_->Cancel(error); + return StatusToError(binder_proxy_->cancel(), error); } } // namespace weave diff --git a/libweaved/command.h b/libweaved/command.h index a8332e5..2b69266 100644 --- a/libweaved/command.h +++ b/libweaved/command.h @@ -1,18 +1,16 @@ -/* - * 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. - */ +// 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. #ifndef LIBWEAVED_COMMAND_H_ #define LIBWEAVED_COMMAND_H_ @@ -23,19 +21,17 @@ #include <brillo/errors/error.h> #include <brillo/variant_dictionary.h> #include <libweaved/export.h> +#include <utils/StrongPointer.h> -namespace com { namespace android { -namespace Weave { -class CommandProxyInterface; -} // namespace Weave +namespace weave { +class IWeaveCommand; +} // namespace weave } // namespace android -} // namespace com - namespace weaved { -class Device; +class ServiceImpl; namespace detail { @@ -76,14 +72,16 @@ class LIBWEAVED_EXPORT Command final { enum class Origin { kLocal, kCloud }; + ~Command(); + // Returns the full command ID. - const std::string& GetID() const; + std::string GetID() const; // Returns the full name of the command. - const std::string& GetName() const; + std::string GetName() const; // Returns the name of the component this command was sent to. - const std::string& GetComponent() const; + std::string GetComponent() const; // Returns the command state. Command::State GetState() const; @@ -92,7 +90,7 @@ class LIBWEAVED_EXPORT Command final { Command::Origin GetOrigin() const; // Returns the command parameters. - const brillo::VariantDictionary& GetParameters() const; + brillo::VariantDictionary GetParameters() const; // Helper function to get a command parameter of particular type T from the // command parameter list. Returns default value for type T (e.g. 0 for int or @@ -130,11 +128,11 @@ class LIBWEAVED_EXPORT Command final { bool Cancel(brillo::ErrorPtr* error); protected: - Command(com::android::Weave::CommandProxyInterface* proxy); + explicit Command(const android::sp<android::weave::IWeaveCommand>& proxy); private: - friend class Device; - com::android::Weave::CommandProxyInterface* proxy_{nullptr}; + friend class ServiceImpl; + android::sp<android::weave::IWeaveCommand> binder_proxy_; DISALLOW_COPY_AND_ASSIGN(Command); }; diff --git a/libweaved/device.cc b/libweaved/device.cc deleted file mode 100644 index 5f396a7..0000000 --- a/libweaved/device.cc +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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 "device.h" - -#include "buffet/dbus-proxies.h" - -using com::android::Weave::CommandProxyInterface; -using com::android::Weave::ManagerProxyInterface; - -namespace weaved { - -Device::Device(const scoped_refptr<dbus::Bus>& bus, - const base::Closure& state_required_callback) - : bus_{bus}, state_required_callback_{state_required_callback} { - weaved_object_mgr_.reset(new com::android::Weave::ObjectManagerProxy{bus_}); - weaved_object_mgr_->SetCommandAddedCallback( - base::Bind(&Device::OnCommandAdded, base::Unretained(this))); - weaved_object_mgr_->SetCommandRemovedCallback( - base::Bind(&Device::OnCommandRemoved, base::Unretained(this))); - weaved_object_mgr_->SetManagerAddedCallback( - base::Bind(&Device::OnManagerAdded, base::Unretained(this))); - weaved_object_mgr_->SetManagerRemovedCallback( - base::Bind(&Device::OnManagerRemoved, base::Unretained(this))); -} - -Device::~Device() { -} - -std::unique_ptr<Device> Device::CreateInstance( - const scoped_refptr<dbus::Bus>& bus, - const base::Closure& state_required_callback) { - return std::unique_ptr<Device>{new Device{bus, state_required_callback}}; -} - -void Device::AddComponent(const std::string& component, - const std::vector<std::string>& traits) { - ComponentEntry entry; - entry.component = component; - entry.traits = traits; - components_.push_back(std::move(entry)); - if (proxy_) - proxy_->AddComponent(component, traits, nullptr); -} - -void Device::AddCommandHandler(const std::string& component, - const std::string& command_name, - const CommandHandlerCallback& callback) { - for (const auto& entry : command_handlers_) { - if (entry.command_name != command_name) - continue; - // The command names are the same, make sure we have different components. - // This means that both component names are not empty and are different. - CHECK(!component.empty() && !entry.component.empty() && - component != entry.component) - << "Handler for " << component << ":" << command_name << " already set"; - } - CommandHandlerEntry entry; - entry.component = component; - entry.command_name = command_name; - entry.callback = callback; - - command_handlers_.push_back(std::move(entry)); - - // If there are any commands already received, call the handler immediately. - for (auto& pair : command_map_) { - if (pair.first->name() == command_name && - (component.empty() || pair.first->component() == component)) { - if (!pair.second) - pair.second.reset(new Command{pair.first}); - callback.Run(pair.second); - } - } -} - -bool Device::SetStateProperties(const std::string& component, - const brillo::VariantDictionary& dict, - brillo::ErrorPtr* error) { - if (proxy_) - return proxy_->UpdateState(component, dict, error); - - brillo::Error::AddTo(error, FROM_HERE, "weaved", "service_unavailable", - "Process 'weaved' is unreachable"); - return false; -} - -bool Device::SetStateProperty(const std::string& component, - const std::string& name, - const brillo::Any& value, - brillo::ErrorPtr* error) { - return SetStateProperties(component, brillo::VariantDictionary{{name, value}}, - error); -} - -void Device::AddCommandHandler(const std::string& command_name, - const CommandHandlerCallback& callback) { - AddCommandHandler("", command_name, callback); -} - -bool Device::SetStateProperties(const brillo::VariantDictionary& dict, - brillo::ErrorPtr* error) { - return SetStateProperties("", dict, error); -} - -bool Device::SetStateProperty(const std::string& name, - const brillo::Any& value, - brillo::ErrorPtr* error) { - return SetStateProperty("", name, value, error); -} - -void Device::OnCommandAdded(CommandProxyInterface* proxy) { - std::shared_ptr<Command>& command = command_map_[proxy]; - const Device::CommandHandlerCallback* callback = FindHandlerForCommand(proxy); - if (!callback) - return; - if (!command) - command.reset(new Command{proxy}); - callback->Run(command); -} - -void Device::OnCommandRemoved(const dbus::ObjectPath& object_path) { - auto proxy = weaved_object_mgr_->GetCommandProxy(object_path); - if (!proxy) - return; - command_map_.erase(proxy); -} - -void Device::OnManagerAdded(ManagerProxyInterface* proxy) { - proxy_ = proxy; - for (const auto& entry : components_) - proxy_->AddComponent(entry.component, entry.traits, nullptr); - state_required_callback_.Run(); -} - -void Device::OnManagerRemoved(const dbus::ObjectPath& object_path) { - proxy_ = nullptr; -} - -const Device::CommandHandlerCallback* Device::FindHandlerForCommand( - com::android::Weave::CommandProxyInterface* proxy) const { - for (const auto& entry : command_handlers_) { - if (proxy->name() == entry.command_name && - (entry.component.empty() || proxy->component() == entry.component)) { - return &entry.callback; - } - } - return nullptr; -} - - -} // namespace weave diff --git a/libweaved/device.h b/libweaved/device.h deleted file mode 100644 index 376b656..0000000 --- a/libweaved/device.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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. - */ - - -#ifndef LIBWEAVED_DEVICE_H_ -#define LIBWEAVED_DEVICE_H_ - -#include <map> -#include <memory> -#include <string> -#include <vector> - -#include <base/callback.h> -#include <base/macros.h> -#include <base/memory/ref_counted.h> -#include <brillo/any.h> -#include <brillo/errors/error.h> -#include <brillo/variant_dictionary.h> -#include <libweaved/command.h> -#include <libweaved/export.h> - -namespace com { -namespace android { -namespace Weave { -class CommandProxyInterface; -class ManagerProxyInterface; -class ObjectManagerProxy; -} // namespace Weave -} // namespace android -} // namespace com - -namespace dbus { -class Bus; -class ObjectPath; -} // namespace dbus - -namespace weaved { - -class LIBWEAVED_EXPORT Device final { - public: - ~Device(); - - static std::unique_ptr<Device> CreateInstance( - const scoped_refptr<dbus::Bus>& bus, - const base::Closure& state_required_callback); - - // Callback type for AddCommandHandler. - using CommandHandlerCallback = - base::Callback<void(const std::weak_ptr<Command>& command)>; - - void AddComponent(const std::string& component, - const std::vector<std::string>& traits); - - // Sets handler for new commands added to the queue. - // |command_name| is the full command name of the command to handle. e.g. - // "base.reboot". Each command can have no more than one handler. - void AddCommandHandler(const std::string& component, - const std::string& command_name, - const CommandHandlerCallback& callback); - - bool SetStateProperties(const std::string& component, - const brillo::VariantDictionary& dict, - brillo::ErrorPtr* error); - - // Sets value of the single property. - // |name| is full property name, including package name. e.g. "base.network". - bool SetStateProperty(const std::string& component, - const std::string& name, - const brillo::Any& value, - brillo::ErrorPtr* error); - - // Sets handler for new commands added to the queue. - // |command_name| is the full command name of the command to handle. e.g. - // "base.reboot". Each command can have no more than one handler. - LIBWEAVED_DEPRECATED void AddCommandHandler( - const std::string& command_name, - const CommandHandlerCallback& callback); - - LIBWEAVED_DEPRECATED bool SetStateProperties( - const brillo::VariantDictionary& dict, - brillo::ErrorPtr* error); - - // Sets value of the single property. - // |name| is full property name, including package name. e.g. "base.network". - LIBWEAVED_DEPRECATED bool SetStateProperty(const std::string& name, - const brillo::Any& value, - brillo::ErrorPtr* error); - - private: - Device(const scoped_refptr<dbus::Bus>& bus, - const base::Closure& state_required_callback); - - void OnCommandAdded(com::android::Weave::CommandProxyInterface* proxy); - void OnCommandRemoved(const dbus::ObjectPath& object_path); - - void OnManagerAdded(com::android::Weave::ManagerProxyInterface* proxy); - void OnManagerRemoved(const dbus::ObjectPath& object_path); - - const CommandHandlerCallback* FindHandlerForCommand( - com::android::Weave::CommandProxyInterface* proxy) const; - - std::unique_ptr<com::android::Weave::ObjectManagerProxy> weaved_object_mgr_; - com::android::Weave::ManagerProxyInterface* proxy_{nullptr}; - - using CommandMap = std::map<com::android::Weave::CommandProxyInterface*, - std::shared_ptr<Command>>; - CommandMap command_map_; - - struct CommandHandlerEntry { - std::string component; - std::string command_name; - CommandHandlerCallback callback; - }; - std::vector<CommandHandlerEntry> command_handlers_; - - struct ComponentEntry { - std::string component; - std::vector<std::string> traits; - }; - std::vector<ComponentEntry> components_; - - scoped_refptr<dbus::Bus> bus_; - base::Closure state_required_callback_; - - - DISALLOW_COPY_AND_ASSIGN(Device); -}; - -} // namespace weave - -#endif // LIBWEAVE_INCLUDE_WEAVE_DEVICE_H_ diff --git a/libweaved/service.cc b/libweaved/service.cc new file mode 100644 index 0000000..83a404d --- /dev/null +++ b/libweaved/service.cc @@ -0,0 +1,400 @@ +// 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 <binderwrapper/binder_wrapper.h> +#include <brillo/message_loops/message_loop.h> + +#include "android/weave/BnWeaveClient.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" +#include "common/data_conversion.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); +}; + +// 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& command_name, + const CommandHandlerCallback& callback) override; + bool SetStateProperties(const std::string& component, + const brillo::VariantDictionary& dict, + brillo::ErrorPtr* error) override; + bool SetStateProperty(const std::string& component, + const std::string& name, + const brillo::Any& value, + brillo::ErrorPtr* error) 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); + + 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. + void OnWeaveServiceDisconnected(); + + android::BinderWrapper* binder_wrapper_; + brillo::MessageLoop* message_loop_; + ServiceSubscription* service_subscription_; + ConnectionCallback connection_callback_; + android::sp<android::weave::IWeaveService> weave_service_; + + 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(); +} + +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& command_name, + const CommandHandlerCallback& callback) { + CHECK(!component.empty() && !command_name.empty()); + CHECK(weave_service_.get()); + + CommandHandlerEntry entry; + entry.component = component; + entry.command_name = command_name; + entry.callback = callback; + command_handlers_.push_back(std::move(entry)); + + auto status = weave_service_->registerCommandHandler( + android::String16{component.c_str()}, + android::String16{command_name.c_str()}); + CHECK(status.isOk()); +} + +bool ServiceImpl::SetStateProperties(const std::string& component, + const brillo::VariantDictionary& dict, + brillo::ErrorPtr* error) { + CHECK(!component.empty()); + CHECK(weave_service_.get()); + auto properties = details::VariantDictionaryToDictionaryValue(dict, error); + if (!properties) + return false; + return StatusToError(weave_service_->updateState(ToString16(component), + ToString16(*properties)), + error); +} + +bool ServiceImpl::SetStateProperty(const std::string& component, + const std::string& name, + const brillo::Any& value, + brillo::ErrorPtr* error) { + return SetStateProperties(component, brillo::VariantDictionary{{name, value}}, + error); +} + +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()); + // Call this last in case the binder object is already gone and the death + // notification callback (OnWeaveServiceDisconnected) is invoked immediately. + // In this case, the instance of ServiceImpl will get destroyed before + // RegisterForDeathNotifications returns. + binder_wrapper_->RegisterForDeathNotifications( + android::IInterface::asBinder(service), + base::Bind(&ServiceImpl::OnWeaveServiceDisconnected, + weak_ptr_factory_.GetWeakPtr())); +} + +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(INFO) << "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; + } + + auto service_manager = + android::interface_cast<android::weave::IWeaveServiceManager>(binder); + android::sp<WeaveClient> weave_client = new WeaveClient{shared_from_this()}; + service_manager->connect(weave_client); +} + +void ServiceImpl::OnWeaveServiceDisconnected() { + 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. +} + +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 diff --git a/libweaved/service.h b/libweaved/service.h new file mode 100644 index 0000000..7ae6824 --- /dev/null +++ b/libweaved/service.h @@ -0,0 +1,122 @@ +// 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. + +#ifndef LIBWEAVED_SERVICE_H_ +#define LIBWEAVED_SERVICE_H_ + +#include <memory> +#include <string> +#include <vector> + +#include <base/callback.h> +#include <base/compiler_specific.h> +#include <base/macros.h> +#include <brillo/errors/error.h> +#include <libweaved/command.h> +#include <libweaved/export.h> + +namespace brillo { +class MessageLoop; +} // namespace brillo + +namespace weaved { + +// A weaved service is an abstract interface representing an instance of weave +// services for a particular client daemon. Apart from providing an API to +// weaved process, it manages resources specific for an instance of the client. +// For example, when a client exits, it removes any resources (e.g. components) +// that were added by this client from the weaved's component tree. +class LIBWEAVED_EXPORT Service { + public: + // Callback type for AddCommandHandler. + using CommandHandlerCallback = + base::Callback<void(std::unique_ptr<Command> command)>; + + Service() = default; + virtual ~Service() = default; + + // Adds a new component instance to device. + // |component| is a component name being added. + // |traits| is a list of trait names this component supports. + virtual bool AddComponent(const std::string& component, + const std::vector<std::string>& traits, + brillo::ErrorPtr* error) = 0; + + // Sets handler for new commands added to the queue for a given |component|. + // |command_name| is the full name of the command to handle, including the + // name of the trait, e.g. "base.reboot". + // Each command can have no more than one handler. + virtual void AddCommandHandler(const std::string& component, + const std::string& command_name, + const CommandHandlerCallback& callback) = 0; + + // Sets a number of state properties for a given |component|. + // |dict| is a dictionary containing property-name/property-value pairs. + virtual bool SetStateProperties(const std::string& component, + const brillo::VariantDictionary& dict, + brillo::ErrorPtr* error) = 0; + + // Sets value of the single property. + // |name| is full property name, including trait name. e.g. "base.network". + virtual bool SetStateProperty(const std::string& component, + const std::string& name, + const brillo::Any& value, + brillo::ErrorPtr* error) = 0; + + // Service creation functionality. + // Subscription is a base class for an object responsible for life-time + // management for the service. The service instance is kept alive for as long + // as the service connection is alive. See comments for Service::Connect for + // more details. + class Subscription { + public: + virtual ~Subscription() = default; + + protected: + Subscription() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(Subscription); + }; + + using ConnectionCallback = + base::Callback<void(const std::weak_ptr<Service>& service)>; + + // Creates an instance of weaved service asynchronously. This not only creates + // the service class instance but also establishes an RPC connection to + // weaved daemon. Upon connection having been established, a |callback| is + // invoked and an instance of Service is passed to it as an argument. + // The service instance provided to the callback is a weak pointer to the + // actual service which may be destroyed at any time if RPC connection to + // weaved is lost. If this happens, a connection is re-established and the + // |callback| is called again with a new instance of the service. + // Therefore, if locking the |service| produces nullptr, this means that the + // service got disconnected, so no further action can be taken. Since the + // |callback| will be invoked with the new service instance when connection + // is re-established, it's a good idea to update the device state on each + // invocation of the callback (along with registering command handlers, etc). + // IMPORTANT: Keep the returned subscription object around for as long as the + // service is needed. As soon as the subscription is destroyed, the connection + // to weaved is terminated and the service instance is discarded. + static std::unique_ptr<Subscription> Connect( + brillo::MessageLoop* message_loop, + const ConnectionCallback& callback) WARN_UNUSED_RESULT; + + private: + DISALLOW_COPY_AND_ASSIGN(Service); +}; + +} // namespace weaved + +#endif // LIBWEAVED_SERVICE_H_ |