Enable Enhanced Bookmark on Android Tablet
[chromium-blink-merge.git] / remoting / host / setup / daemon_controller_delegate_mac.mm
blob788475e2e86f94d58f914d4deb3709d3679a3a70
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_NOT_INSTALLED;
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::InstallHost(
68     const DaemonController::CompletionCallback& done) {
69   NOTREACHED();
72 void DaemonControllerDelegateMac::SetConfigAndStart(
73     scoped_ptr<base::DictionaryValue> config,
74     bool consent,
75     const DaemonController::CompletionCallback& done) {
76   config->SetBoolean(kUsageStatsConsentConfigPath, consent);
77   ShowPreferencePane(HostConfigToJson(*config), done);
80 void DaemonControllerDelegateMac::UpdateConfig(
81     scoped_ptr<base::DictionaryValue> config,
82     const DaemonController::CompletionCallback& done) {
83   base::FilePath config_file_path(kHostConfigFilePath);
84   scoped_ptr<base::DictionaryValue> host_config(
85       HostConfigFromJsonFile(config_file_path));
86   if (!host_config) {
87     done.Run(DaemonController::RESULT_FAILED);
88     return;
89   }
91   host_config->MergeDictionary(config.get());
92   ShowPreferencePane(HostConfigToJson(*host_config), done);
95 void DaemonControllerDelegateMac::Stop(
96     const DaemonController::CompletionCallback& done) {
97   ShowPreferencePane("", done);
100 void DaemonControllerDelegateMac::SetWindow(void* window_handle) {
101   // noop
104 std::string DaemonControllerDelegateMac::GetVersion() {
105   std::string version = "";
106   std::string command_line = remoting::kHostHelperScriptPath;
107   command_line += " --host-version";
108   FILE* script_output = popen(command_line.c_str(), "r");
109   if (script_output) {
110     char buffer[100];
111     char* result = fgets(buffer, sizeof(buffer), script_output);
112     pclose(script_output);
113     if (result) {
114       // The string is guaranteed to be null-terminated, but probably contains
115       // a newline character, which we don't want.
116       for (int i = 0; result[i]; ++i) {
117         if (result[i] < ' ') {
118           result[i] = 0;
119           break;
120         }
121       }
122       version = result;
123     }
124   }
126   return version;
129 DaemonController::UsageStatsConsent
130 DaemonControllerDelegateMac::GetUsageStatsConsent() {
131   DaemonController::UsageStatsConsent consent;
132   consent.supported = true;
133   consent.allowed = false;
134   // set_by_policy is not yet supported.
135   consent.set_by_policy = false;
137   base::FilePath config_file_path(kHostConfigFilePath);
138   scoped_ptr<base::DictionaryValue> host_config(
139       HostConfigFromJsonFile(config_file_path));
140   if (host_config) {
141     host_config->GetBoolean(kUsageStatsConsentConfigPath, &consent.allowed);
142   }
144   return consent;
147 void DaemonControllerDelegateMac::ShowPreferencePane(
148     const std::string& config_data,
149     const DaemonController::CompletionCallback& done) {
150   if (DoShowPreferencePane(config_data)) {
151     RegisterForPreferencePaneNotifications(done);
152   } else {
153     done.Run(DaemonController::RESULT_FAILED);
154   }
157 // CFNotificationCenterAddObserver ties the thread on which distributed
158 // notifications are received to the one on which it is first called.
159 // This is safe because HostNPScriptObject::InvokeAsyncResultCallback
160 // bounces the invocation to the correct thread, so it doesn't matter
161 // which thread CompletionCallbacks are called on.
162 void DaemonControllerDelegateMac::RegisterForPreferencePaneNotifications(
163     const DaemonController::CompletionCallback& done) {
164   // We can only have one callback registered at a time. This is enforced by the
165   // UX flow of the web-app.
166   DCHECK(current_callback_.is_null());
167   current_callback_ = done;
169   CFNotificationCenterAddObserver(
170       CFNotificationCenterGetDistributedCenter(),
171       this,
172       &DaemonControllerDelegateMac::PreferencePaneCallback,
173       CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME),
174       NULL,
175       CFNotificationSuspensionBehaviorDeliverImmediately);
176   CFNotificationCenterAddObserver(
177       CFNotificationCenterGetDistributedCenter(),
178       this,
179       &DaemonControllerDelegateMac::PreferencePaneCallback,
180       CFSTR(UPDATE_FAILED_NOTIFICATION_NAME),
181       NULL,
182       CFNotificationSuspensionBehaviorDeliverImmediately);
185 void DaemonControllerDelegateMac::DeregisterForPreferencePaneNotifications() {
186   CFNotificationCenterRemoveObserver(
187       CFNotificationCenterGetDistributedCenter(),
188       this,
189       CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME),
190       NULL);
191   CFNotificationCenterRemoveObserver(
192       CFNotificationCenterGetDistributedCenter(),
193       this,
194       CFSTR(UPDATE_FAILED_NOTIFICATION_NAME),
195       NULL);
198 void DaemonControllerDelegateMac::PreferencePaneCallbackDelegate(
199     CFStringRef name) {
200   DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
201   if (CFStringCompare(name, CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME), 0) ==
202           kCFCompareEqualTo) {
203     result = DaemonController::RESULT_OK;
204   } else if (CFStringCompare(name, CFSTR(UPDATE_FAILED_NOTIFICATION_NAME), 0) ==
205           kCFCompareEqualTo) {
206     result = DaemonController::RESULT_FAILED;
207   } else {
208     LOG(WARNING) << "Ignoring unexpected notification: " << name;
209     return;
210   }
212   DCHECK(!current_callback_.is_null());
213   DaemonController::CompletionCallback done = current_callback_;
214   current_callback_.Reset();
215   done.Run(result);
217   DeregisterForPreferencePaneNotifications();
220 // static
221 bool DaemonControllerDelegateMac::DoShowPreferencePane(
222     const std::string& config_data) {
223   if (!config_data.empty()) {
224     base::FilePath config_path;
225     if (!base::GetTempDir(&config_path)) {
226       LOG(ERROR) << "Failed to get filename for saving configuration data.";
227       return false;
228     }
229     config_path = config_path.Append(kHostConfigFileName);
231     int written = base::WriteFile(config_path, config_data.data(),
232                                        config_data.size());
233     if (written != static_cast<int>(config_data.size())) {
234       LOG(ERROR) << "Failed to save configuration data to: "
235                  << config_path.value();
236       return false;
237     }
238   }
240   base::FilePath pane_path;
241   // TODO(lambroslambrou): Use NSPreferencePanesDirectory once we start
242   // building against SDK 10.6.
243   if (!base::mac::GetLocalDirectory(NSLibraryDirectory, &pane_path)) {
244     LOG(ERROR) << "Failed to get directory for local preference panes.";
245     return false;
246   }
247   pane_path = pane_path.Append("PreferencePanes").Append(kPrefPaneFileName);
249   FSRef pane_path_ref;
250   if (!base::mac::FSRefFromPath(pane_path.value(), &pane_path_ref)) {
251     LOG(ERROR) << "Failed to create FSRef";
252     return false;
253   }
254   OSStatus status = LSOpenFSRef(&pane_path_ref, NULL);
255   if (status != noErr) {
256     OSSTATUS_LOG(ERROR, status) << "LSOpenFSRef failed for path: "
257                                 << pane_path.value();
258     return false;
259   }
261   CFNotificationCenterRef center =
262       CFNotificationCenterGetDistributedCenter();
263   base::ScopedCFTypeRef<CFStringRef> service_name(CFStringCreateWithCString(
264       kCFAllocatorDefault, remoting::kServiceName, kCFStringEncodingUTF8));
265   CFNotificationCenterPostNotification(center, service_name, NULL, NULL,
266                                        TRUE);
267   return true;
270 // static
271 void DaemonControllerDelegateMac::PreferencePaneCallback(
272     CFNotificationCenterRef center,
273     void* observer,
274     CFStringRef name,
275     const void* object,
276     CFDictionaryRef user_info) {
277   DaemonControllerDelegateMac* self =
278       reinterpret_cast<DaemonControllerDelegateMac*>(observer);
279   if (!self) {
280     LOG(WARNING) << "Ignoring notification with NULL observer: " << name;
281     return;
282   }
284   self->PreferencePaneCallbackDelegate(name);
287 scoped_refptr<DaemonController> DaemonController::Create() {
288   scoped_ptr<DaemonController::Delegate> delegate(
289       new DaemonControllerDelegateMac());
290   return new DaemonController(delegate.Pass());
293 }  // namespace remoting