// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/dbus/gesture_properties_service_provider.h" #include #include #include "base/bind.h" #include "base/logging.h" #include "dbus/message.h" #include "third_party/cros_system_api/dbus/service_constants.h" #include "ui/ozone/public/input_controller.h" #include "ui/ozone/public/ozone_platform.h" namespace ash { GesturePropertiesServiceProvider::GesturePropertiesServiceProvider() : weak_ptr_factory_(this) {} GesturePropertiesServiceProvider::~GesturePropertiesServiceProvider() = default; void GesturePropertiesServiceProvider::Start( scoped_refptr exported_object) { auto on_exported = base::BindRepeating(&GesturePropertiesServiceProvider::OnExported, weak_ptr_factory_.GetWeakPtr()); exported_object->ExportMethod( chromeos::kGesturePropertiesServiceInterface, chromeos::kGesturePropertiesServiceListDevicesMethod, base::BindRepeating(&GesturePropertiesServiceProvider::ListDevices, weak_ptr_factory_.GetWeakPtr()), on_exported); exported_object->ExportMethod( chromeos::kGesturePropertiesServiceInterface, chromeos::kGesturePropertiesServiceListPropertiesMethod, base::BindRepeating(&GesturePropertiesServiceProvider::ListProperties, weak_ptr_factory_.GetWeakPtr()), on_exported); exported_object->ExportMethod( chromeos::kGesturePropertiesServiceInterface, chromeos::kGesturePropertiesServiceGetPropertyMethod, base::BindRepeating(&GesturePropertiesServiceProvider::GetProperty, weak_ptr_factory_.GetWeakPtr()), on_exported); exported_object->ExportMethod( chromeos::kGesturePropertiesServiceInterface, chromeos::kGesturePropertiesServiceSetPropertyMethod, base::BindRepeating(&GesturePropertiesServiceProvider::SetProperty, weak_ptr_factory_.GetWeakPtr()), on_exported); } void GesturePropertiesServiceProvider::OnExported( const std::string& interface_name, const std::string& method_name, bool success) { if (!success) LOG(ERROR) << "Failed to export " << interface_name << "." << method_name; } namespace { void GetPropertyCallback(dbus::MethodCall* method_call, std::unique_ptr response, dbus::ExportedObject::ResponseSender response_sender, bool is_read_only, ui::ozone::mojom::GesturePropValuePtr values) { if (values.is_null()) { std::move(response_sender) .Run(dbus::ErrorResponse::FromMethodCall( method_call, DBUS_ERROR_INVALID_ARGS, "The device ID or property name specified was not found.")); return; } dbus::MessageWriter writer(response.get()); dbus::MessageWriter variant_writer(nullptr); dbus::MessageWriter array_writer(nullptr); writer.AppendBool(is_read_only); switch (values->which()) { case ui::ozone::mojom::GesturePropValue::Tag::kInts: { writer.AppendUint32(values->get_ints().size()); writer.OpenVariant("ai", &variant_writer); variant_writer.AppendArrayOfInt32s(values->get_ints().data(), values->get_ints().size()); writer.CloseContainer(&variant_writer); break; } case ui::ozone::mojom::GesturePropValue::Tag::kShorts: { writer.AppendUint32(values->get_shorts().size()); writer.OpenVariant("an", &variant_writer); variant_writer.OpenArray("n", &array_writer); for (int16_t value : values->get_shorts()) { array_writer.AppendInt16(value); } variant_writer.CloseContainer(&array_writer); writer.CloseContainer(&variant_writer); break; } case ui::ozone::mojom::GesturePropValue::Tag::kBools: { writer.AppendUint32(values->get_bools().size()); writer.OpenVariant("ab", &variant_writer); variant_writer.OpenArray("b", &array_writer); for (bool value : values->get_bools()) { array_writer.AppendBool(value); } variant_writer.CloseContainer(&array_writer); writer.CloseContainer(&variant_writer); break; } case ui::ozone::mojom::GesturePropValue::Tag::kStr: { writer.AppendUint32(1); writer.AppendVariantOfString(values->get_str()); break; } case ui::ozone::mojom::GesturePropValue::Tag::kReals: { writer.AppendUint32(values->get_reals().size()); writer.OpenVariant("ad", &variant_writer); variant_writer.AppendArrayOfDoubles(values->get_reals().data(), values->get_reals().size()); writer.CloseContainer(&variant_writer); break; } default: { // This should never happen. LOG(WARNING) << "No value set on GesturePropValue union; not returning " "values to GetProperty call."; writer.AppendUint32(0); break; } } std::move(response_sender).Run(std::move(response)); } void SetPropertyCallback(dbus::MethodCall* method_call, dbus::ExportedObject::ResponseSender response_sender, ui::ozone::mojom::SetGesturePropErrorCode error) { std::string error_message; switch (error) { case ui::ozone::mojom::SetGesturePropErrorCode::SUCCESS: std::move(response_sender) .Run(dbus::Response::FromMethodCall(method_call)); return; case ui::ozone::mojom::SetGesturePropErrorCode::NOT_FOUND: error_message = "The device ID or property name specified was not found."; break; case ui::ozone::mojom::SetGesturePropErrorCode::READ_ONLY: error_message = "That property is read-only."; break; case ui::ozone::mojom::SetGesturePropErrorCode::TYPE_MISMATCH: error_message = "The property is of a different type than the value(s) " "provided."; break; case ui::ozone::mojom::SetGesturePropErrorCode::SIZE_MISMATCH: error_message = "The property has a different number of values to that " "provided."; break; case ui::ozone::mojom::SetGesturePropErrorCode::UNKNOWN_ERROR: default: error_message = "An unknown error occurred."; break; } LOG(ERROR) << "SetProperty error: " << error_message; std::move(response_sender) .Run(dbus::ErrorResponse::FromMethodCall( method_call, DBUS_ERROR_INVALID_ARGS, error_message)); } void ListDevicesCallback(std::unique_ptr response, dbus::ExportedObject::ResponseSender response_sender, const base::flat_map& result) { dbus::MessageWriter writer(response.get()); writer.AppendUint32(result.size()); dbus::MessageWriter dict_writer(nullptr); writer.OpenArray("{is}", &dict_writer); for (const auto& pair : result) { dbus::MessageWriter dict_entry_writer(nullptr); dict_writer.OpenDictEntry(&dict_entry_writer); dict_entry_writer.AppendInt32(pair.first); dict_entry_writer.AppendString(pair.second); dict_writer.CloseContainer(&dict_entry_writer); } writer.CloseContainer(&dict_writer); std::move(response_sender).Run(std::move(response)); } void ListPropertiesCallback( std::unique_ptr response, dbus::ExportedObject::ResponseSender response_sender, const std::vector& result) { dbus::MessageWriter writer(response.get()); writer.AppendUint32(result.size()); writer.AppendArrayOfStrings(result); std::move(response_sender).Run(std::move(response)); } } // namespace void GesturePropertiesServiceProvider::ListDevices( dbus::MethodCall* method_call, dbus::ExportedObject::ResponseSender response_sender) { std::unique_ptr response = dbus::Response::FromMethodCall(method_call); GetService()->ListDevices(base::BindOnce( &ListDevicesCallback, std::move(response), std::move(response_sender))); } void GesturePropertiesServiceProvider::ListProperties( dbus::MethodCall* method_call, dbus::ExportedObject::ResponseSender response_sender) { dbus::MessageReader reader(method_call); int32_t device_id; if (!reader.PopInt32(&device_id)) { std::move(response_sender) .Run(dbus::ErrorResponse::FromMethodCall( method_call, DBUS_ERROR_INVALID_ARGS, "The device ID (int32) is missing.")); return; } std::unique_ptr response = dbus::Response::FromMethodCall(method_call); GetService()->ListProperties( device_id, base::BindOnce(&ListPropertiesCallback, std::move(response), std::move(response_sender))); } void GesturePropertiesServiceProvider::GetProperty( dbus::MethodCall* method_call, dbus::ExportedObject::ResponseSender response_sender) { dbus::MessageReader reader(method_call); int32_t device_id; std::string property_name; if (!reader.PopInt32(&device_id) || !reader.PopString(&property_name)) { std::move(response_sender) .Run(dbus::ErrorResponse::FromMethodCall( method_call, DBUS_ERROR_INVALID_ARGS, "The device ID (int32) and/or property name (string) is missing.")); return; } std::unique_ptr response = dbus::Response::FromMethodCall(method_call); GetService()->GetProperty( device_id, property_name, base::BindOnce(&GetPropertyCallback, method_call, std::move(response), std::move(response_sender))); } static ui::ozone::mojom::GesturePropValuePtr GesturePropValueFromVariant( dbus::MessageReader* variant_reader, std::string* error_message) { std::string string_value; if (variant_reader->PopString(&string_value)) { return ui::ozone::mojom::GesturePropValue::NewStr(string_value); } dbus::MessageReader array_reader(nullptr); if (!variant_reader->PopArray(&array_reader)) { *error_message = "Value(s) should be specified either as a string or an " "array of the appropriate type."; return nullptr; } switch (array_reader.GetDataType()) { case dbus::Message::DataType::INT32: { std::vector values = {}; int32_t value; while (array_reader.PopInt32(&value)) { values.push_back(value); } return ui::ozone::mojom::GesturePropValue::NewInts(values); } case dbus::Message::DataType::INT16: { std::vector values = {}; int16_t value; while (array_reader.PopInt16(&value)) { values.push_back(value); } return ui::ozone::mojom::GesturePropValue::NewShorts(values); } case dbus::Message::DataType::BOOL: { std::vector values = {}; bool value; while (array_reader.PopBool(&value)) { values.push_back(value); } return ui::ozone::mojom::GesturePropValue::NewBools(values); } case dbus::Message::DataType::DOUBLE: { std::vector values = {}; double value; while (array_reader.PopDouble(&value)) { values.push_back(value); } return ui::ozone::mojom::GesturePropValue::NewReals(values); } case dbus::Message::DataType::STRING: { *error_message = "String properties can only have one value, and so " "should not be specified as arrays."; return nullptr; } default: { *error_message = "Unsupported D-Bus value type; supported types are " "int32, int16, bool, double, and string."; return nullptr; } } } void GesturePropertiesServiceProvider::SetProperty( dbus::MethodCall* method_call, dbus::ExportedObject::ResponseSender response_sender) { dbus::MessageReader reader(method_call); int32_t device_id; std::string property_name; if (!reader.PopInt32(&device_id) || !reader.PopString(&property_name)) { std::move(response_sender) .Run(dbus::ErrorResponse::FromMethodCall( method_call, DBUS_ERROR_INVALID_ARGS, "The device ID (int32) and/or property name (string) is missing.")); return; } if (!reader.HasMoreData()) { std::move(response_sender) .Run(dbus::ErrorResponse::FromMethodCall( method_call, DBUS_ERROR_INVALID_ARGS, "No value(s) specified.")); return; } std::string error_message; ui::ozone::mojom::GesturePropValuePtr values = GesturePropValueFromVariant(&reader, &error_message); if (values.is_null()) { std::move(response_sender) .Run(dbus::ErrorResponse::FromMethodCall( method_call, DBUS_ERROR_INVALID_ARGS, error_message)); return; } GetService()->SetProperty(device_id, property_name, std::move(values), base::BindOnce(&SetPropertyCallback, method_call, std::move(response_sender))); } ui::ozone::mojom::GesturePropertiesService* GesturePropertiesServiceProvider::GetService() { if (service_for_test_ != nullptr) return service_for_test_; if (!service_.is_bound()) { ui::OzonePlatform::GetInstance() ->GetInputController() ->GetGesturePropertiesService(service_.BindNewPipeAndPassReceiver()); } return service_.get(); } } // namespace ash