blob: 7d6e6a7a181359302f313afe42249f5f8f04e07d [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// 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 <memory>
#include <utility>
#include "base/functional/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<dbus::ExportedObject> 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<dbus::Response> 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());
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());
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<dbus::Response> response,
dbus::ExportedObject::ResponseSender response_sender,
const base::flat_map<int, std::string>& 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<dbus::Response> response,
dbus::ExportedObject::ResponseSender response_sender,
const std::vector<std::string>& 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<dbus::Response> 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<dbus::Response> 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<dbus::Response> 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<int32_t> 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<int16_t> 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<bool> 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<double> 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