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