Commit 028f593d authored by Tom Anderson's avatar Tom Anderson Committed by Commit Bot

MPRIS: Refactor to use DBusPropertiesInterface

BUG=992948
R=steimel,thestig,hashimoto

Change-Id: I39cb1737c914ecb37065f544538113f9b2db7efc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1778584Reviewed-by: default avatarRyo Hashimoto <hashimoto@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#695159}
parent 439a06bc
...@@ -17,9 +17,9 @@ ...@@ -17,9 +17,9 @@
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "components/dbus/menu/menu.h" #include "components/dbus/menu/menu.h"
#include "components/dbus/menu/properties_interface.h" #include "components/dbus/properties/dbus_properties.h"
#include "components/dbus/menu/success_barrier_callback.h" #include "components/dbus/properties/success_barrier_callback.h"
#include "components/dbus/menu/types.h" #include "components/dbus/properties/types.h"
#include "components/dbus/thread_linux/dbus_thread_linux.h" #include "components/dbus/thread_linux/dbus_thread_linux.h"
#include "dbus/bus.h" #include "dbus/bus.h"
#include "dbus/exported_object.h" #include "dbus/exported_object.h"
...@@ -279,7 +279,7 @@ void StatusIconLinuxDbus::OnOwnership(const std::string& service_name, ...@@ -279,7 +279,7 @@ void StatusIconLinuxDbus::OnOwnership(const std::string& service_name,
bus_->GetExportedObject(dbus::ObjectPath(kPathDbusMenu)), barrier_); bus_->GetExportedObject(dbus::ObjectPath(kPathDbusMenu)), barrier_);
UpdateMenuImpl(delegate_->GetMenuModel(), false); UpdateMenuImpl(delegate_->GetMenuModel(), false);
properties_ = std::make_unique<DbusPropertiesInterface>(item_, barrier_); properties_ = std::make_unique<DbusProperties>(item_, barrier_);
properties_->RegisterInterface(kInterfaceStatusNotifierItem); properties_->RegisterInterface(kInterfaceStatusNotifierItem);
auto set_property = [&](const std::string& property_name, auto&& value) { auto set_property = [&](const std::string& property_name, auto&& value) {
properties_->SetProperty(kInterfaceStatusNotifierItem, property_name, properties_->SetProperty(kInterfaceStatusNotifierItem, property_name,
......
...@@ -24,7 +24,7 @@ class ImageSkia; ...@@ -24,7 +24,7 @@ class ImageSkia;
} // namespace gfx } // namespace gfx
class DbusMenu; class DbusMenu;
class DbusPropertiesInterface; class DbusProperties;
// A status icon following the StatusNotifierItem specification. // A status icon following the StatusNotifierItem specification.
// https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem/ // https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem/
...@@ -93,7 +93,7 @@ class StatusIconLinuxDbus : public views::StatusIconLinux, ...@@ -93,7 +93,7 @@ class StatusIconLinuxDbus : public views::StatusIconLinux,
base::RepeatingCallback<void(bool)> barrier_; base::RepeatingCallback<void(bool)> barrier_;
std::unique_ptr<DbusPropertiesInterface> properties_; std::unique_ptr<DbusProperties> properties_;
std::unique_ptr<DbusMenu> menu_; std::unique_ptr<DbusMenu> menu_;
// A menu that contains the click action (if there is a click action) and a // A menu that contains the click action (if there is a click action) and a
......
...@@ -387,7 +387,10 @@ test("components_unittests") { ...@@ -387,7 +387,10 @@ test("components_unittests") {
} }
if (use_dbus) { if (use_dbus) {
deps += [ "//components/dbus/menu:unit_tests" ] deps += [
"//components/dbus/menu:unit_tests",
"//components/dbus/properties:unit_tests",
]
} }
if (enable_plugins) { if (enable_plugins) {
......
...@@ -10,12 +10,6 @@ component("menu") { ...@@ -10,12 +10,6 @@ component("menu") {
"menu.h", "menu.h",
"menu_property_list.cc", "menu_property_list.cc",
"menu_property_list.h", "menu_property_list.h",
"properties_interface.cc",
"properties_interface.h",
"success_barrier_callback.cc",
"success_barrier_callback.h",
"types.cc",
"types.h",
] ]
defines = [ "IS_DBUS_IMPL" ] defines = [ "IS_DBUS_IMPL" ]
deps = [ deps = [
...@@ -23,6 +17,7 @@ component("menu") { ...@@ -23,6 +17,7 @@ component("menu") {
"//base:i18n", "//base:i18n",
] ]
public_deps = [ public_deps = [
"//components/dbus/properties",
"//dbus", "//dbus",
"//skia", "//skia",
"//ui/base", "//ui/base",
...@@ -37,8 +32,6 @@ source_set("unit_tests") { ...@@ -37,8 +32,6 @@ source_set("unit_tests") {
testonly = true testonly = true
sources = [ sources = [
"menu_property_list_unittest.cc", "menu_property_list_unittest.cc",
"success_barrier_callback_unittest.cc",
"types_unittest.cc",
] ]
deps = [ deps = [
":menu", ":menu",
......
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "components/dbus/menu/properties_interface.h" #include "components/dbus/properties/dbus_properties.h"
#include "components/dbus/menu/success_barrier_callback.h" #include "components/dbus/properties/success_barrier_callback.h"
#include "ui/base/accelerators/accelerator.h" #include "ui/base/accelerators/accelerator.h"
#include "ui/base/models/menu_model.h" #include "ui/base/models/menu_model.h"
#include "ui/base/models/simple_menu_model.h" #include "ui/base/models/simple_menu_model.h"
...@@ -163,7 +163,7 @@ DbusMenu::DbusMenu(dbus::ExportedObject* exported_object, ...@@ -163,7 +163,7 @@ DbusMenu::DbusMenu(dbus::ExportedObject* exported_object,
base::BindRepeating(&DbusMenu::OnExported, weak_factory_.GetWeakPtr())); base::BindRepeating(&DbusMenu::OnExported, weak_factory_.GetWeakPtr()));
} }
properties_ = std::make_unique<DbusPropertiesInterface>(menu_, barrier_); properties_ = std::make_unique<DbusProperties>(menu_, barrier_);
properties_->RegisterInterface(kInterfaceDbusMenu); properties_->RegisterInterface(kInterfaceDbusMenu);
auto set_property = [&](const std::string& property_name, auto&& value) { auto set_property = [&](const std::string& property_name, auto&& value) {
properties_->SetProperty(kInterfaceDbusMenu, property_name, properties_->SetProperty(kInterfaceDbusMenu, property_name,
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "components/dbus/menu/menu_property_list.h" #include "components/dbus/menu/menu_property_list.h"
#include "components/dbus/menu/types.h" #include "components/dbus/properties/types.h"
#include "dbus/bus.h" #include "dbus/bus.h"
#include "dbus/exported_object.h" #include "dbus/exported_object.h"
#include "dbus/message.h" #include "dbus/message.h"
...@@ -25,7 +25,7 @@ namespace ui { ...@@ -25,7 +25,7 @@ namespace ui {
class MenuModel; class MenuModel;
} }
class DbusPropertiesInterface; class DbusProperties;
// Implements the com.canonical.dbusmenu interface. // Implements the com.canonical.dbusmenu interface.
class COMPONENT_EXPORT(DBUS) DbusMenu { class COMPONENT_EXPORT(DBUS) DbusMenu {
...@@ -152,7 +152,7 @@ class COMPONENT_EXPORT(DBUS) DbusMenu { ...@@ -152,7 +152,7 @@ class COMPONENT_EXPORT(DBUS) DbusMenu {
base::RepeatingCallback<void(bool)> barrier_; base::RepeatingCallback<void(bool)> barrier_;
std::unique_ptr<DbusPropertiesInterface> properties_; std::unique_ptr<DbusProperties> properties_;
uint32_t revision_ = 0; uint32_t revision_ = 0;
int32_t last_item_id_ = 0; int32_t last_item_id_ = 0;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "components/dbus/menu/types.h" #include "components/dbus/properties/types.h"
using MenuPropertyList = std::vector<std::string>; using MenuPropertyList = std::vector<std::string>;
using MenuItemProperties = std::map<std::string, DbusVariant>; using MenuItemProperties = std::map<std::string, DbusVariant>;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include <memory> #include <memory>
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "components/dbus/menu/types.h" #include "components/dbus/properties/types.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/accelerators/accelerator.h" #include "ui/base/accelerators/accelerator.h"
#include "ui/base/models/menu_model.h" #include "ui/base/models/menu_model.h"
......
# 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.
component("properties") {
sources = [
"dbus_properties.cc",
"dbus_properties.h",
"success_barrier_callback.cc",
"success_barrier_callback.h",
"types.cc",
"types.h",
]
defines = [ "IS_DBUS_IMPL" ]
deps = [
"//base",
"//base:i18n",
]
public_deps = [
"//dbus",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"success_barrier_callback_unittest.cc",
"types_unittest.cc",
]
deps = [
":properties",
"//base",
"//testing/gtest",
]
}
include_rules = [
"+dbus",
]
thestig@chromium.org
thomasanderson@chromium.org
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "components/dbus/menu/properties_interface.h" #include "components/dbus/properties/dbus_properties.h"
#include <dbus/dbus-shared.h> #include <dbus/dbus-shared.h>
#include "base/bind.h" #include "base/bind.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "components/dbus/menu/success_barrier_callback.h" #include "components/dbus/properties/success_barrier_callback.h"
#include "dbus/exported_object.h" #include "dbus/exported_object.h"
#include "dbus/message.h" #include "dbus/message.h"
...@@ -24,19 +24,17 @@ const char kSignalPropertiesChanged[] = "PropertiesChanged"; ...@@ -24,19 +24,17 @@ const char kSignalPropertiesChanged[] = "PropertiesChanged";
} // namespace } // namespace
DbusPropertiesInterface::DbusPropertiesInterface( DbusProperties::DbusProperties(dbus::ExportedObject* exported_object,
dbus::ExportedObject* exported_object, InitializedCallback callback)
InitializedCallback callback)
: exported_object_(exported_object) { : exported_object_(exported_object) {
static constexpr struct { static constexpr struct {
const char* name; const char* name;
void (DbusPropertiesInterface::*callback)( void (DbusProperties::*callback)(dbus::MethodCall*,
dbus::MethodCall*, dbus::ExportedObject::ResponseSender);
dbus::ExportedObject::ResponseSender);
} methods[3] = { } methods[3] = {
{kMethodPropertiesGetAll, &DbusPropertiesInterface::OnGetAllProperties}, {kMethodPropertiesGetAll, &DbusProperties::OnGetAllProperties},
{kMethodPropertiesGet, &DbusPropertiesInterface::OnGetProperty}, {kMethodPropertiesGet, &DbusProperties::OnGetProperty},
{kMethodPropertiesSet, &DbusPropertiesInterface::OnSetProperty}, {kMethodPropertiesSet, &DbusProperties::OnSetProperty},
}; };
barrier_ = SuccessBarrierCallback(base::size(methods), std::move(callback)); barrier_ = SuccessBarrierCallback(base::size(methods), std::move(callback));
...@@ -44,27 +42,76 @@ DbusPropertiesInterface::DbusPropertiesInterface( ...@@ -44,27 +42,76 @@ DbusPropertiesInterface::DbusPropertiesInterface(
exported_object_->ExportMethod( exported_object_->ExportMethod(
DBUS_INTERFACE_PROPERTIES, method.name, DBUS_INTERFACE_PROPERTIES, method.name,
base::BindRepeating(method.callback, weak_factory_.GetWeakPtr()), base::BindRepeating(method.callback, weak_factory_.GetWeakPtr()),
base::BindRepeating(&DbusPropertiesInterface::OnExported, base::BindRepeating(&DbusProperties::OnExported,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }
} }
DbusPropertiesInterface::~DbusPropertiesInterface() = default; DbusProperties::~DbusProperties() = default;
void DbusPropertiesInterface::RegisterInterface(const std::string& interface) { void DbusProperties::RegisterInterface(const std::string& interface) {
bool inserted = bool inserted =
properties_.emplace(interface, std::map<std::string, DbusVariant>{}) properties_.emplace(interface, std::map<std::string, DbusVariant>{})
.second; .second;
DCHECK(inserted); DCHECK(inserted);
} }
void DbusPropertiesInterface::OnExported(const std::string& interface_name, DbusVariant* DbusProperties::GetProperty(const std::string& interface,
const std::string& method_name, const std::string& property_name) {
bool success) { auto interface_it = properties_.find(interface);
if (interface_it == properties_.end())
return nullptr;
auto name_it = interface_it->second.find(property_name);
return name_it != interface_it->second.end() ? &name_it->second : nullptr;
}
void DbusProperties::PropertyUpdated(const std::string& interface,
const std::string& property_name,
bool send_change) {
if (!initialized_)
return;
// |signal| follows the PropertiesChanged API:
// org.freedesktop.DBus.Properties.PropertiesChanged(
// STRING interface_name,
// DICT<STRING,VARIANT> changed_properties,
// ARRAY<STRING> invalidated_properties);
dbus::Signal signal(DBUS_INTERFACE_PROPERTIES, kSignalPropertiesChanged);
dbus::MessageWriter writer(&signal);
writer.AppendString(interface);
if (send_change) {
// Changed properties.
dbus::MessageWriter array_writer(nullptr);
writer.OpenArray("{sv}", &array_writer);
dbus::MessageWriter dict_entry_writer(nullptr);
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString(property_name);
properties_[interface][property_name].Write(&dict_entry_writer);
array_writer.CloseContainer(&dict_entry_writer);
writer.CloseContainer(&array_writer);
// Invalidated properties.
writer.AppendArrayOfStrings({});
} else {
// Changed properties.
DbusDictionary().Write(&writer);
// Invalidated properties.
writer.AppendArrayOfStrings({property_name});
}
exported_object_->SendSignal(&signal);
}
void DbusProperties::OnExported(const std::string& interface_name,
const std::string& method_name,
bool success) {
initialized_ = success;
barrier_.Run(success); barrier_.Run(success);
} }
void DbusPropertiesInterface::OnGetAllProperties( void DbusProperties::OnGetAllProperties(
dbus::MethodCall* method_call, dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) { dbus::ExportedObject::ResponseSender response_sender) {
// org.freedesktop.DBus.Properties.GetAll(in STRING interface_name, // org.freedesktop.DBus.Properties.GetAll(in STRING interface_name,
...@@ -93,7 +140,7 @@ void DbusPropertiesInterface::OnGetAllProperties( ...@@ -93,7 +140,7 @@ void DbusPropertiesInterface::OnGetAllProperties(
writer.CloseContainer(&array_writer); writer.CloseContainer(&array_writer);
} else if (interface == DBUS_INTERFACE_PROPERTIES) { } else if (interface == DBUS_INTERFACE_PROPERTIES) {
// There are no properties to give for this interface. // There are no properties to give for this interface.
DbusArray<DbusDictEntry<DbusString, DbusVariant>>().Write(&writer); DbusDictionary().Write(&writer);
} else { } else {
// The given interface is not supported, so return a null response. // The given interface is not supported, so return a null response.
response = nullptr; response = nullptr;
...@@ -102,7 +149,7 @@ void DbusPropertiesInterface::OnGetAllProperties( ...@@ -102,7 +149,7 @@ void DbusPropertiesInterface::OnGetAllProperties(
response_sender.Run(std::move(response)); response_sender.Run(std::move(response));
} }
void DbusPropertiesInterface::OnGetProperty( void DbusProperties::OnGetProperty(
dbus::MethodCall* method_call, dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) { dbus::ExportedObject::ResponseSender response_sender) {
// org.freedesktop.DBus.Properties.Get(in STRING interface_name, // org.freedesktop.DBus.Properties.Get(in STRING interface_name,
...@@ -125,47 +172,10 @@ void DbusPropertiesInterface::OnGetProperty( ...@@ -125,47 +172,10 @@ void DbusPropertiesInterface::OnGetProperty(
response_sender.Run(std::move(response)); response_sender.Run(std::move(response));
} }
void DbusPropertiesInterface::OnSetProperty( void DbusProperties::OnSetProperty(
dbus::MethodCall* method_call, dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) { dbus::ExportedObject::ResponseSender response_sender) {
// Not needed for now. // Not needed for now.
NOTIMPLEMENTED(); NOTIMPLEMENTED();
response_sender.Run(dbus::Response::FromMethodCall(method_call)); response_sender.Run(dbus::Response::FromMethodCall(method_call));
} }
void DbusPropertiesInterface::EmitPropertiesChangedSignal(
const std::string& interface,
const std::string& property_name,
bool send_change) {
// |signal| follows the PropertiesChanged API:
// org.freedesktop.DBus.Properties.PropertiesChanged(
// STRING interface_name,
// DICT<STRING,VARIANT> changed_properties,
// ARRAY<STRING> invalidated_properties);
dbus::Signal signal(DBUS_INTERFACE_PROPERTIES, kSignalPropertiesChanged);
dbus::MessageWriter writer(&signal);
writer.AppendString(interface);
if (send_change) {
// Changed properties.
dbus::MessageWriter array_writer(nullptr);
writer.OpenArray("{sv}", &array_writer);
dbus::MessageWriter dict_entry_writer(nullptr);
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString(property_name);
properties_[interface][property_name].Write(&dict_entry_writer);
array_writer.CloseContainer(&dict_entry_writer);
writer.CloseContainer(&array_writer);
// Invalidated properties.
writer.AppendArrayOfStrings({});
} else {
// Changed properties.
DbusArray<DbusDictEntry<DbusString, DbusVariant>>().Write(&writer);
// Invalidated properties.
writer.AppendArrayOfStrings({property_name});
}
exported_object_->SendSignal(&signal);
}
...@@ -2,28 +2,28 @@ ...@@ -2,28 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef COMPONENTS_DBUS_MENU_PROPERTIES_INTERFACE_H_ #ifndef COMPONENTS_DBUS_PROPERTIES_DBUS_PROPERTIES_H_
#define COMPONENTS_DBUS_MENU_PROPERTIES_INTERFACE_H_ #define COMPONENTS_DBUS_PROPERTIES_DBUS_PROPERTIES_H_
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/component_export.h" #include "base/component_export.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "components/dbus/menu/types.h" #include "components/dbus/properties/types.h"
#include "dbus/bus.h" #include "dbus/bus.h"
#include "dbus/exported_object.h" #include "dbus/exported_object.h"
// https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties // https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
class COMPONENT_EXPORT(DBUS) DbusPropertiesInterface { class COMPONENT_EXPORT(DBUS) DbusProperties {
public: public:
using InitializedCallback = base::OnceCallback<void(bool success)>; using InitializedCallback = base::OnceCallback<void(bool success)>;
// Registers method handlers for the properties interface. The handlers will // Registers method handlers for the properties interface. The handlers will
// not be removed until the bus is shut down. // not be removed until the bus is shut down.
DbusPropertiesInterface(dbus::ExportedObject* exported_object, DbusProperties(dbus::ExportedObject* exported_object,
InitializedCallback callback); InitializedCallback callback);
~DbusPropertiesInterface(); ~DbusProperties();
void RegisterInterface(const std::string& interface); void RegisterInterface(const std::string& interface);
...@@ -33,13 +33,28 @@ class COMPONENT_EXPORT(DBUS) DbusPropertiesInterface { ...@@ -33,13 +33,28 @@ class COMPONENT_EXPORT(DBUS) DbusPropertiesInterface {
T&& value, T&& value,
bool emit_signal = true, bool emit_signal = true,
bool send_change = true) { bool send_change = true) {
auto it = properties_.find(interface); auto interface_it = properties_.find(interface);
DCHECK(it != properties_.end()); DCHECK(interface_it != properties_.end());
(it->second)[name] = MakeDbusVariant(std::move(value)); auto property_it = interface_it->second.find(name);
if (emit_signal) DbusVariant new_value = MakeDbusVariant(std::move(value));
EmitPropertiesChangedSignal(interface, name, send_change); const bool send_signal =
emit_signal && (property_it == interface_it->second.end() ||
property_it->second != new_value);
(interface_it->second)[name] = std::move(new_value);
if (send_signal)
PropertyUpdated(interface, name, send_change);
} }
DbusVariant* GetProperty(const std::string& interface,
const std::string& property_name);
// If emitting a PropertiesChangedSignal is desired, this should be called
// after an existing property is modified through any means other than
// SetProperty().
void PropertyUpdated(const std::string& interface,
const std::string& property_name,
bool send_change = true);
private: private:
void OnExported(const std::string& interface_name, void OnExported(const std::string& interface_name,
const std::string& method_name, const std::string& method_name,
...@@ -52,9 +67,7 @@ class COMPONENT_EXPORT(DBUS) DbusPropertiesInterface { ...@@ -52,9 +67,7 @@ class COMPONENT_EXPORT(DBUS) DbusPropertiesInterface {
void OnSetProperty(dbus::MethodCall* method_call, void OnSetProperty(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender); dbus::ExportedObject::ResponseSender response_sender);
void EmitPropertiesChangedSignal(const std::string& interface, bool initialized_ = false;
const std::string& property_name,
bool send_change);
dbus::ExportedObject* exported_object_ = nullptr; dbus::ExportedObject* exported_object_ = nullptr;
...@@ -64,9 +77,9 @@ class COMPONENT_EXPORT(DBUS) DbusPropertiesInterface { ...@@ -64,9 +77,9 @@ class COMPONENT_EXPORT(DBUS) DbusPropertiesInterface {
// from property name to property value. // from property name to property value.
std::map<std::string, std::map<std::string, DbusVariant>> properties_; std::map<std::string, std::map<std::string, DbusVariant>> properties_;
base::WeakPtrFactory<DbusPropertiesInterface> weak_factory_{this}; base::WeakPtrFactory<DbusProperties> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(DbusPropertiesInterface); DISALLOW_COPY_AND_ASSIGN(DbusProperties);
}; };
#endif // COMPONENTS_DBUS_MENU_PROPERTIES_INTERFACE_H_ #endif // COMPONENTS_DBUS_PROPERTIES_DBUS_PROPERTIES_H_
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "components/dbus/menu/success_barrier_callback.h" #include "components/dbus/properties/success_barrier_callback.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef COMPONENTS_DBUS_MENU_SUCCESS_BARRIER_CALLBACK_H_ #ifndef COMPONENTS_DBUS_PROPERTIES_SUCCESS_BARRIER_CALLBACK_H_
#define COMPONENTS_DBUS_MENU_SUCCESS_BARRIER_CALLBACK_H_ #define COMPONENTS_DBUS_PROPERTIES_SUCCESS_BARRIER_CALLBACK_H_
#include <cstddef> #include <cstddef>
...@@ -20,4 +20,4 @@ base::RepeatingCallback<void(bool)> SuccessBarrierCallback( ...@@ -20,4 +20,4 @@ base::RepeatingCallback<void(bool)> SuccessBarrierCallback(
size_t num_calls, size_t num_calls,
base::OnceCallback<void(bool)> done_callback); base::OnceCallback<void(bool)> done_callback);
#endif // COMPONENTS_DBUS_MENU_SUCCESS_BARRIER_CALLBACK_H_ #endif // COMPONENTS_DBUS_PROPERTIES_SUCCESS_BARRIER_CALLBACK_H_
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "components/dbus/menu/success_barrier_callback.h" #include "components/dbus/properties/success_barrier_callback.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "components/dbus/menu/types.h" #include "components/dbus/properties/types.h"
#include "dbus/message.h" #include "dbus/message.h"
#include "dbus/object_path.h" #include "dbus/object_path.h"
...@@ -57,6 +57,32 @@ std::string DbusUint32::GetSignature() { ...@@ -57,6 +57,32 @@ std::string DbusUint32::GetSignature() {
return "u"; return "u";
} }
DbusInt64::DbusInt64(int64_t value) : value_(value) {}
DbusInt64::DbusInt64(DbusInt64&& other) = default;
DbusInt64::~DbusInt64() = default;
void DbusInt64::Write(dbus::MessageWriter* writer) const {
writer->AppendInt64(value_);
}
// static
std::string DbusInt64::GetSignature() {
return "x";
}
DbusDouble::DbusDouble(double value) : value_(value) {}
DbusDouble::DbusDouble(DbusDouble&& other) = default;
DbusDouble::~DbusDouble() = default;
void DbusDouble::Write(dbus::MessageWriter* writer) const {
writer->AppendDouble(value_);
}
// static
std::string DbusDouble::GetSignature() {
return "d";
}
DbusString::DbusString(const std::string& value) : value_(value) {} DbusString::DbusString(const std::string& value) : value_(value) {}
DbusString::DbusString(DbusString&& other) = default; DbusString::DbusString(DbusString&& other) = default;
DbusString::~DbusString() = default; DbusString::~DbusString() = default;
...@@ -131,3 +157,32 @@ void DbusByteArray::Write(dbus::MessageWriter* writer) const { ...@@ -131,3 +157,32 @@ void DbusByteArray::Write(dbus::MessageWriter* writer) const {
std::string DbusByteArray::GetSignature() { std::string DbusByteArray::GetSignature() {
return "ay"; // lmao return "ay"; // lmao
} }
DbusDictionary::DbusDictionary() = default;
DbusDictionary::DbusDictionary(DbusDictionary&& other) = default;
DbusDictionary::~DbusDictionary() = default;
bool DbusDictionary::Put(const std::string& key, DbusVariant&& value) {
auto it = value_.find(key);
const bool updated = it == value_.end() || it->second != value;
value_[key] = std::move(value);
return updated;
}
void DbusDictionary::Write(dbus::MessageWriter* writer) const {
dbus::MessageWriter array_writer(nullptr);
writer->OpenArray("{sv}", &array_writer);
for (const auto& pair : value_) {
dbus::MessageWriter dict_entry_writer(nullptr);
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString(pair.first);
pair.second.Write(&dict_entry_writer);
array_writer.CloseContainer(&dict_entry_writer);
}
writer->CloseContainer(&array_writer);
}
// static
std::string DbusDictionary::GetSignature() {
return "a{sv}";
}
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef COMPONENTS_DBUS_MENU_TYPES_H_ #ifndef COMPONENTS_DBUS_PROPERTIES_TYPES_H_
#define COMPONENTS_DBUS_MENU_TYPES_H_ #define COMPONENTS_DBUS_PROPERTIES_TYPES_H_
#include <stdint.h> #include <stdint.h>
...@@ -150,6 +150,40 @@ class COMPONENT_EXPORT(DBUS) DbusUint32 : public DbusTypeImpl<DbusUint32> { ...@@ -150,6 +150,40 @@ class COMPONENT_EXPORT(DBUS) DbusUint32 : public DbusTypeImpl<DbusUint32> {
uint32_t value_; uint32_t value_;
}; };
class COMPONENT_EXPORT(DBUS) DbusInt64 : public DbusTypeImpl<DbusInt64> {
public:
explicit DbusInt64(int64_t value);
DbusInt64(DbusInt64&& other);
~DbusInt64() override;
// DbusType:
void Write(dbus::MessageWriter* writer) const override;
static std::string GetSignature();
private:
friend class DbusTypeImpl<DbusInt64>;
int64_t value_;
};
class COMPONENT_EXPORT(DBUS) DbusDouble : public DbusTypeImpl<DbusDouble> {
public:
explicit DbusDouble(double value);
DbusDouble(DbusDouble&& other);
~DbusDouble() override;
// DbusType:
void Write(dbus::MessageWriter* writer) const override;
static std::string GetSignature();
private:
friend class DbusTypeImpl<DbusDouble>;
double value_;
};
class COMPONENT_EXPORT(DBUS) DbusString : public DbusTypeImpl<DbusString> { class COMPONENT_EXPORT(DBUS) DbusString : public DbusTypeImpl<DbusString> {
public: public:
explicit DbusString(const std::string& value); explicit DbusString(const std::string& value);
...@@ -192,6 +226,13 @@ class COMPONENT_EXPORT(DBUS) DbusVariant : public DbusTypeImpl<DbusVariant> { ...@@ -192,6 +226,13 @@ class COMPONENT_EXPORT(DBUS) DbusVariant : public DbusTypeImpl<DbusVariant> {
DbusVariant(DbusVariant&& other); DbusVariant(DbusVariant&& other);
~DbusVariant() override; ~DbusVariant() override;
template <typename T>
T* GetAs() {
return value_ && value_->GetSignatureDynamic() == T::GetSignature()
? reinterpret_cast<T*>(value_.get())
: nullptr;
}
DbusVariant& operator=(DbusVariant&& other); DbusVariant& operator=(DbusVariant&& other);
explicit operator bool() const; explicit operator bool() const;
...@@ -337,4 +378,31 @@ auto MakeDbusDictEntry(K&& k, V&& v) { ...@@ -337,4 +378,31 @@ auto MakeDbusDictEntry(K&& k, V&& v) {
return DbusDictEntry<K, V>(std::move(k), std::move(v)); return DbusDictEntry<K, V>(std::move(k), std::move(v));
} }
#endif // COMPONENTS_DBUS_MENU_TYPES_H_ // A convenience class for DbusArray<DbusDictEntry<DbusString, DbusVariant>>,
// which is a common idiom for DBus APIs. Except this class has some subtle
// differences:
// 1. Duplicate keys are not allowed.
// 2. You cannot control the ordering of keys. They will always be in sorted
// order.
class COMPONENT_EXPORT(DBUS) DbusDictionary
: public DbusTypeImpl<DbusDictionary> {
public:
DbusDictionary();
DbusDictionary(DbusDictionary&& other);
~DbusDictionary() override;
// Returns true iff the value corresponding to |key| was updated.
bool Put(const std::string& key, DbusVariant&& value);
// DbusType:
void Write(dbus::MessageWriter* writer) const override;
static std::string GetSignature();
private:
friend class DbusTypeImpl<DbusDictionary>;
std::map<std::string, DbusVariant> value_;
};
#endif // COMPONENTS_DBUS_PROPERTIES_TYPES_H_
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "components/dbus/menu/types.h" #include "components/dbus/properties/types.h"
#include "base/memory/ref_counted_memory.h" #include "base/memory/ref_counted_memory.h"
#include "dbus/object_path.h" #include "dbus/object_path.h"
......
...@@ -16,6 +16,7 @@ component("mpris") { ...@@ -16,6 +16,7 @@ component("mpris") {
deps = [ deps = [
"//base", "//base",
"//build:branding_buildflags", "//build:branding_buildflags",
"//components/dbus/properties",
"//components/dbus/thread_linux", "//components/dbus/thread_linux",
"//dbus", "//dbus",
] ]
......
...@@ -9,25 +9,27 @@ ...@@ -9,25 +9,27 @@
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/singleton.h"
#include "base/process/process.h" #include "base/process/process.h"
#include "base/unguessable_token.h" #include "base/strings/string_number_conversions.h"
#include "base/values.h" #include "base/strings/utf_string_conversions.h"
#include "build/branding_buildflags.h" #include "build/branding_buildflags.h"
#include "components/dbus/properties/dbus_properties.h"
#include "components/dbus/properties/success_barrier_callback.h"
#include "components/dbus/thread_linux/dbus_thread_linux.h" #include "components/dbus/thread_linux/dbus_thread_linux.h"
#include "dbus/bus.h" #include "dbus/bus.h"
#include "dbus/exported_object.h" #include "dbus/exported_object.h"
#include "dbus/message.h" #include "dbus/message.h"
#include "dbus/object_path.h" #include "dbus/object_path.h"
#include "dbus/property.h" #include "dbus/property.h"
#include "dbus/values_util.h"
#include "ui/base/mpris/mpris_service_observer.h" #include "ui/base/mpris/mpris_service_observer.h"
namespace mpris { namespace mpris {
namespace { namespace {
constexpr int kDebounceTimeoutMilliseconds = 50; constexpr int kNumMethodsToExport = 11;
constexpr int kNumMethodsToExport = 14;
} // namespace } // namespace
...@@ -38,9 +40,7 @@ MprisServiceImpl* MprisServiceImpl::GetInstance() { ...@@ -38,9 +40,7 @@ MprisServiceImpl* MprisServiceImpl::GetInstance() {
MprisServiceImpl::MprisServiceImpl() MprisServiceImpl::MprisServiceImpl()
: service_name_(std::string(kMprisAPIServiceNamePrefix) + : service_name_(std::string(kMprisAPIServiceNamePrefix) +
std::to_string(base::Process::Current().Pid())) { base::NumberToString(base::Process::Current().Pid())) {}
InitializeProperties();
}
MprisServiceImpl::~MprisServiceImpl() { MprisServiceImpl::~MprisServiceImpl() {
if (bus_) { if (bus_) {
...@@ -66,57 +66,54 @@ void MprisServiceImpl::RemoveObserver(MprisServiceObserver* observer) { ...@@ -66,57 +66,54 @@ void MprisServiceImpl::RemoveObserver(MprisServiceObserver* observer) {
} }
void MprisServiceImpl::SetCanGoNext(bool value) { void MprisServiceImpl::SetCanGoNext(bool value) {
SetPropertyInternal(media_player2_player_properties_, "CanGoNext", properties_->SetProperty(kMprisAPIPlayerInterfaceName, "CanGoNext",
base::Value(value)); DbusBoolean(value));
} }
void MprisServiceImpl::SetCanGoPrevious(bool value) { void MprisServiceImpl::SetCanGoPrevious(bool value) {
SetPropertyInternal(media_player2_player_properties_, "CanGoPrevious", properties_->SetProperty(kMprisAPIPlayerInterfaceName, "CanGoPrevious",
base::Value(value)); DbusBoolean(value));
} }
void MprisServiceImpl::SetCanPlay(bool value) { void MprisServiceImpl::SetCanPlay(bool value) {
SetPropertyInternal(media_player2_player_properties_, "CanPlay", properties_->SetProperty(kMprisAPIPlayerInterfaceName, "CanPlay",
base::Value(value)); DbusBoolean(value));
} }
void MprisServiceImpl::SetCanPause(bool value) { void MprisServiceImpl::SetCanPause(bool value) {
SetPropertyInternal(media_player2_player_properties_, "CanPause", properties_->SetProperty(kMprisAPIPlayerInterfaceName, "CanPause",
base::Value(value)); DbusBoolean(value));
} }
void MprisServiceImpl::SetPlaybackStatus(PlaybackStatus value) { void MprisServiceImpl::SetPlaybackStatus(PlaybackStatus value) {
base::Value status; auto status = [&]() {
switch (value) { switch (value) {
case PlaybackStatus::kPlaying: case PlaybackStatus::kPlaying:
status = base::Value("Playing"); return DbusString("Playing");
break; case PlaybackStatus::kPaused:
case PlaybackStatus::kPaused: return DbusString("Paused");
status = base::Value("Paused"); case PlaybackStatus::kStopped:
break; return DbusString("Stopped");
case PlaybackStatus::kStopped: }
status = base::Value("Stopped"); };
break; properties_->SetProperty(kMprisAPIPlayerInterfaceName, "PlaybackStatus",
} status());
SetPropertyInternal(media_player2_player_properties_, "PlaybackStatus",
status);
} }
void MprisServiceImpl::SetTitle(const base::string16& value) { void MprisServiceImpl::SetTitle(const base::string16& value) {
SetMetadataPropertyInternal("xesam:title", base::Value(value)); SetMetadataPropertyInternal(
"xesam:title", MakeDbusVariant(DbusString(base::UTF16ToUTF8(value))));
} }
void MprisServiceImpl::SetArtist(const base::string16& value) { void MprisServiceImpl::SetArtist(const base::string16& value) {
// xesam:artist is actually supposed to be a list of strings, but base::Value SetMetadataPropertyInternal(
// only supports lists of base::Value, which makes this difficult to correctly "xesam:artist",
// propagate to |AddPropertiesToWriter()|. Instead, we'll only track the MakeDbusVariant(MakeDbusArray(DbusString(base::UTF16ToUTF8(value)))));
// string in |media_player2_player_properties_| and special-case within
// |AddPropertiesToWriter()|.
SetMetadataPropertyInternal("xesam:artist", base::Value(value));
} }
void MprisServiceImpl::SetAlbum(const base::string16& value) { void MprisServiceImpl::SetAlbum(const base::string16& value) {
SetMetadataPropertyInternal("xesam:album", base::Value(value)); SetMetadataPropertyInternal(
"xesam:album", MakeDbusVariant(DbusString(base::UTF16ToUTF8(value))));
} }
std::string MprisServiceImpl::GetServiceName() const { std::string MprisServiceImpl::GetServiceName() const {
...@@ -125,35 +122,41 @@ std::string MprisServiceImpl::GetServiceName() const { ...@@ -125,35 +122,41 @@ std::string MprisServiceImpl::GetServiceName() const {
void MprisServiceImpl::InitializeProperties() { void MprisServiceImpl::InitializeProperties() {
// org.mpris.MediaPlayer2 interface properties. // org.mpris.MediaPlayer2 interface properties.
media_player2_properties_["CanQuit"] = base::Value(false); auto set_property = [&](const std::string& property_name, auto&& value) {
media_player2_properties_["CanRaise"] = base::Value(false); properties_->SetProperty(kMprisAPIInterfaceName, property_name,
media_player2_properties_["HasTrackList"] = base::Value(false); std::move(value), false);
};
set_property("CanQuit", DbusBoolean(false));
set_property("CanRaise", DbusBoolean(false));
set_property("HasTrackList", DbusBoolean(false));
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
media_player2_properties_["Identity"] = base::Value("Chrome"); set_property("Identity", DbusString("Chrome"));
#else #else
media_player2_properties_["Identity"] = base::Value("Chromium"); set_property("Identity", DbusString("Chromium"));
#endif #endif
media_player2_properties_["SupportedUriSchemes"] = set_property("SupportedUriSchemes", DbusArray<DbusString>());
base::Value(base::Value::Type::LIST); set_property("SupportedMimeTypes", DbusArray<DbusString>());
media_player2_properties_["SupportedMimeTypes"] =
base::Value(base::Value::Type::LIST);
// org.mpris.MediaPlayer2.Player interface properties. // org.mpris.MediaPlayer2.Player interface properties.
media_player2_player_properties_["PlaybackStatus"] = base::Value("Stopped"); auto set_player_property = [&](const std::string& property_name,
media_player2_player_properties_["Rate"] = base::Value(1.0); auto&& value) {
media_player2_player_properties_["Metadata"] = properties_->SetProperty(kMprisAPIPlayerInterfaceName, property_name,
base::Value(base::Value::DictStorage()); std::move(value), false);
media_player2_player_properties_["Volume"] = base::Value(1.0); };
media_player2_player_properties_["Position"] = base::Value(0); set_player_property("PlaybackStatus", DbusString("Stopped"));
media_player2_player_properties_["MinimumRate"] = base::Value(1.0); set_player_property("Rate", DbusDouble(1.0));
media_player2_player_properties_["MaximumRate"] = base::Value(1.0); set_player_property("Metadata", DbusDictionary());
media_player2_player_properties_["CanGoNext"] = base::Value(false); set_player_property("Volume", DbusDouble(1.0));
media_player2_player_properties_["CanGoPrevious"] = base::Value(false); set_player_property("Position", DbusInt64(0));
media_player2_player_properties_["CanPlay"] = base::Value(false); set_player_property("MinimumRate", DbusDouble(1.0));
media_player2_player_properties_["CanPause"] = base::Value(false); set_player_property("MaximumRate", DbusDouble(1.0));
media_player2_player_properties_["CanSeek"] = base::Value(false); set_player_property("CanGoNext", DbusBoolean(false));
media_player2_player_properties_["CanControl"] = base::Value(true); set_player_property("CanGoPrevious", DbusBoolean(false));
set_player_property("CanPlay", DbusBoolean(false));
set_player_property("CanPause", DbusBoolean(false));
set_player_property("CanSeek", DbusBoolean(false));
set_player_property("CanControl", DbusBoolean(false));
} }
void MprisServiceImpl::InitializeDbusInterface() { void MprisServiceImpl::InitializeDbusInterface() {
...@@ -170,6 +173,17 @@ void MprisServiceImpl::InitializeDbusInterface() { ...@@ -170,6 +173,17 @@ void MprisServiceImpl::InitializeDbusInterface() {
bus_->GetExportedObject(dbus::ObjectPath(kMprisAPIObjectPath)); bus_->GetExportedObject(dbus::ObjectPath(kMprisAPIObjectPath));
int num_methods_attempted_to_export = 0; int num_methods_attempted_to_export = 0;
// kNumMethodsToExport calls for method export, 1 call for properties
// initialization.
barrier_ = SuccessBarrierCallback(
kNumMethodsToExport + 1,
base::BindOnce(&MprisServiceImpl::OnInitialized, base::Unretained(this)));
properties_ = std::make_unique<DbusProperties>(exported_object_, barrier_);
properties_->RegisterInterface(kMprisAPIInterfaceName);
properties_->RegisterInterface(kMprisAPIPlayerInterfaceName);
InitializeProperties();
// Helper lambdas for exporting methods while keeping track of the number of // Helper lambdas for exporting methods while keeping track of the number of
// exported methods. // exported methods.
auto export_method = auto export_method =
...@@ -217,45 +231,28 @@ void MprisServiceImpl::InitializeDbusInterface() { ...@@ -217,45 +231,28 @@ void MprisServiceImpl::InitializeDbusInterface() {
export_unhandled_method(kMprisAPIPlayerInterfaceName, "SetPosition"); export_unhandled_method(kMprisAPIPlayerInterfaceName, "SetPosition");
export_unhandled_method(kMprisAPIPlayerInterfaceName, "OpenUri"); export_unhandled_method(kMprisAPIPlayerInterfaceName, "OpenUri");
// Set up org.freedesktop.DBus.Properties interface.
// https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
export_method(dbus::kPropertiesInterface, dbus::kPropertiesGetAll,
base::BindRepeating(&MprisServiceImpl::GetAllProperties,
base::Unretained(this)));
export_method(dbus::kPropertiesInterface, dbus::kPropertiesGet,
base::BindRepeating(&MprisServiceImpl::GetProperty,
base::Unretained(this)));
export_unhandled_method(dbus::kPropertiesInterface, dbus::kPropertiesSet);
DCHECK_EQ(kNumMethodsToExport, num_methods_attempted_to_export); DCHECK_EQ(kNumMethodsToExport, num_methods_attempted_to_export);
} }
void MprisServiceImpl::OnExported(const std::string& interface_name, void MprisServiceImpl::OnExported(const std::string& interface_name,
const std::string& method_name, const std::string& method_name,
bool success) { bool success) {
if (!success) { barrier_.Run(success);
service_failed_to_start_ = true; }
return;
}
num_methods_exported_++;
// Still waiting for more methods to finish exporting.
if (num_methods_exported_ < kNumMethodsToExport)
return;
bus_->RequestOwnership(service_name_, void MprisServiceImpl::OnInitialized(bool success) {
dbus::Bus::ServiceOwnershipOptions::REQUIRE_PRIMARY, if (success) {
base::BindRepeating(&MprisServiceImpl::OnOwnership, bus_->RequestOwnership(service_name_,
base::Unretained(this))); dbus::Bus::ServiceOwnershipOptions::REQUIRE_PRIMARY,
base::BindRepeating(&MprisServiceImpl::OnOwnership,
base::Unretained(this)));
}
} }
void MprisServiceImpl::OnOwnership(const std::string& service_name, void MprisServiceImpl::OnOwnership(const std::string& service_name,
bool success) { bool success) {
if (!success) { if (!success)
service_failed_to_start_ = true;
return; return;
}
service_ready_ = true; service_ready_ = true;
...@@ -317,228 +314,16 @@ void MprisServiceImpl::DoNothing( ...@@ -317,228 +314,16 @@ void MprisServiceImpl::DoNothing(
response_sender.Run(dbus::Response::FromMethodCall(method_call)); response_sender.Run(dbus::Response::FromMethodCall(method_call));
} }
// org.freedesktop.DBus.Properties.GetAll(in STRING interface_name,
// out DICT<STRING,VARIANT> props);
void MprisServiceImpl::GetAllProperties(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
dbus::MessageReader reader(method_call);
std::string interface;
if (!reader.PopString(&interface)) {
response_sender.Run(nullptr);
return;
}
std::unique_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get());
if (interface == kMprisAPIInterfaceName) {
AddPropertiesToWriter(&writer, media_player2_properties_);
} else if (interface == kMprisAPIPlayerInterfaceName) {
AddPropertiesToWriter(&writer, media_player2_player_properties_);
} else if (interface == dbus::kPropertiesInterface) {
// There are no properties to give for this interface.
PropertyMap empty_properties_map;
AddPropertiesToWriter(&writer, empty_properties_map);
} else {
// The given interface is not supported, so return a null response.
response_sender.Run(nullptr);
return;
}
response_sender.Run(std::move(response));
}
// org.freedesktop.DBus.Properties.Get(in STRING interface_name,
// in STRING property_name,
// out VARIANT value);
void MprisServiceImpl::GetProperty(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
dbus::MessageReader reader(method_call);
std::string interface;
if (!reader.PopString(&interface)) {
response_sender.Run(nullptr);
return;
}
std::string property_name;
if (!reader.PopString(&property_name)) {
response_sender.Run(nullptr);
return;
}
std::unique_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get());
bool success = false;
if (interface == kMprisAPIInterfaceName) {
auto property_iter = media_player2_properties_.find(property_name);
if (property_iter != media_player2_properties_.end()) {
dbus::AppendValueDataAsVariant(&writer, property_iter->second);
success = true;
}
} else if (interface == kMprisAPIPlayerInterfaceName) {
auto property_iter = media_player2_player_properties_.find(property_name);
if (property_iter != media_player2_player_properties_.end()) {
if (property_name == "Metadata") {
const base::DictionaryValue* metadata = nullptr;
property_iter->second.GetAsDictionary(&metadata);
AddMetadataToWriter(&writer, metadata);
} else {
dbus::AppendValueDataAsVariant(&writer, property_iter->second);
}
success = true;
}
}
// If we don't support the given property, return a null response.
if (!success) {
response_sender.Run(nullptr);
return;
}
response_sender.Run(std::move(response));
}
void MprisServiceImpl::AddPropertiesToWriter(dbus::MessageWriter* writer,
const PropertyMap& properties) {
DCHECK(writer);
dbus::MessageWriter array_writer(nullptr);
dbus::MessageWriter dict_entry_writer(nullptr);
writer->OpenArray("{sv}", &array_writer);
for (auto& property : properties) {
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString(property.first);
if (property.first == "Metadata") {
const base::DictionaryValue* metadata = nullptr;
property.second.GetAsDictionary(&metadata);
AddMetadataToWriter(&dict_entry_writer, metadata);
} else {
dbus::AppendValueDataAsVariant(&dict_entry_writer, property.second);
}
array_writer.CloseContainer(&dict_entry_writer);
}
writer->CloseContainer(&array_writer);
}
void MprisServiceImpl::AddMetadataToWriter(
dbus::MessageWriter* writer,
const base::DictionaryValue* metadata) {
// We need to special-case Metadata's xesam:artist when emitting properties,
// since |dbus::AppendValueDataAsVariant()| only supports arrays of variants
// and not arrays of strings.
DCHECK(writer);
DCHECK(metadata);
dbus::MessageWriter metadata_variant_writer(nullptr);
writer->OpenVariant("a{sv}", &metadata_variant_writer);
dbus::MessageWriter metadata_writer(nullptr);
metadata_variant_writer.OpenArray("{sv}", &metadata_writer);
for (base::DictionaryValue::Iterator iter(*metadata); !iter.IsAtEnd();
iter.Advance()) {
dbus::MessageWriter metadata_entry_writer(nullptr);
metadata_writer.OpenDictEntry(&metadata_entry_writer);
metadata_entry_writer.AppendString(iter.key());
if (iter.key() == "xesam:artist") {
// Here, we convert our string value for artist into an array of
// strings to append to the message.
dbus::MessageWriter artist_writer(nullptr);
metadata_entry_writer.OpenVariant("as", &artist_writer);
std::vector<std::string> artists{iter.value().GetString()};
artist_writer.AppendArrayOfStrings(artists);
metadata_entry_writer.CloseContainer(&artist_writer);
} else {
dbus::AppendValueDataAsVariant(&metadata_entry_writer, iter.value());
}
metadata_writer.CloseContainer(&metadata_entry_writer);
}
metadata_variant_writer.CloseContainer(&metadata_writer);
writer->CloseContainer(&metadata_variant_writer);
}
void MprisServiceImpl::SetPropertyInternal(PropertyMap& property_map,
const std::string& property_name,
const base::Value& new_value) {
if (property_map[property_name] == new_value)
return;
property_map[property_name] = new_value.Clone();
changed_properties_.insert(property_name);
EmitPropertiesChangedSignalDebounced();
}
void MprisServiceImpl::SetMetadataPropertyInternal( void MprisServiceImpl::SetMetadataPropertyInternal(
const std::string& property_name, const std::string& property_name,
const base::Value& new_value) { DbusVariant&& new_value) {
base::Value& metadata = media_player2_player_properties_["Metadata"]; DbusVariant* dictionary_variant =
base::Value* current_value = metadata.FindKey(property_name); properties_->GetProperty(kMprisAPIPlayerInterfaceName, "Metadata");
DCHECK(dictionary_variant);
if (current_value && *current_value == new_value) DbusDictionary* dictionary = dictionary_variant->GetAs<DbusDictionary>();
return; DCHECK(dictionary);
metadata.SetKey(property_name, new_value.Clone()); if (dictionary->Put(property_name, std::move(new_value)))
properties_->PropertyUpdated(kMprisAPIPlayerInterfaceName, "Metadata");
changed_properties_.insert("Metadata");
EmitPropertiesChangedSignalDebounced();
}
void MprisServiceImpl::EmitPropertiesChangedSignalDebounced() {
properties_changed_debounce_timer_.Stop();
properties_changed_debounce_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds), this,
&MprisServiceImpl::EmitPropertiesChangedSignal);
}
void MprisServiceImpl::EmitPropertiesChangedSignal() {
// If we're still trying to start the service, delay emitting.
if (!service_ready_ && !service_failed_to_start_) {
EmitPropertiesChangedSignalDebounced();
return;
}
if (!bus_ || !exported_object_ || !service_ready_)
return;
PropertyMap changed_property_map;
for (std::string property_name : changed_properties_) {
changed_property_map[property_name] =
media_player2_player_properties_[property_name].Clone();
}
changed_properties_.clear();
// |signal| follows the PropertiesChanged API:
// org.freedesktop.DBus.Properties.PropertiesChanged(
// STRING interface_name,
// DICT<STRING,VARIANT> changed_properties,
// ARRAY<STRING> invalidated_properties);
dbus::Signal signal(dbus::kPropertiesInterface, dbus::kPropertiesChanged);
dbus::MessageWriter writer(&signal);
writer.AppendString(kMprisAPIPlayerInterfaceName);
AddPropertiesToWriter(&writer, changed_property_map);
std::vector<std::string> empty_invalidated_properties;
writer.AppendArrayOfStrings(empty_invalidated_properties);
exported_object_->SendSignal(&signal);
} }
} // namespace mpris } // namespace mpris
...@@ -11,20 +11,16 @@ ...@@ -11,20 +11,16 @@
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
#include "base/containers/flat_set.h" #include "base/containers/flat_set.h"
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "base/memory/singleton.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "components/dbus/properties/types.h"
#include "dbus/bus.h" #include "dbus/bus.h"
#include "dbus/exported_object.h" #include "dbus/exported_object.h"
#include "ui/base/mpris/mpris_service.h" #include "ui/base/mpris/mpris_service.h"
namespace base { class DbusProperties;
class DictionaryValue;
class Value;
} // namespace base
namespace dbus { namespace dbus {
class MessageWriter;
class MethodCall; class MethodCall;
} // namespace dbus } // namespace dbus
...@@ -64,6 +60,7 @@ class COMPONENT_EXPORT(MPRIS) MprisServiceImpl : public MprisService { ...@@ -64,6 +60,7 @@ class COMPONENT_EXPORT(MPRIS) MprisServiceImpl : public MprisService {
void OnExported(const std::string& interface_name, void OnExported(const std::string& interface_name,
const std::string& method_name, const std::string& method_name,
bool success); bool success);
void OnInitialized(bool success);
void OnOwnership(const std::string& service_name, bool success); void OnOwnership(const std::string& service_name, bool success);
// org.mpris.MediaPlayer2.Player interface. // org.mpris.MediaPlayer2.Player interface.
...@@ -84,47 +81,12 @@ class COMPONENT_EXPORT(MPRIS) MprisServiceImpl : public MprisService { ...@@ -84,47 +81,12 @@ class COMPONENT_EXPORT(MPRIS) MprisServiceImpl : public MprisService {
void DoNothing(dbus::MethodCall* method_call, void DoNothing(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender); dbus::ExportedObject::ResponseSender response_sender);
// org.freedesktop.DBus.Properties interface.
void GetAllProperties(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
void GetProperty(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
using PropertyMap = base::flat_map<std::string, base::Value>;
// Sets a value on the given PropertyMap and sends a PropertiesChanged signal
// if necessary.
void SetPropertyInternal(PropertyMap& property_map,
const std::string& property_name,
const base::Value& new_value);
// Sets a value on the Metadata property map and sends a PropertiesChanged // Sets a value on the Metadata property map and sends a PropertiesChanged
// signal if necessary. // signal if necessary.
void SetMetadataPropertyInternal(const std::string& property_name, void SetMetadataPropertyInternal(const std::string& property_name,
const base::Value& new_value); DbusVariant&& new_value);
// Updates a timer to debounce calls to |EmitPropertiesChangedSignal|.
void EmitPropertiesChangedSignalDebounced();
// Emits a org.freedesktop.DBus.Properties.PropertiesChanged signal for the
// given map of changed properties.
void EmitPropertiesChangedSignal();
// Writes all properties onto writer.
void AddPropertiesToWriter(dbus::MessageWriter* writer,
const PropertyMap& properties);
// Writes the metadata property onto writer. Metadata is handled differently std::unique_ptr<DbusProperties> properties_;
// than other properties since it has sub-properties that need to be handled
// as non-variants.
void AddMetadataToWriter(dbus::MessageWriter* writer,
const base::DictionaryValue* metadata);
// Map of org.mpris.MediaPlayer2 interface properties.
PropertyMap media_player2_properties_;
// Map of org.mpris.MediaPlayer2.Player interface properties.
PropertyMap media_player2_player_properties_;
scoped_refptr<dbus::Bus> bus_; scoped_refptr<dbus::Bus> bus_;
dbus::ExportedObject* exported_object_; dbus::ExportedObject* exported_object_;
...@@ -132,24 +94,11 @@ class COMPONENT_EXPORT(MPRIS) MprisServiceImpl : public MprisService { ...@@ -132,24 +94,11 @@ class COMPONENT_EXPORT(MPRIS) MprisServiceImpl : public MprisService {
// The generated service name given to |bus_| when requesting ownership. // The generated service name given to |bus_| when requesting ownership.
const std::string service_name_; const std::string service_name_;
// The number of methods that have been successfully exported through base::RepeatingCallback<void(bool)> barrier_;
// |exported_object_|.
int num_methods_exported_ = 0;
// True if we have finished creating the DBus service and received ownership. // True if we have finished creating the DBus service and received ownership.
bool service_ready_ = false; bool service_ready_ = false;
// True if we failed to start the MPRIS DBus service.
bool service_failed_to_start_ = false;
// Used to only send 1 PropertiesChanged signal when many properties are
// changed at once.
base::OneShotTimer properties_changed_debounce_timer_;
// Holds a list of properties that have changed since the last time we emitted
// a PropertiesChanged signal.
base::flat_set<std::string> changed_properties_;
base::ObserverList<MprisServiceObserver> observers_; base::ObserverList<MprisServiceObserver> observers_;
DISALLOW_COPY_AND_ASSIGN(MprisServiceImpl); DISALLOW_COPY_AND_ASSIGN(MprisServiceImpl);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment