D-Bus用于在Chrome OS上执行进程间通信。 本文档介绍如何使用D-Bus在Chrome和系统守护程序之间进行通信。
共享常量
system_api存储库包含在Chrome和Chrome OS系统守护程序之间共享的C ++常量和协议缓冲区.proto文件。 这包括D-Bus服务名称,路径和接口,信号和方法名称以及作为D-Bus参数传递的枚举值。
系统守护程序基本上总是使用最新版本的存储库,而Chrome使用src / DEPS中指定的修订版。 在使用新常量或删除已弃用的常量时要小心。 要创建将Chrome使用的版本更新为ToT的更改,请运行roll-dep src / third_party / cros_system_api。
创建Chrome D-Bus服务
接收方法调用或发出信号需要注册服务。 Chrome使用各种服务名称注册服务,包括org.chromium.DisplayService和org.chromium.NetworkProxyService。 Services实现了chromeos::CrosDBusService::ServiceProviderInterface接口。
代码位置
mus + ash项目将Chrome的shell / window管理代码(即// ash)与其浏览器代码(即// chrome)分开。 如果服务不依赖于// chrome下的任何代码,则其服务提供者类应该位于ash / dbus中并由ash_dbus_services.cc实例化。
具有依赖于// chrome的实现的服务应该在chrome / browser / chromeos / dbus中实现,并由chrome_browser_main_chromeos.cc实例化。
策略文件
为了使Chrome能够获得服务名称的所有权以及其他进程能够调用其方法,每个服务还需要ash/dbus或chrome/browser/chromeos/dbus中的.conf XML策略文件。 策略文件由dbus-daemon(实现系统总线)加载,并指定哪些Unix用户可以拥有服务名称或调用服务的方法。 Chrome的政策文件已安装到/opt/google/chrome/dbus,并且必须在ash/BUILD.gn或chrome/browser/chromeos/BUILD.gn中的dbus_service_files目标中列出。 (根据上面描述的旧模式,请注意某些策略文件改为使用chromeos/dbus/services。)
有关D-Bus权限的更多信息,请参阅D-Bus最佳实践。
使用系统守护进程的D-Bus服务
要调用由系统守护程序导出的方法或观察信号,Chrome使用位于chromeos/dbus下的Client类。
Chrome的D-Bus相关代码不是线程安全的,并且在浏览器进程中的UI线程上运行。
系统守护程序必须安装自己的D-Bus策略文件,为chronos用户授予send_destination权限,以便从Chrome接收方法调用。 有关详细信息,请在D-Bus最佳实践中搜索“权限”。
Client类由chromeos::DBusClientCommon(在所有进程中实例化)和chromeos::DBusClientsBrowser(仅在浏览器进程中实例化)拥有,这两者都由chromeos::DBusThreadManager拥有。 客户端类目前通过DBusThreadManager单例访问。
添加新的Client类时,请遵循现有代码使用的模式:
定义真实和虚假的实现
对于名为“foo”的服务,在foo_client.h中定义一个FooClient接口。 在foo_client.cc中,声明并定义实现接口的FooClientImpl类。 添加定义FakeFooClient实现的fake_foo_client.h和fake_foo_client.cc文件,这些实现可用于单元测试,以及在不存在系统守护程序的工作站上运行Chrome操作系统版本的Chrome时。
保持ClientImpl类最小化
理想情况下,客户端类应仅连接到信号,调用方法,以及序列化和反序列化D-Bus消息参数。客户端接口的实际实现不是由单元测试来实现的,因此请将您的实际逻辑保留在使用客户端接口的类中,在该类中可以使用虚假客户端实现进行测试。
更具体地说,Client接口应公开与相应D-Bus方法具有相同名称的公共方法,并可选择定义可用于接收有关信号通知的嵌套Observer接口。 FakeClientImpl还可以显示指定由方法返回的固定值的setter和NotifyObserversAboutSomeSignal方法,这些方法在所有观察者上调用方法来模拟信号的接收。
如果D-Bus方法将序列化协议缓冲区作为参数,则客户端类的相应方法应该将该protobuf的const引用作为其参数(而不是对应于protobuf字段的单个args)。观察者接口和方法回调也应尽可能采用protobuf args。有关其他信息,请在D-Bus最佳实践中搜索“复杂消息”
方法调用必须是异步的
由于Client类的代码在Chrome的UI线程上运行,因此对系统守护程序(可能以其他方式占用甚至挂起)的D-Bus方法调用必须是异步的。 这可以使用类似于以下的代码来完成:
foo_client.h
|
foo_client.cc
|
在编写接收和运行回调的方法的假实现时,将回调发布到消息循环而不是同步运行它。 这与实际实现中使用的调用模式匹配,其中系统守护程序的回复是异步接收的。
在服务可用之前不要调用服务
Chrome与许多系统守护程序并行启动,并且您的代码可能在它要与之通信的守护程序之前运行,已导出其方法或获取其服务名称的所有权。 如果您立即尝试对守护程序进行方法调用,则可能会失败,并将令人讨厌且无法执行的错误记录到Chrome的日志文件中。 要避免这种难看的失态,请将WaitForServiceToBeAvailable方法添加到客户端界面,调用者可以使用该方法推迟方法调用,直到守护程序准备就绪。 查看一些现有的客户端类,例如 chromeos :: CryptohomeClient或`chromeos :: DebugDaemonClient,举例说明。
在进程之间共享状态
如果您在Chrome和系统守护程序之间共享状态,则需要考虑当其中一个进程重新启动或其中一个进程在另一个进程准备就绪之前启动时,如何让两个进程重新同步。
系统守护程序重新启动
虽然通常不会出现这种情况,但您正在与之通信的系统守护程序可能会在系统运行时重新启动。 当然,如果守护进程崩溃,就会发生这种情况,但是当开发人员将新版本的守护进程推送到他们的开发设备时也会发生这种情况。 要处理这种情况,可以在ClientImpl类中调用dbus :: ObjectProxy :: SetNameOwnerChangedCallback,并在看到守护程序服务名称的非空“新所有者”时通知观察者。 有关此示例,请参阅chromeos :: PowerManagerClient。
Chrome重新启动
Chrome重启更为常见。 崩溃发生,但Chrome也会在用户注销时有意重启,有时甚至在用户登录时重新启动(以获取对chrome:// flags的更改)。 系统守护程序可以使用上述相同的SetNameOwnerChangedCallback技术来了解Chrome重新启动(使用相关的Chrome D-Bus服务名称)。
如果Chrome通过观察其D-Bus信号来跟踪守护程序的状态,则可能需要提供Chrome在启动或重新启动时可以调用的方法,以便从守护程序获取正确的初始状态。