Revert of Remove OneClickSigninHelper since it is no longer used. (patchset #5 id...
[chromium-blink-merge.git] / remoting / host / setup / daemon_controller_delegate_mac.mm
blob2fd7daaa660ac4aea21d964d73a72af8b9fff585
1 // Copyright 2013 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.
5 #include <CoreFoundation/CoreFoundation.h>
7 #include "remoting/host/setup/daemon_controller_delegate_mac.h"
9 #include <launch.h>
10 #include <stdio.h>
11 #include <sys/types.h>
13 #include "base/basictypes.h"
14 #include "base/bind.h"
15 #include "base/compiler_specific.h"
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/logging.h"
19 #include "base/mac/foundation_util.h"
20 #include "base/mac/launchd.h"
21 #include "base/mac/mac_logging.h"
22 #include "base/mac/mac_util.h"
23 #include "base/mac/scoped_launch_data.h"
24 #include "base/time/time.h"
25 #include "base/values.h"
26 #include "remoting/host/constants_mac.h"
27 #include "remoting/host/host_config.h"
28 #include "remoting/host/usage_stats_consent.h"
30 namespace remoting {
32 DaemonControllerDelegateMac::DaemonControllerDelegateMac() {
35 DaemonControllerDelegateMac::~DaemonControllerDelegateMac() {
36   DeregisterForPreferencePaneNotifications();
39 DaemonController::State DaemonControllerDelegateMac::GetState() {
40   pid_t job_pid = base::mac::PIDForJob(kServiceName);
41   if (job_pid < 0) {
42     return DaemonController::STATE_UNKNOWN;
43   } else if (job_pid == 0) {
44     // Service is stopped, or a start attempt failed.
45     return DaemonController::STATE_STOPPED;
46   } else {
47     return DaemonController::STATE_STARTED;
48   }
51 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateMac::GetConfig() {
52   base::FilePath config_path(kHostConfigFilePath);
53   scoped_ptr<base::DictionaryValue> host_config(
54       HostConfigFromJsonFile(config_path));
55   if (!host_config)
56     return nullptr;
58   scoped_ptr<base::DictionaryValue> config(new base::DictionaryValue);
59   std::string value;
60   if (host_config->GetString(kHostIdConfigPath, &value))
61     config->SetString(kHostIdConfigPath, value);
62   if (host_config->GetString(kXmppLoginConfigPath, &value))
63     config->SetString(kXmppLoginConfigPath, value);
64   return config.Pass();
67 void DaemonControllerDelegateMac::SetConfigAndStart(
68     scoped_ptr<base::DictionaryValue> config,
69     bool consent,
70     const DaemonController::CompletionCallback& done) {
71   config->SetBoolean(kUsageStatsConsentConfigPath, consent);
72   ShowPreferencePane(HostConfigToJson(*config), done);
75 void DaemonControllerDelegateMac::UpdateConfig(
76     scoped_ptr<base::DictionaryValue> config,
77     const DaemonController::CompletionCallback& done) {
78   base::FilePath config_file_path(kHostConfigFilePath);
79   scoped_ptr<base::DictionaryValue> host_config(
80       HostConfigFromJsonFile(config_file_path));
81   if (!host_config) {
82     done.Run(DaemonController::RESULT_FAILED);
83     return;
84   }
86   host_config->MergeDictionary(config.get());
87   ShowPreferencePane(HostConfigToJson(*host_config), done);
90 void DaemonControllerDelegateMac::Stop(
91     const DaemonController::CompletionCallback& done) {
92   ShowPreferencePane("", done);
95 DaemonController::UsageStatsConsent
96 DaemonControllerDelegateMac::GetUsageStatsConsent() {
97   DaemonController::UsageStatsConsent consent;
98   consent.supported = true;
99   consent.allowed = false;
100   // set_by_policy is not yet supported.
101   consent.set_by_policy = false;
103   base::FilePath config_file_path(kHostConfigFilePath);
104   scoped_ptr<base::DictionaryValue> host_config(
105       HostConfigFromJsonFile(config_file_path));
106   if (host_config) {
107     host_config->GetBoolean(kUsageStatsConsentConfigPath, &consent.allowed);
108   }
110   return consent;
113 void DaemonControllerDelegateMac::ShowPreferencePane(
114     const std::string& config_data,
115     const DaemonController::CompletionCallback& done) {
116   if (DoShowPreferencePane(config_data)) {
117     RegisterForPreferencePaneNotifications(done);
118   } else {
119     done.Run(DaemonController::RESULT_FAILED);
120   }
123 // CFNotificationCenterAddObserver ties the thread on which distributed
124 // notifications are received to the one on which it is first called.
125 // This is safe because HostNPScriptObject::InvokeAsyncResultCallback
126 // bounces the invocation to the correct thread, so it doesn't matter
127 // which thread CompletionCallbacks are called on.
128 void DaemonControllerDelegateMac::RegisterForPreferencePaneNotifications(
129     const DaemonController::CompletionCallback& done) {
130   // We can only have one callback registered at a time. This is enforced by the
131   // UX flow of the web-app.
132   DCHECK(current_callback_.is_null());
133   current_callback_ = done;
135   CFNotificationCenterAddObserver(
136       CFNotificationCenterGetDistributedCenter(),
137       this,
138       &DaemonControllerDelegateMac::PreferencePaneCallback,
139       CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME),
140       nullptr,
141       CFNotificationSuspensionBehaviorDeliverImmediately);
142   CFNotificationCenterAddObserver(
143       CFNotificationCenterGetDistributedCenter(),
144       this,
145       &DaemonControllerDelegateMac::PreferencePaneCallback,
146       CFSTR(UPDATE_FAILED_NOTIFICATION_NAME),
147       nullptr,
148       CFNotificationSuspensionBehaviorDeliverImmediately);
151 void DaemonControllerDelegateMac::DeregisterForPreferencePaneNotifications() {
152   CFNotificationCenterRemoveObserver(
153       CFNotificationCenterGetDistributedCenter(),
154       this,
155       CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME),
156       nullptr);
157   CFNotificationCenterRemoveObserver(
158       CFNotificationCenterGetDistributedCenter(),
159       this,
160       CFSTR(UPDATE_FAILED_NOTIFICATION_NAME),
161       nullptr);
164 void DaemonControllerDelegateMac::PreferencePaneCallbackDelegate(
165     CFStringRef name) {
166   DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
167   if (CFStringCompare(name, CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME), 0) ==
168           kCFCompareEqualTo) {
169     result = DaemonController::RESULT_OK;
170   } else if (CFStringCompare(name, CFSTR(UPDATE_FAILED_NOTIFICATION_NAME), 0) ==
171           kCFCompareEqualTo) {
172     result = DaemonController::RESULT_FAILED;
173   } else {
174     LOG(WARNING) << "Ignoring unexpected notification: " << name;
175     return;
176   }
178   DCHECK(!current_callback_.is_null());
179   DaemonController::CompletionCallback done = current_callback_;
180   current_callback_.Reset();
181   done.Run(result);
183   DeregisterForPreferencePaneNotifications();
186 // static
187 bool DaemonControllerDelegateMac::DoShowPreferencePane(
188     const std::string& config_data) {
189   if (!config_data.empty()) {
190     base::FilePath config_path;
191     if (!base::GetTempDir(&config_path)) {
192       LOG(ERROR) << "Failed to get filename for saving configuration data.";
193       return false;
194     }
195     config_path = config_path.Append(kHostConfigFileName);
197     int written = base::WriteFile(config_path, config_data.data(),
198                                        config_data.size());
199     if (written != static_cast<int>(config_data.size())) {
200       LOG(ERROR) << "Failed to save configuration data to: "
201                  << config_path.value();
202       return false;
203     }
204   }
206   base::FilePath pane_path;
207   // TODO(lambroslambrou): Use NSPreferencePanesDirectory once we start
208   // building against SDK 10.6.
209   if (!base::mac::GetLocalDirectory(NSLibraryDirectory, &pane_path)) {
210     LOG(ERROR) << "Failed to get directory for local preference panes.";
211     return false;
212   }
213   pane_path = pane_path.Append("PreferencePanes").Append(kPrefPaneFileName);
215   FSRef pane_path_ref;
216   if (!base::mac::FSRefFromPath(pane_path.value(), &pane_path_ref)) {
217     LOG(ERROR) << "Failed to create FSRef";
218     return false;
219   }
220   OSStatus status = LSOpenFSRef(&pane_path_ref, nullptr);
221   if (status != noErr) {
222     OSSTATUS_LOG(ERROR, status) << "LSOpenFSRef failed for path: "
223                                 << pane_path.value();
224     return false;
225   }
227   CFNotificationCenterRef center =
228       CFNotificationCenterGetDistributedCenter();
229   base::ScopedCFTypeRef<CFStringRef> service_name(CFStringCreateWithCString(
230       kCFAllocatorDefault, remoting::kServiceName, kCFStringEncodingUTF8));
231   CFNotificationCenterPostNotification(center, service_name, nullptr, nullptr,
232                                        TRUE);
233   return true;
236 // static
237 void DaemonControllerDelegateMac::PreferencePaneCallback(
238     CFNotificationCenterRef center,
239     void* observer,
240     CFStringRef name,
241     const void* object,
242     CFDictionaryRef user_info) {
243   DaemonControllerDelegateMac* self =
244       reinterpret_cast<DaemonControllerDelegateMac*>(observer);
245   if (!self) {
246     LOG(WARNING) << "Ignoring notification with nullptr observer: " << name;
247     return;
248   }
250   self->PreferencePaneCallbackDelegate(name);
253 scoped_refptr<DaemonController> DaemonController::Create() {
254   scoped_ptr<DaemonController::Delegate> delegate(
255       new DaemonControllerDelegateMac());
256   return new DaemonController(delegate.Pass());
259 }  // namespace remoting