Home | History | Annotate | Download | only in automation
      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/automation/testing_automation_provider.h"
      6 
      7 #include "ash/new_window_delegate.h"
      8 #include "ash/shell.h"
      9 #include "ash/system/tray/system_tray_delegate.h"
     10 #include "base/command_line.h"
     11 #include "base/i18n/time_formatting.h"
     12 #include "base/prefs/pref_service.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "base/time/time.h"
     16 #include "chrome/browser/automation/automation_provider_json.h"
     17 #include "chrome/browser/automation/automation_provider_observers.h"
     18 #include "chrome/browser/automation/automation_util.h"
     19 #include "chrome/browser/browser_process.h"
     20 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
     21 #include "chrome/browser/chromeos/accessibility/accessibility_util.h"
     22 #include "chrome/browser/chromeos/login/default_user_images.h"
     23 #include "chrome/browser/chromeos/login/enrollment/enrollment_screen.h"
     24 #include "chrome/browser/chromeos/login/existing_user_controller.h"
     25 #include "chrome/browser/chromeos/login/login_display.h"
     26 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
     27 #include "chrome/browser/chromeos/login/screen_locker.h"
     28 #include "chrome/browser/chromeos/login/screens/eula_screen.h"
     29 #include "chrome/browser/chromeos/login/screens/network_screen.h"
     30 #include "chrome/browser/chromeos/login/screens/update_screen.h"
     31 #include "chrome/browser/chromeos/login/screens/user_image_screen.h"
     32 #include "chrome/browser/chromeos/login/startup_utils.h"
     33 #include "chrome/browser/chromeos/login/webui_login_display.h"
     34 #include "chrome/browser/chromeos/login/wizard_controller.h"
     35 #include "chrome/browser/chromeos/net/proxy_config_handler.h"
     36 #include "chrome/browser/chromeos/settings/cros_settings.h"
     37 #include "chrome/browser/prefs/proxy_config_dictionary.h"
     38 #include "chrome/browser/ui/browser.h"
     39 #include "chrome/browser/ui/browser_window.h"
     40 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
     41 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
     42 #include "chrome/common/pref_names.h"
     43 #include "chromeos/audio/cras_audio_handler.h"
     44 #include "chromeos/dbus/dbus_thread_manager.h"
     45 #include "chromeos/dbus/session_manager_client.h"
     46 #include "chromeos/dbus/update_engine_client.h"
     47 #include "chromeos/settings/cros_settings_names.h"
     48 #include "chromeos/settings/timezone_settings.h"
     49 #include "content/public/browser/web_contents.h"
     50 #include "policy/policy_constants.h"
     51 #include "ui/views/widget/widget.h"
     52 
     53 using chromeos::DBusThreadManager;
     54 using chromeos::ExistingUserController;
     55 using chromeos::UpdateEngineClient;
     56 using chromeos::User;
     57 using chromeos::UserManager;
     58 using chromeos::WizardController;
     59 
     60 namespace {
     61 
     62 void UpdateCheckCallback(AutomationJSONReply* reply,
     63                          UpdateEngineClient::UpdateCheckResult result) {
     64   if (result == UpdateEngineClient::UPDATE_RESULT_SUCCESS)
     65     reply->SendSuccess(NULL);
     66   else
     67     reply->SendError("update check failed");
     68   delete reply;
     69 }
     70 
     71 }  // namespace
     72 
     73 #if defined(OS_CHROMEOS)
     74 void TestingAutomationProvider::PowerChanged(
     75     const power_manager::PowerSupplyProperties& proto) {
     76   power_supply_properties_ = proto;
     77 }
     78 #endif
     79 
     80 void TestingAutomationProvider::AcceptOOBENetworkScreen(
     81     DictionaryValue* args,
     82     IPC::Message* reply_message) {
     83   WizardController* wizard_controller = WizardController::default_controller();
     84   if (!wizard_controller || wizard_controller->current_screen()->GetName() !=
     85           WizardController::kNetworkScreenName) {
     86     AutomationJSONReply(this, reply_message).SendError(
     87         "Network screen not active.");
     88     return;
     89   }
     90   // Observer will delete itself.
     91   new WizardControllerObserver(wizard_controller, this, reply_message);
     92   wizard_controller->GetNetworkScreen()->OnContinuePressed();
     93 }
     94 
     95 void TestingAutomationProvider::AcceptOOBEEula(DictionaryValue* args,
     96                                                IPC::Message* reply_message) {
     97   bool accepted;
     98   bool usage_stats_reporting;
     99   if (!args->GetBoolean("accepted", &accepted) ||
    100       !args->GetBoolean("usage_stats_reporting", &usage_stats_reporting)) {
    101     AutomationJSONReply(this, reply_message).SendError(
    102         "Invalid or missing args.");
    103     return;
    104   }
    105 
    106   WizardController* wizard_controller = WizardController::default_controller();
    107   if (!wizard_controller || wizard_controller->current_screen()->GetName() !=
    108       WizardController::kEulaScreenName) {
    109     AutomationJSONReply(this, reply_message).SendError(
    110         "EULA screen not active.");
    111     return;
    112   }
    113   // Observer will delete itself.
    114   new WizardControllerObserver(wizard_controller, this, reply_message);
    115   wizard_controller->GetEulaScreen()->OnExit(accepted, usage_stats_reporting);
    116 }
    117 
    118 void TestingAutomationProvider::CancelOOBEUpdate(DictionaryValue* args,
    119                                                  IPC::Message* reply_message) {
    120   if (chromeos::StartupUtils::IsOobeCompleted()) {
    121     // Update already finished.
    122     scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    123     return_value->SetString("next_screen",
    124                             WizardController::kLoginScreenName);
    125     AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
    126     return;
    127   }
    128   WizardController* wizard_controller = WizardController::default_controller();
    129   if (!wizard_controller || wizard_controller->current_screen()->GetName() !=
    130           WizardController::kUpdateScreenName) {
    131     AutomationJSONReply(this, reply_message).SendError(
    132         "Update screen not active.");
    133     return;
    134   }
    135   // Observer will delete itself.
    136   new WizardControllerObserver(wizard_controller, this, reply_message);
    137   wizard_controller->GetUpdateScreen()->CancelUpdate();
    138 }
    139 
    140 void TestingAutomationProvider::GetLoginInfo(DictionaryValue* args,
    141                                              IPC::Message* reply_message) {
    142   AutomationJSONReply reply(this, reply_message);
    143   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    144 
    145   const UserManager* user_manager = UserManager::Get();
    146   if (!user_manager)
    147     reply.SendError("No user manager!");
    148   const chromeos::ScreenLocker* screen_locker =
    149       chromeos::ScreenLocker::default_screen_locker();
    150 
    151   return_value->SetString("login_ui_type", "webui");
    152   return_value->SetBoolean("is_owner", user_manager->IsCurrentUserOwner());
    153   return_value->SetBoolean("is_logged_in", user_manager->IsUserLoggedIn());
    154   return_value->SetBoolean("is_screen_locked", screen_locker);
    155   if (user_manager->IsUserLoggedIn()) {
    156     const User* user = user_manager->GetLoggedInUser();
    157     return_value->SetBoolean("is_guest", user_manager->IsLoggedInAsGuest());
    158     return_value->SetString("email", user->email());
    159     return_value->SetString("display_email", user->display_email());
    160     switch (user->image_index()) {
    161       case User::kExternalImageIndex:
    162         return_value->SetString("user_image", "file");
    163         break;
    164 
    165       case User::kProfileImageIndex:
    166         return_value->SetString("user_image", "profile");
    167         break;
    168 
    169       default:
    170         return_value->SetInteger("user_image", user->image_index());
    171         break;
    172     }
    173   }
    174 
    175   reply.SendSuccess(return_value.get());
    176 }
    177 
    178 // See the note under LoginAsGuest(). CreateAccount() causes a login as guest.
    179 void TestingAutomationProvider::ShowCreateAccountUI(
    180     DictionaryValue* args, IPC::Message* reply_message) {
    181   ExistingUserController* controller =
    182       ExistingUserController::current_controller();
    183   // Return immediately, since we're going to die before the login is finished.
    184   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
    185   controller->CreateAccount();
    186 }
    187 
    188 // Logging in as guest will cause session_manager to restart Chrome with new
    189 // flags. If you used EnableChromeTesting, you will have to call it again.
    190 void TestingAutomationProvider::LoginAsGuest(DictionaryValue* args,
    191                                              IPC::Message* reply_message) {
    192   ExistingUserController* controller =
    193       ExistingUserController::current_controller();
    194   // Return immediately, since we're going to die before the login is finished.
    195   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
    196   controller->LoginAsGuest();
    197 }
    198 
    199 void TestingAutomationProvider::SubmitLoginForm(DictionaryValue* args,
    200                                                 IPC::Message* reply_message) {
    201   AutomationJSONReply reply(this, reply_message);
    202 
    203   std::string username, password;
    204   if (!args->GetString("username", &username) ||
    205       !args->GetString("password", &password)) {
    206     reply.SendError("Invalid or missing args.");
    207     return;
    208   }
    209 
    210   chromeos::ExistingUserController* controller =
    211       chromeos::ExistingUserController::current_controller();
    212   if (!controller) {
    213     reply.SendError("Unable to access ExistingUserController");
    214     return;
    215   }
    216 
    217   // WebUI login.
    218   chromeos::WebUILoginDisplay* webui_login_display =
    219       static_cast<chromeos::WebUILoginDisplay*>(controller->login_display());
    220   VLOG(2) << "TestingAutomationProvider::SubmitLoginForm "
    221           << "ShowSigninScreenForCreds(" << username << ", " << password << ")";
    222 
    223   webui_login_display->ShowSigninScreenForCreds(username, password);
    224   reply.SendSuccess(NULL);
    225 }
    226 
    227 void TestingAutomationProvider::AddLoginEventObserver(
    228     DictionaryValue* args, IPC::Message* reply_message) {
    229   ExistingUserController* controller =
    230       ExistingUserController::current_controller();
    231   AutomationJSONReply reply(this, reply_message);
    232   if (!controller) {
    233     // This may happen due to SkipToLogin not being called.
    234     reply.SendError("Unable to access ExistingUserController");
    235     return;
    236   }
    237 
    238   if (!automation_event_queue_.get())
    239     automation_event_queue_.reset(new AutomationEventQueue);
    240 
    241   int observer_id = automation_event_queue_->AddObserver(
    242       new LoginEventObserver(automation_event_queue_.get(), this));
    243 
    244   // Return the observer's id.
    245   DictionaryValue return_value;
    246   return_value.SetInteger("observer_id", observer_id);
    247   reply.SendSuccess(&return_value);
    248 }
    249 
    250 void TestingAutomationProvider::SignOut(DictionaryValue* args,
    251                                         IPC::Message* reply_message) {
    252   ash::Shell::GetInstance()->system_tray_delegate()->SignOut();
    253   // Sign out has the side effect of restarting the session_manager
    254   // and chrome, thereby severing the automation channel, so it's
    255   // not really necessary to send a reply back. The next line is
    256   // for consistency with other methods.
    257   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
    258 }
    259 
    260 void TestingAutomationProvider::PickUserImage(DictionaryValue* args,
    261                                               IPC::Message* reply_message) {
    262   std::string image_type;
    263   int image_number = -1;
    264   if (!args->GetString("image", &image_type)
    265       && !args->GetInteger("image", &image_number)) {
    266     AutomationJSONReply(this, reply_message).SendError(
    267         "Invalid or missing args.");
    268     return;
    269   }
    270   WizardController* wizard_controller = WizardController::default_controller();
    271   if (!wizard_controller || wizard_controller->current_screen()->GetName() !=
    272           WizardController::kUserImageScreenName) {
    273     AutomationJSONReply(this, reply_message).SendError(
    274         "User image screen not active.");
    275     return;
    276   }
    277   chromeos::UserImageScreen* image_screen =
    278       wizard_controller->GetUserImageScreen();
    279   // Observer will delete itself unless error is returned.
    280   WizardControllerObserver* observer =
    281       new WizardControllerObserver(wizard_controller, this, reply_message);
    282   if (image_type == "profile") {
    283     image_screen->OnImageSelected("", image_type, true);
    284     image_screen->OnImageAccepted();
    285   } else if (image_type.empty() && image_number >= 0 &&
    286              image_number < chromeos::kDefaultImagesCount) {
    287     image_screen->OnImageSelected(
    288         chromeos::GetDefaultImageUrl(image_number), image_type, true);
    289     image_screen->OnImageAccepted();
    290   } else {
    291     AutomationJSONReply(this, reply_message).SendError(
    292         "Invalid or missing args.");
    293     delete observer;
    294     return;
    295   }
    296 }
    297 
    298 void TestingAutomationProvider::SkipToLogin(DictionaryValue* args,
    299                                             IPC::Message* reply_message) {
    300   bool skip_post_login_screens;
    301   // The argument name is a legacy. If set to |true|, this argument causes any
    302   // screens that may otherwise be shown after login (registration, Terms of
    303   // Service, user image selection) to be skipped.
    304   if (!args->GetBoolean("skip_image_selection", &skip_post_login_screens)) {
    305     AutomationJSONReply reply(this, reply_message);
    306     reply.SendError("Invalid or missing args.");
    307     return;
    308   }
    309   if (skip_post_login_screens)
    310     WizardController::SkipPostLoginScreensForTesting();
    311 
    312   WizardController* wizard_controller = WizardController::default_controller();
    313   if (!wizard_controller) {
    314     AutomationJSONReply reply(this, reply_message);
    315     if (ExistingUserController::current_controller()) {
    316       // Already at login screen.
    317       scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    318       return_value->SetString("next_screen",
    319                               WizardController::kLoginScreenName);
    320       reply.SendSuccess(return_value.get());
    321     } else {
    322       reply.SendError("OOBE not active.");
    323     }
    324     return;
    325   }
    326 
    327   // Observer will delete itself.
    328   WizardControllerObserver* observer =
    329       new WizardControllerObserver(wizard_controller, this, reply_message);
    330   observer->set_screen_to_wait_for(WizardController::kLoginScreenName);
    331   wizard_controller->SkipToLoginForTesting(chromeos::LoginScreenContext());
    332 }
    333 
    334 void TestingAutomationProvider::GetOOBEScreenInfo(DictionaryValue* args,
    335                                                   IPC::Message* reply_message) {
    336   static const char kScreenNameKey[] = "screen_name";
    337   AutomationJSONReply reply(this, reply_message);
    338   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    339 
    340   WizardController* wizard_controller = WizardController::default_controller();
    341   if (wizard_controller) {
    342     if (wizard_controller->login_screen_started()) {
    343       return_value->SetString(kScreenNameKey,
    344                               WizardController::kLoginScreenName);
    345     } else {
    346       return_value->SetString(kScreenNameKey,
    347                               wizard_controller->current_screen()->GetName());
    348     }
    349   } else if (ExistingUserController::current_controller()) {
    350     return_value->SetString(kScreenNameKey, WizardController::kLoginScreenName);
    351   } else {
    352     // Already logged in.
    353     reply.SendSuccess(NULL);
    354     return;
    355   }
    356   reply.SendSuccess(return_value.get());
    357 }
    358 
    359 void TestingAutomationProvider::LockScreen(DictionaryValue* args,
    360                                            IPC::Message* reply_message) {
    361   new ScreenLockUnlockObserver(this, reply_message, true);
    362   DBusThreadManager::Get()->GetSessionManagerClient()->RequestLockScreen();
    363 }
    364 
    365 void TestingAutomationProvider::UnlockScreen(DictionaryValue* args,
    366                                              IPC::Message* reply_message) {
    367   std::string password;
    368   if (!args->GetString("password", &password)) {
    369     AutomationJSONReply(this, reply_message).SendError(
    370         "Invalid or missing args.");
    371     return;
    372   }
    373 
    374   chromeos::ScreenLocker* screen_locker =
    375       chromeos::ScreenLocker::default_screen_locker();
    376   if (!screen_locker) {
    377     AutomationJSONReply(this, reply_message).SendError(
    378         "No default screen locker. Are you sure the screen is locked?");
    379     return;
    380   }
    381 
    382   new ScreenUnlockObserver(this, reply_message);
    383   screen_locker->AuthenticateByPassword(password);
    384 }
    385 
    386 // Signing out could have undesirable side effects: session_manager is
    387 // killed, so its children, including chrome and the window manager, will
    388 // also be killed. Anything owned by chronos will probably be killed.
    389 void TestingAutomationProvider::SignoutInScreenLocker(
    390     DictionaryValue* args, IPC::Message* reply_message) {
    391   AutomationJSONReply reply(this, reply_message);
    392   chromeos::ScreenLocker* screen_locker =
    393       chromeos::ScreenLocker::default_screen_locker();
    394   if (!screen_locker) {
    395     reply.SendError(
    396         "No default screen locker. Are you sure the screen is locked?");
    397     return;
    398   }
    399 
    400   // Send success before stopping session because if we're a child of
    401   // session manager then we'll die when the session is stopped.
    402   reply.SendSuccess(NULL);
    403   screen_locker->Signout();
    404 }
    405 
    406 void TestingAutomationProvider::GetBatteryInfo(DictionaryValue* args,
    407                                                IPC::Message* reply_message) {
    408   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    409 
    410   const bool battery_is_present = power_supply_properties_.battery_state() !=
    411       power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT;
    412   const bool line_power_on = power_supply_properties_.external_power() !=
    413       power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED;
    414 
    415   return_value->SetBoolean("battery_is_present", battery_is_present);
    416   return_value->SetBoolean("line_power_on", line_power_on);
    417 
    418   if (battery_is_present) {
    419     const bool battery_is_full = power_supply_properties_.battery_state() ==
    420         power_manager::PowerSupplyProperties_BatteryState_FULL;
    421     return_value->SetBoolean("battery_fully_charged", battery_is_full);
    422     return_value->SetDouble("battery_percentage",
    423                             power_supply_properties_.battery_percent());
    424     if (line_power_on) {
    425       int64 time = power_supply_properties_.battery_time_to_full_sec();
    426       if (time > 0 || battery_is_full)
    427         return_value->SetInteger("battery_seconds_to_full", time);
    428     } else {
    429       int64 time = power_supply_properties_.battery_time_to_empty_sec();
    430       if (time > 0)
    431         return_value->SetInteger("battery_seconds_to_empty", time);
    432     }
    433   }
    434 
    435   AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
    436 }
    437 
    438 void TestingAutomationProvider::ExecuteJavascriptInOOBEWebUI(
    439     DictionaryValue* args, IPC::Message* reply_message) {
    440   std::string javascript, frame_xpath;
    441   if (!args->GetString("javascript", &javascript)) {
    442     AutomationJSONReply(this, reply_message)
    443         .SendError("'javascript' missing or invalid");
    444     return;
    445   }
    446   if (!args->GetString("frame_xpath", &frame_xpath)) {
    447     AutomationJSONReply(this, reply_message)
    448         .SendError("'frame_xpath' missing or invalid");
    449     return;
    450   }
    451   const UserManager* user_manager = UserManager::Get();
    452   if (!user_manager) {
    453     AutomationJSONReply(this, reply_message).SendError(
    454         "No user manager!");
    455     return;
    456   }
    457   if (user_manager->IsUserLoggedIn()) {
    458     AutomationJSONReply(this, reply_message).SendError(
    459         "User is already logged in.");
    460     return;
    461   }
    462   ExistingUserController* controller =
    463       ExistingUserController::current_controller();
    464   if (!controller) {
    465     AutomationJSONReply(this, reply_message).SendError(
    466         "Unable to access ExistingUserController");
    467     return;
    468   }
    469   chromeos::LoginDisplayHostImpl* webui_host =
    470       static_cast<chromeos::LoginDisplayHostImpl*>(
    471           controller->login_display_host());
    472   content::WebContents* web_contents =
    473       webui_host->GetOobeUI()->web_ui()->GetWebContents();
    474 
    475   new DomOperationMessageSender(this, reply_message, true);
    476   ExecuteJavascriptInRenderViewFrame(ASCIIToUTF16(frame_xpath),
    477                                      ASCIIToUTF16(javascript),
    478                                      reply_message,
    479                                      web_contents->GetRenderViewHost());
    480 }
    481 
    482 void TestingAutomationProvider::EnableSpokenFeedback(
    483     DictionaryValue* args, IPC::Message* reply_message) {
    484   AutomationJSONReply reply(this, reply_message);
    485   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    486   bool enabled;
    487   if (!args->GetBoolean("enabled", &enabled)) {
    488     reply.SendError("Invalid or missing args.");
    489     return;
    490   }
    491   chromeos::AccessibilityManager::Get()->EnableSpokenFeedback(
    492       enabled, ash::A11Y_NOTIFICATION_NONE);
    493 
    494   reply.SendSuccess(return_value.get());
    495 }
    496 
    497 void TestingAutomationProvider::IsSpokenFeedbackEnabled(
    498     DictionaryValue* args, IPC::Message* reply_message) {
    499   AutomationJSONReply reply(this, reply_message);
    500   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    501   return_value->SetBoolean(
    502       "spoken_feedback",
    503       chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
    504   reply.SendSuccess(return_value.get());
    505 }
    506 
    507 void TestingAutomationProvider::GetTimeInfo(Browser* browser,
    508                                             DictionaryValue* args,
    509                                             IPC::Message* reply_message) {
    510   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    511   base::Time time(base::Time::Now());
    512   bool use_24hour_clock = browser && browser->profile()->GetPrefs()->GetBoolean(
    513       prefs::kUse24HourClock);
    514   base::HourClockType hour_clock_type =
    515       use_24hour_clock ? base::k24HourClock : base::k12HourClock;
    516   base::string16 display_time = base::TimeFormatTimeOfDayWithHourClockType(
    517       time, hour_clock_type, base::kDropAmPm);
    518   base::string16 timezone =
    519       chromeos::system::TimezoneSettings::GetInstance()->GetCurrentTimezoneID();
    520   return_value->SetString("display_time", display_time);
    521   return_value->SetString("display_date", base::TimeFormatFriendlyDate(time));
    522   return_value->SetString("timezone", timezone);
    523   AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
    524 }
    525 
    526 void TestingAutomationProvider::GetTimeInfo(DictionaryValue* args,
    527                                             IPC::Message* reply_message) {
    528   GetTimeInfo(NULL, args, reply_message);
    529 }
    530 
    531 void TestingAutomationProvider::SetTimezone(DictionaryValue* args,
    532                                             IPC::Message* reply_message) {
    533   AutomationJSONReply reply(this, reply_message);
    534   std::string timezone_id;
    535   if (!args->GetString("timezone", &timezone_id)) {
    536     reply.SendError("Invalid or missing args.");
    537     return;
    538   }
    539   chromeos::CrosSettings* settings = chromeos::CrosSettings::Get();
    540   settings->SetString(chromeos::kSystemTimezone, timezone_id);
    541   reply.SendSuccess(NULL);
    542 }
    543 
    544 void TestingAutomationProvider::UpdateCheck(
    545     DictionaryValue* args,
    546     IPC::Message* reply_message) {
    547   AutomationJSONReply* reply = new AutomationJSONReply(this, reply_message);
    548   DBusThreadManager::Get()->GetUpdateEngineClient()
    549       ->RequestUpdateCheck(base::Bind(UpdateCheckCallback, reply));
    550 }
    551 
    552 void TestingAutomationProvider::GetVolumeInfo(DictionaryValue* args,
    553                                               IPC::Message* reply_message) {
    554   AutomationJSONReply reply(this, reply_message);
    555   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    556   chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
    557   if (!audio_handler) {
    558     reply.SendError("CrasAudioHandler not initialized.");
    559     return;
    560   }
    561   return_value->SetDouble("volume", audio_handler->GetOutputVolumePercent());
    562   return_value->SetBoolean("is_mute", audio_handler->IsOutputMuted());
    563   reply.SendSuccess(return_value.get());
    564 }
    565 
    566 void TestingAutomationProvider::SetVolume(DictionaryValue* args,
    567                                           IPC::Message* reply_message) {
    568   AutomationJSONReply reply(this, reply_message);
    569   double volume_percent;
    570   if (!args->GetDouble("volume", &volume_percent)) {
    571     reply.SendError("Invalid or missing args.");
    572     return;
    573   }
    574   chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
    575   if (!audio_handler) {
    576     reply.SendError("CrasAudioHandler not initialized.");
    577     return;
    578   }
    579   audio_handler->SetOutputVolumePercent(volume_percent);
    580   reply.SendSuccess(NULL);
    581 }
    582 
    583 void TestingAutomationProvider::SetMute(DictionaryValue* args,
    584                                         IPC::Message* reply_message) {
    585   AutomationJSONReply reply(this, reply_message);
    586   bool mute;
    587   if (!args->GetBoolean("mute", &mute)) {
    588     reply.SendError("Invalid or missing args.");
    589     return;
    590   }
    591   chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
    592   if (!audio_handler) {
    593     reply.SendError("CrasAudioHandler not initialized.");
    594     return;
    595   }
    596   audio_handler->SetOutputMute(mute);
    597   reply.SendSuccess(NULL);
    598 }
    599 
    600 void TestingAutomationProvider::OpenCrosh(DictionaryValue* args,
    601                                           IPC::Message* reply_message) {
    602   new NavigationNotificationObserver(
    603       NULL, this, reply_message, 1, false, true);
    604   ash::Shell::GetInstance()->new_window_delegate()->OpenCrosh();
    605 }
    606 
    607 void TestingAutomationProvider::AddChromeosObservers() {
    608   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
    609       AddObserver(this);
    610 }
    611 
    612 void TestingAutomationProvider::RemoveChromeosObservers() {
    613   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
    614       RemoveObserver(this);
    615 }
    616