1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/dbus/cros_dbus_service.h"
6
7#include "base/bind.h"
8#include "base/stl_util.h"
9#include "base/sys_info.h"
10#include "base/threading/platform_thread.h"
11#include "chrome/browser/chromeos/dbus/display_power_service_provider.h"
12#include "chrome/browser/chromeos/dbus/liveness_service_provider.h"
13#include "chrome/browser/chromeos/dbus/printer_service_provider.h"
14#include "chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h"
15#include "chrome/browser/chromeos/dbus/screen_lock_service_provider.h"
16#include "chromeos/dbus/dbus_thread_manager.h"
17#include "dbus/bus.h"
18#include "dbus/exported_object.h"
19#include "dbus/object_path.h"
20#include "third_party/cros_system_api/dbus/service_constants.h"
21
22namespace chromeos {
23
24namespace {
25
26CrosDBusService* g_cros_dbus_service = NULL;
27
28}  // namespace
29
30// The CrosDBusService implementation used in production, and unit tests.
31class CrosDBusServiceImpl : public CrosDBusService {
32 public:
33  explicit CrosDBusServiceImpl(dbus::Bus* bus)
34      : service_started_(false),
35        origin_thread_id_(base::PlatformThread::CurrentId()),
36        bus_(bus) {
37  }
38
39  virtual ~CrosDBusServiceImpl() {
40    STLDeleteElements(&service_providers_);
41  }
42
43  // Starts the D-Bus service.
44  void Start() {
45    // Make sure we're running on the origin thread (i.e. the UI thread in
46    // production).
47    DCHECK(OnOriginThread());
48
49    // Return if the service has been already started.
50    if (service_started_)
51      return;
52
53    // There are some situations, described in http://crbug.com/234382#c27,
54    // where processes on Linux can wind up stuck in an uninterruptible state
55    // for tens of seconds. If this happens when Chrome is trying to exit,
56    // this unkillable process can wind up clinging to ownership of
57    // kLibCrosServiceName while the system is trying to restart the browser.
58    // This leads to a fatal situation if we don't allow the new browser
59    // instance to replace the old as the owner of kLibCrosServiceName as seen
60    // in http://crbug.com/234382. Hence, REQUIRE_PRIMARY_ALLOW_REPLACEMENT.
61    bus_->RequestOwnership(kLibCrosServiceName,
62                           dbus::Bus::REQUIRE_PRIMARY_ALLOW_REPLACEMENT,
63                           base::Bind(&CrosDBusServiceImpl::OnOwnership,
64                                      base::Unretained(this)));
65
66    exported_object_ = bus_->GetExportedObject(
67        dbus::ObjectPath(kLibCrosServicePath));
68
69    for (size_t i = 0; i < service_providers_.size(); ++i)
70      service_providers_[i]->Start(exported_object_);
71
72    service_started_ = true;
73
74    VLOG(1) << "CrosDBusServiceImpl started.";
75  }
76
77  // Registers a service provider. This must be done before Start().
78  // |provider| will be owned by CrosDBusService.
79  void RegisterServiceProvider(ServiceProviderInterface* provider) {
80    service_providers_.push_back(provider);
81  }
82
83 private:
84  // Returns true if the current thread is on the origin thread.
85  bool OnOriginThread() {
86    return base::PlatformThread::CurrentId() == origin_thread_id_;
87  }
88
89  // Called when an ownership request is completed.
90  void OnOwnership(const std::string& service_name,
91                   bool success) {
92    LOG_IF(FATAL, !success) << "Failed to own: " << service_name;
93  }
94
95  bool service_started_;
96  base::PlatformThreadId origin_thread_id_;
97  dbus::Bus* bus_;
98  scoped_refptr<dbus::ExportedObject> exported_object_;
99
100  // Service providers that form CrosDBusService.
101  std::vector<ServiceProviderInterface*> service_providers_;
102};
103
104// The stub CrosDBusService implementation used on Linux desktop,
105// which does nothing as of now.
106class CrosDBusServiceStubImpl : public CrosDBusService {
107 public:
108  CrosDBusServiceStubImpl() {
109  }
110
111  virtual ~CrosDBusServiceStubImpl() {
112  }
113};
114
115// static
116void CrosDBusService::Initialize() {
117  if (g_cros_dbus_service) {
118    LOG(WARNING) << "CrosDBusService was already initialized";
119    return;
120  }
121  dbus::Bus* bus = DBusThreadManager::Get()->GetSystemBus();
122  if (base::SysInfo::IsRunningOnChromeOS() && bus) {
123    CrosDBusServiceImpl* service = new CrosDBusServiceImpl(bus);
124    service->RegisterServiceProvider(ProxyResolutionServiceProvider::Create());
125#if !defined(USE_ATHENA)
126    // crbug.com/413897
127    service->RegisterServiceProvider(new DisplayPowerServiceProvider);
128    // crbug.com/401285
129    service->RegisterServiceProvider(new PrinterServiceProvider);
130#endif
131    service->RegisterServiceProvider(new LivenessServiceProvider);
132    service->RegisterServiceProvider(new ScreenLockServiceProvider);
133    g_cros_dbus_service = service;
134    service->Start();
135  } else {
136    g_cros_dbus_service = new CrosDBusServiceStubImpl;
137  }
138  VLOG(1) << "CrosDBusService initialized";
139}
140
141// static
142void CrosDBusService::InitializeForTesting(
143    dbus::Bus* bus,
144    ServiceProviderInterface* proxy_resolution_service) {
145  if (g_cros_dbus_service) {
146    LOG(WARNING) << "CrosDBusService was already initialized";
147    return;
148  }
149  CrosDBusServiceImpl* service =  new CrosDBusServiceImpl(bus);
150  service->RegisterServiceProvider(proxy_resolution_service);
151  service->Start();
152  g_cros_dbus_service = service;
153  VLOG(1) << "CrosDBusService initialized";
154}
155
156// static
157void CrosDBusService::Shutdown() {
158  delete g_cros_dbus_service;
159  g_cros_dbus_service = NULL;
160  VLOG(1) << "CrosDBusService Shutdown completed";
161}
162
163CrosDBusService::~CrosDBusService() {
164}
165
166CrosDBusService::ServiceProviderInterface::~ServiceProviderInterface() {
167}
168
169}  // namespace chromeos
170