1// Copyright (c) 2011 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/automation/testing_automation_provider.h"
6
7#include "base/values.h"
8#include "chrome/browser/automation/automation_provider_json.h"
9#include "chrome/browser/automation/automation_provider_observers.h"
10#include "chrome/browser/chromeos/cros/cros_library.h"
11#include "chrome/browser/chromeos/cros/network_library.h"
12#include "chrome/browser/chromeos/cros/power_library.h"
13#include "chrome/browser/chromeos/cros/screen_lock_library.h"
14#include "chrome/browser/chromeos/cros/update_library.h"
15#include "chrome/browser/chromeos/login/existing_user_controller.h"
16#include "chrome/browser/chromeos/login/screen_locker.h"
17#include "chrome/browser/chromeos/proxy_cros_settings_provider.h"
18
19using chromeos::CrosLibrary;
20using chromeos::NetworkLibrary;
21using chromeos::UserManager;
22using chromeos::UpdateLibrary;
23
24namespace {
25
26bool EnsureCrosLibraryLoaded(AutomationProvider* provider,
27                             IPC::Message* reply_message) {
28  if (!CrosLibrary::Get()->EnsureLoaded()) {
29    AutomationJSONReply(provider, reply_message).SendError(
30        "Could not load cros library.");
31    return false;
32  }
33  return true;
34}
35
36DictionaryValue* GetNetworkInfoDict(const chromeos::Network* network) {
37  DictionaryValue* item = new DictionaryValue;
38  item->SetString("name", network->name());
39  item->SetString("device_path", network->device_path());
40  item->SetString("ip_address", network->ip_address());
41  item->SetString("status", network->GetStateString());
42  return item;
43}
44
45Value* GetProxySetting(const std::string& setting_name) {
46  chromeos::ProxyCrosSettingsProvider settings_provider;
47  std::string setting_path = "cros.session.proxy.";
48  setting_path.append(setting_name);
49
50  if (setting_name == "ignorelist") {
51    Value* value;
52    if (settings_provider.Get(setting_path, &value))
53      return value;
54  } else {
55    Value* setting;
56    if (settings_provider.Get(setting_path, &setting)) {
57      DictionaryValue* setting_dict = static_cast<DictionaryValue*>(setting);
58      Value* value;
59      bool found = setting_dict->Remove("value", &value);
60      delete setting;
61      if (found)
62        return value;
63    }
64  }
65  return NULL;
66}
67
68const char* UpdateStatusToString(chromeos::UpdateStatusOperation status) {
69  switch (status) {
70    case chromeos::UPDATE_STATUS_IDLE:
71      return "idle";
72    case chromeos::UPDATE_STATUS_CHECKING_FOR_UPDATE:
73      return "checking for update";
74    case chromeos::UPDATE_STATUS_UPDATE_AVAILABLE:
75      return "update available";
76    case chromeos::UPDATE_STATUS_DOWNLOADING:
77      return "downloading";
78    case chromeos::UPDATE_STATUS_VERIFYING:
79      return "verifying";
80    case chromeos::UPDATE_STATUS_FINALIZING:
81      return "finalizing";
82    case chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT:
83      return "updated need reboot";
84    case chromeos::UPDATE_STATUS_REPORTING_ERROR_EVENT:
85      return "reporting error event";
86    default:
87      return "unknown";
88  }
89}
90
91void GetReleaseTrackCallback(void* user_data, const char* track) {
92  AutomationJSONReply* reply = static_cast<AutomationJSONReply*>(user_data);
93
94  if (track == NULL) {
95    reply->SendError("Unable to get release track.");
96    delete reply;
97    return;
98  }
99
100  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
101  return_value->SetString("release_track", track);
102
103  UpdateLibrary* update_library = CrosLibrary::Get()->GetUpdateLibrary();
104  const UpdateLibrary::Status& status = update_library->status();
105  chromeos::UpdateStatusOperation update_status = status.status;
106  return_value->SetString("status", UpdateStatusToString(update_status));
107  if (update_status == chromeos::UPDATE_STATUS_DOWNLOADING)
108    return_value->SetDouble("download_progress", status.download_progress);
109  if (status.last_checked_time > 0)
110    return_value->SetInteger("last_checked_time", status.last_checked_time);
111  if (status.new_size > 0)
112    return_value->SetInteger("new_size", status.new_size);
113
114  reply->SendSuccess(return_value.get());
115  delete reply;
116}
117
118void UpdateCheckCallback(void* user_data, chromeos::UpdateResult result,
119                         const char* error_msg) {
120  AutomationJSONReply* reply = static_cast<AutomationJSONReply*>(user_data);
121  if (result == chromeos::UPDATE_RESULT_SUCCESS)
122    reply->SendSuccess(NULL);
123  else
124    reply->SendError(error_msg);
125  delete reply;
126}
127
128}  // namespace
129
130void TestingAutomationProvider::GetLoginInfo(DictionaryValue* args,
131                                             IPC::Message* reply_message) {
132  AutomationJSONReply reply(this, reply_message);
133  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
134
135  const UserManager* user_manager = UserManager::Get();
136  if (!user_manager)
137    reply.SendError("No user manager!");
138  const chromeos::ScreenLocker* screen_locker =
139      chromeos::ScreenLocker::default_screen_locker();
140
141  return_value->SetBoolean("is_owner", user_manager->current_user_is_owner());
142  return_value->SetBoolean("is_logged_in", user_manager->user_is_logged_in());
143  return_value->SetBoolean("is_screen_locked", screen_locker);
144  if (user_manager->user_is_logged_in()) {
145    return_value->SetBoolean("is_guest", user_manager->IsLoggedInAsGuest());
146    return_value->SetString("email", user_manager->logged_in_user().email());
147  }
148
149  reply.SendSuccess(return_value.get());
150}
151
152// Logging in as guest will cause session_manager to restart Chrome with new
153// flags. If you used EnableChromeTesting, you will have to call it again.
154void TestingAutomationProvider::LoginAsGuest(DictionaryValue* args,
155                                             IPC::Message* reply_message) {
156  chromeos::ExistingUserController* controller =
157      chromeos::ExistingUserController::current_controller();
158  // Set up an observer (it will delete itself).
159  new LoginManagerObserver(this, reply_message);
160  controller->LoginAsGuest();
161}
162
163void TestingAutomationProvider::Login(DictionaryValue* args,
164                                      IPC::Message* reply_message) {
165  std::string username, password;
166  if (!args->GetString("username", &username) ||
167      !args->GetString("password", &password)) {
168    AutomationJSONReply(this, reply_message).SendError(
169        "Invalid or missing args.");
170    return;
171  }
172
173  chromeos::ExistingUserController* controller =
174      chromeos::ExistingUserController::current_controller();
175  // Set up an observer (it will delete itself).
176  new LoginManagerObserver(this, reply_message);
177  controller->Login(username, password);
178}
179
180void TestingAutomationProvider::LockScreen(DictionaryValue* args,
181                                           IPC::Message* reply_message) {
182  if (!EnsureCrosLibraryLoaded(this, reply_message))
183    return;
184
185  new ScreenLockUnlockObserver(this, reply_message, true);
186  CrosLibrary::Get()->GetScreenLockLibrary()->
187      NotifyScreenLockRequested();
188}
189
190void TestingAutomationProvider::UnlockScreen(DictionaryValue* args,
191                                             IPC::Message* reply_message) {
192  if (!EnsureCrosLibraryLoaded(this, reply_message))
193    return;
194
195  new ScreenLockUnlockObserver(this, reply_message, false);
196  CrosLibrary::Get()->GetScreenLockLibrary()->
197      NotifyScreenUnlockRequested();
198}
199
200// Signing out could have undesirable side effects: session_manager is
201// killed, so its children, including chrome and the window manager, will
202// also be killed. Anything owned by chronos will probably be killed.
203void TestingAutomationProvider::SignoutInScreenLocker(
204    DictionaryValue* args, IPC::Message* reply_message) {
205  AutomationJSONReply reply(this, reply_message);
206  chromeos::ScreenLocker* screen_locker =
207      chromeos::ScreenLocker::default_screen_locker();
208  if (!screen_locker) {
209    reply.SendError(
210        "No default screen locker. Are you sure the screen is locked?");
211    return;
212  }
213
214  // Send success before stopping session because if we're a child of
215  // session manager then we'll die when the session is stopped.
216  reply.SendSuccess(NULL);
217  screen_locker->Signout();
218}
219
220void TestingAutomationProvider::GetBatteryInfo(DictionaryValue* args,
221                                               IPC::Message* reply_message) {
222  if (!EnsureCrosLibraryLoaded(this, reply_message))
223    return;
224
225  chromeos::PowerLibrary* power_library = CrosLibrary::Get()->GetPowerLibrary();
226  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
227
228  return_value->SetBoolean("battery_is_present",
229                           power_library->battery_is_present());
230  return_value->SetBoolean("line_power_on", power_library->line_power_on());
231  if (power_library->battery_is_present()) {
232    return_value->SetBoolean("battery_fully_charged",
233                             power_library->battery_fully_charged());
234    return_value->SetDouble("battery_percentage",
235                            power_library->battery_percentage());
236    if (power_library->line_power_on()) {
237      int time = power_library->battery_time_to_full().InSeconds();
238      if (time > 0 || power_library->battery_fully_charged())
239        return_value->SetInteger("battery_time_to_full", time);
240    } else {
241      int time = power_library->battery_time_to_empty().InSeconds();
242      if (time > 0)
243        return_value->SetInteger("battery_time_to_empty", time);
244    }
245  }
246
247  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
248}
249
250void TestingAutomationProvider::GetNetworkInfo(DictionaryValue* args,
251                                               IPC::Message* reply_message) {
252  if (!EnsureCrosLibraryLoaded(this, reply_message))
253    return;
254
255  NetworkLibrary* network_library = CrosLibrary::Get()->GetNetworkLibrary();
256  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
257
258  // IP address.
259  return_value->SetString("ip_address", network_library->IPAddress());
260
261  // Currently connected networks.
262  if (network_library->ethernet_network())
263    return_value->SetString(
264        "connected_ethernet",
265        network_library->ethernet_network()->service_path());
266  if (network_library->wifi_network())
267    return_value->SetString("connected_wifi",
268                            network_library->wifi_network()->service_path());
269  if (network_library->cellular_network())
270    return_value->SetString(
271        "connected_cellular",
272        network_library->cellular_network()->service_path());
273
274  // Ethernet network.
275  bool ethernet_available = network_library->ethernet_available();
276  bool ethernet_enabled = network_library->ethernet_enabled();
277  if (ethernet_available && ethernet_enabled) {
278    const chromeos::EthernetNetwork* ethernet_network =
279        network_library->ethernet_network();
280    if (ethernet_network) {
281      DictionaryValue* items = new DictionaryValue;
282      DictionaryValue* item = GetNetworkInfoDict(ethernet_network);
283      items->Set(ethernet_network->service_path(), item);
284      return_value->Set("ethernet_networks", items);
285    }
286  }
287
288  // Wi-fi networks.
289  bool wifi_available = network_library->wifi_available();
290  bool wifi_enabled = network_library->wifi_enabled();
291  if (wifi_available && wifi_enabled) {
292    const chromeos::WifiNetworkVector& wifi_networks =
293        network_library->wifi_networks();
294    DictionaryValue* items = new DictionaryValue;
295    for (chromeos::WifiNetworkVector::const_iterator iter =
296         wifi_networks.begin(); iter != wifi_networks.end(); ++iter) {
297      const chromeos::WifiNetwork* wifi = *iter;
298      DictionaryValue* item = GetNetworkInfoDict(wifi);
299      item->SetInteger("strength", wifi->strength());
300      item->SetBoolean("encrypted", wifi->encrypted());
301      item->SetString("encryption", wifi->GetEncryptionString());
302      items->Set(wifi->service_path(), item);
303    }
304    return_value->Set("wifi_networks", items);
305  }
306
307  // Cellular networks.
308  bool cellular_available = network_library->cellular_available();
309  bool cellular_enabled = network_library->cellular_enabled();
310  if (cellular_available && cellular_enabled) {
311    const chromeos::CellularNetworkVector& cellular_networks =
312        network_library->cellular_networks();
313    DictionaryValue* items = new DictionaryValue;
314    for (size_t i = 0; i < cellular_networks.size(); ++i) {
315      DictionaryValue* item = GetNetworkInfoDict(cellular_networks[i]);
316      item->SetInteger("strength", cellular_networks[i]->strength());
317      item->SetString("operator_name", cellular_networks[i]->operator_name());
318      item->SetString("operator_code", cellular_networks[i]->operator_code());
319      item->SetString("payment_url", cellular_networks[i]->payment_url());
320      item->SetString("usage_url", cellular_networks[i]->usage_url());
321      item->SetString("network_technology",
322                      cellular_networks[i]->GetNetworkTechnologyString());
323      item->SetString("connectivity_state",
324                      cellular_networks[i]->GetConnectivityStateString());
325      item->SetString("activation_state",
326                      cellular_networks[i]->GetActivationStateString());
327      item->SetString("roaming_state",
328                      cellular_networks[i]->GetRoamingStateString());
329      items->Set(cellular_networks[i]->service_path(), item);
330    }
331    return_value->Set("cellular_networks", items);
332  }
333
334  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
335}
336
337void TestingAutomationProvider::NetworkScan(DictionaryValue* args,
338                                            IPC::Message* reply_message) {
339  if (!EnsureCrosLibraryLoaded(this, reply_message))
340    return;
341
342  NetworkLibrary* network_library = CrosLibrary::Get()->GetNetworkLibrary();
343  network_library->RequestNetworkScan();
344
345  // Set up an observer (it will delete itself).
346  new NetworkScanObserver(this, reply_message);
347}
348
349void TestingAutomationProvider::GetProxySettings(DictionaryValue* args,
350                                                 IPC::Message* reply_message) {
351  const char* settings[] = { "pacurl", "singlehttp", "singlehttpport",
352                             "httpurl", "httpport", "httpsurl", "httpsport",
353                             "type", "single", "ftpurl", "ftpport",
354                             "socks", "socksport", "ignorelist" };
355
356  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
357  chromeos::ProxyCrosSettingsProvider settings_provider;
358
359  for (size_t i = 0; i < arraysize(settings); ++i) {
360    Value* setting = GetProxySetting(settings[i]);
361    if (setting)
362      return_value->Set(settings[i], setting);
363  }
364
365  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
366}
367
368void TestingAutomationProvider::SetProxySettings(DictionaryValue* args,
369                                                IPC::Message* reply_message) {
370  AutomationJSONReply reply(this, reply_message);
371  std::string key;
372  Value* value;
373  if (!args->GetString("key", &key) || !args->Get("value", &value)) {
374    reply.SendError("Invalid or missing args.");
375    return;
376  }
377
378  std::string setting_path = "cros.session.proxy.";
379  setting_path.append(key);
380
381  // ProxyCrosSettingsProvider will own the Value* passed to Set().
382  chromeos::ProxyCrosSettingsProvider().Set(setting_path, value->DeepCopy());
383  reply.SendSuccess(NULL);
384}
385
386void TestingAutomationProvider::ConnectToWifiNetwork(
387    DictionaryValue* args, IPC::Message* reply_message) {
388  if (!EnsureCrosLibraryLoaded(this, reply_message))
389    return;
390
391  AutomationJSONReply reply(this, reply_message);
392  std::string service_path, password, identity, certpath;
393  if (!args->GetString("service_path", &service_path) ||
394      !args->GetString("password", &password) ||
395      !args->GetString("identity", &identity) ||
396      !args->GetString("certpath", &certpath)) {
397    reply.SendError("Invalid or missing args.");
398    return;
399  }
400
401  NetworkLibrary* network_library = CrosLibrary::Get()->GetNetworkLibrary();
402  chromeos::WifiNetwork* wifi =
403      network_library->FindWifiNetworkByPath(service_path);
404  if (!wifi) {
405    reply.SendError("No network found with specified service path.");
406    return;
407  }
408  if (!password.empty())
409    wifi->SetPassphrase(password);
410  if (!identity.empty())
411    wifi->SetIdentity(identity);
412  if (!certpath.empty())
413    wifi->SetCertPath(certpath);
414
415  // Set up an observer (it will delete itself).
416  new ServicePathConnectObserver(this, reply_message, service_path);
417
418  network_library->ConnectToWifiNetwork(wifi);
419  network_library->RequestNetworkScan();
420}
421
422void TestingAutomationProvider::ConnectToHiddenWifiNetwork(
423    DictionaryValue* args, IPC::Message* reply_message) {
424  if (!CrosLibrary::Get()->EnsureLoaded()) {
425    AutomationJSONReply(this, reply_message)
426        .SendError("Could not load cros library.");
427    return;
428  }
429
430  std::string ssid, security, password, identity, certpath;
431  if (!args->GetString("ssid", &ssid) ||
432      !args->GetString("security", &security) ||
433      !args->GetString("password", &password) ||
434      !args->GetString("identity", &identity) ||
435      !args->GetString("certpath", &certpath)) {
436    AutomationJSONReply(this, reply_message)
437        .SendError("Invalid or missing args.");
438    return;
439  }
440
441  std::map<std::string, chromeos::ConnectionSecurity> connection_security_map;
442  connection_security_map["SECURITY_NONE"] = chromeos::SECURITY_NONE;
443  connection_security_map["SECURITY_WEP"] = chromeos::SECURITY_WEP;
444  connection_security_map["SECURITY_WPA"] = chromeos::SECURITY_WPA;
445  connection_security_map["SECURITY_RSN"] = chromeos::SECURITY_RSN;
446  connection_security_map["SECURITY_8021X"] = chromeos::SECURITY_8021X;
447
448  if (connection_security_map.find(security) == connection_security_map.end()) {
449    AutomationJSONReply(this, reply_message)
450        .SendError("Unknown security type.");
451    return;
452  }
453  chromeos::ConnectionSecurity connection_security =
454      connection_security_map[security];
455
456  NetworkLibrary* network_library = CrosLibrary::Get()->GetNetworkLibrary();
457
458  // Set up an observer (it will delete itself).
459  new SSIDConnectObserver(this, reply_message, ssid);
460
461  network_library->ConnectToWifiNetwork(connection_security, ssid, password,
462                                        identity, certpath);
463}
464
465void TestingAutomationProvider::DisconnectFromWifiNetwork(
466    DictionaryValue* args, IPC::Message* reply_message) {
467  if (!EnsureCrosLibraryLoaded(this, reply_message))
468    return;
469
470  AutomationJSONReply reply(this, reply_message);
471  NetworkLibrary* network_library = CrosLibrary::Get()->GetNetworkLibrary();
472  const chromeos::WifiNetwork* wifi = network_library->wifi_network();
473  if (!wifi) {
474    reply.SendError("Not connected to any wifi network.");
475    return;
476  }
477
478  network_library->DisconnectFromNetwork(wifi);
479  reply.SendSuccess(NULL);
480}
481
482void TestingAutomationProvider::GetUpdateInfo(DictionaryValue* args,
483                                              IPC::Message* reply_message) {
484  if (!EnsureCrosLibraryLoaded(this, reply_message))
485    return;
486
487  UpdateLibrary* update_library = CrosLibrary::Get()->GetUpdateLibrary();
488  AutomationJSONReply* reply = new AutomationJSONReply(this, reply_message);
489  update_library->GetReleaseTrack(GetReleaseTrackCallback, reply);
490}
491
492void TestingAutomationProvider::UpdateCheck(
493    DictionaryValue* args,
494    IPC::Message* reply_message) {
495  if (!EnsureCrosLibraryLoaded(this, reply_message))
496    return;
497
498  UpdateLibrary* update_library = CrosLibrary::Get()->GetUpdateLibrary();
499  AutomationJSONReply* reply = new AutomationJSONReply(this, reply_message);
500  update_library->RequestUpdateCheck(UpdateCheckCallback, reply);
501}
502
503void TestingAutomationProvider::SetReleaseTrack(DictionaryValue* args,
504                                                IPC::Message* reply_message) {
505  if (!EnsureCrosLibraryLoaded(this, reply_message))
506    return;
507
508  AutomationJSONReply reply(this, reply_message);
509  std::string track;
510  if (!args->GetString("track", &track)) {
511    reply.SendError("Invalid or missing args.");
512    return;
513  }
514
515  UpdateLibrary* update_library = CrosLibrary::Get()->GetUpdateLibrary();
516  update_library->SetReleaseTrack(track);
517  reply.SendSuccess(NULL);
518}
519