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/ui/webui/chromeos/login/reset_screen_handler.h"
6
7#include <string>
8
9#include "base/command_line.h"
10#include "base/metrics/histogram.h"
11#include "base/prefs/pref_registry_simple.h"
12#include "base/prefs/pref_service.h"
13#include "base/values.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/chromeos/login/help_app_launcher.h"
16#include "chrome/browser/chromeos/reset/metrics.h"
17#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
18#include "chrome/common/pref_names.h"
19#include "chrome/grit/chromium_strings.h"
20#include "chrome/grit/generated_resources.h"
21#include "chromeos/chromeos_switches.h"
22#include "chromeos/dbus/dbus_thread_manager.h"
23#include "chromeos/dbus/power_manager_client.h"
24#include "chromeos/dbus/session_manager_client.h"
25#include "chromeos/dbus/update_engine_client.h"
26#include "content/public/browser/browser_thread.h"
27
28namespace {
29
30const char kJsScreenPath[] = "login.ResetScreen";
31
32// Reset screen id.
33const char kResetScreen[] = "reset";
34
35const int kErrorUIStateRollback = 7;
36
37}  // namespace
38
39namespace chromeos {
40
41ResetScreenHandler::ResetScreenHandler()
42    : BaseScreenHandler(kJsScreenPath),
43      delegate_(NULL),
44      show_on_init_(false),
45      restart_required_(true),
46      reboot_was_requested_(false),
47      rollback_available_(false),
48      rollback_checked_(false),
49      preparing_for_rollback_(false),
50      weak_ptr_factory_(this) {
51}
52
53ResetScreenHandler::~ResetScreenHandler() {
54  if (delegate_)
55    delegate_->OnActorDestroyed(this);
56  DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
57}
58
59void ResetScreenHandler::PrepareToShow() {
60}
61
62void ResetScreenHandler::ShowWithParams() {
63  int dialog_type;
64  if (restart_required_) {
65    dialog_type = reset::DIALOG_SHORTCUT_RESTART_REQUIRED;
66  } else {
67    dialog_type = reset::DIALOG_SHORTCUT_OFFERING_ROLLBACK_UNAVAILABLE;
68  }
69  UMA_HISTOGRAM_ENUMERATION("Reset.ChromeOS.PowerwashDialogShown",
70                            dialog_type,
71                            reset::DIALOG_VIEW_TYPE_SIZE);
72
73  PrefService* prefs = g_browser_process->local_state();
74  prefs->SetBoolean(prefs::kFactoryResetRequested, false);
75  prefs->CommitPendingWrite();
76  base::DictionaryValue reset_screen_params;
77  reset_screen_params.SetBoolean("restartRequired", restart_required_);
78  reset_screen_params.SetBoolean("rollbackAvailable", rollback_available_);
79  ShowScreen(kResetScreen, &reset_screen_params);
80}
81
82void ResetScreenHandler::Show() {
83  if (!page_is_ready()) {
84    show_on_init_ = true;
85    return;
86  }
87
88  ChooseAndApplyShowScenario();
89}
90
91void ResetScreenHandler::ChooseAndApplyShowScenario() {
92  PrefService* prefs = g_browser_process->local_state();
93  restart_required_ = !CommandLine::ForCurrentProcess()->HasSwitch(
94      switches::kFirstExecAfterBoot);
95
96  reboot_was_requested_ = false;
97  rollback_available_ = false;
98  preparing_for_rollback_ = false;
99  if (!restart_required_)  // First exec after boot.
100    reboot_was_requested_ = prefs->GetBoolean(prefs::kFactoryResetRequested);
101
102  if (!CommandLine::ForCurrentProcess()->HasSwitch(
103          switches::kEnableRollbackOption)) {
104    rollback_available_ = false;
105    ShowWithParams();
106  } else if (!restart_required_ && reboot_was_requested_) {
107    // First exec after boot.
108    chromeos::DBusThreadManager::Get()->GetUpdateEngineClient()->
109        CanRollbackCheck(base::Bind(&ResetScreenHandler::OnRollbackCheck,
110        weak_ptr_factory_.GetWeakPtr()));
111  } else {
112    // Will require restart.
113    ShowWithParams();
114  }
115}
116
117void ResetScreenHandler::Hide() {
118  DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
119}
120
121void ResetScreenHandler::SetDelegate(Delegate* delegate) {
122  delegate_ = delegate;
123  if (page_is_ready())
124    Initialize();
125}
126
127void ResetScreenHandler::DeclareLocalizedValues(
128    LocalizedValuesBuilder* builder) {
129  builder->Add("resetScreenTitle", IDS_RESET_SCREEN_TITLE);
130  builder->Add("resetScreenAccessibleTitle", IDS_RESET_SCREEN_TITLE);
131  builder->Add("resetScreenIconTitle", IDS_RESET_SCREEN_ICON_TITLE);
132  builder->Add("cancelButton", IDS_CANCEL);
133  builder->Add("resetButtonReset", IDS_RESET_SCREEN_RESET);
134  builder->Add("resetButtonRestart", IDS_RELAUNCH_BUTTON);
135  builder->Add("resetButtonPowerwash", IDS_RESET_SCREEN_POWERWASH);
136  builder->Add("resetButtonPowerwashAndRollback",
137               IDS_RESET_SCREEN_POWERWASH_AND_REVERT);
138
139  builder->Add("resetWarningDataDetails",
140               IDS_RESET_SCREEN_WARNING_DETAILS_DATA);
141  builder->Add("resetRestartMessage", IDS_RESET_SCREEN_RESTART_MSG);
142  builder->AddF("resetRevertPromise",
143                IDS_RESET_SCREEN_PREPARING_REVERT_PROMISE,
144                IDS_SHORT_PRODUCT_NAME);
145  builder->AddF("resetRevertSpinnerMessage",
146                IDS_RESET_SCREEN_PREPARING_REVERT_SPINNER_MESSAGE,
147                IDS_SHORT_PRODUCT_NAME);
148
149  // Variants for screen title.
150  builder->AddF("resetWarningTitle",
151                IDS_RESET_SCREEN_WARNING_MSG,
152                IDS_SHORT_PRODUCT_NAME);
153  builder->AddF("resetPowerwashWarningTitle",
154                IDS_RESET_SCREEN_WARNING_POWERWASH_MSG,
155                IDS_SHORT_PRODUCT_NAME);
156  builder->AddF("resetPowerwasAndRollbackhWarningTitle",
157                IDS_RESET_SCREEN_WARNING_POWERWASH_AND_ROLLBACK_MSG,
158                IDS_SHORT_PRODUCT_NAME);
159
160  // Variants for screen message.
161  builder->AddF("resetPowerwashWarningDetails",
162                IDS_RESET_SCREEN_WARNING_DETAILS,
163                IDS_SHORT_PRODUCT_NAME);
164  builder->AddF("resetPowerwashRollbackWarningDetails",
165                IDS_RESET_SCREEN_WARNING_POWERWASH_AND_ROLLBACK_MSG,
166                IDS_SHORT_PRODUCT_NAME);
167  builder->AddF("resetPowerwashConfirmationDetails",
168                IDS_RESET_SCREEN_CONFIRMATION_WARNING_DETAILS,
169                IDS_SHORT_PRODUCT_NAME);
170  builder->AddF("resetPowerwashRollbackConfirmationDetails",
171                IDS_RESET_SCREEN_CONFIRMATION_WARNING_ROLLBACK_DETAILS,
172                IDS_SHORT_PRODUCT_NAME);
173}
174
175// Invoked from call to CanRollbackCheck upon completion of the DBus call.
176void ResetScreenHandler::OnRollbackCheck(bool can_rollback) {
177  VLOG(1) << "Callback from CanRollbackCheck, result " << can_rollback;
178  rollback_available_ = can_rollback;
179  ShowWithParams();
180}
181
182// static
183void ResetScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
184  registry->RegisterBooleanPref(prefs::kFactoryResetRequested, false);
185}
186
187void ResetScreenHandler::Initialize() {
188  if (!page_is_ready() || !delegate_)
189    return;
190
191  if (show_on_init_) {
192    Show();
193    show_on_init_ = false;
194  }
195}
196
197void ResetScreenHandler::RegisterMessages() {
198  AddCallback("cancelOnReset", &ResetScreenHandler::HandleOnCancel);
199  AddCallback("restartOnReset", &ResetScreenHandler::HandleOnRestart);
200  AddCallback("powerwashOnReset", &ResetScreenHandler::HandleOnPowerwash);
201  AddCallback("resetOnLearnMore", &ResetScreenHandler::HandleOnLearnMore);
202  AddCallback(
203      "showRollbackOnResetScreen", &ResetScreenHandler::HandleOnShowRollback);
204  AddCallback(
205      "hideRollbackOnResetScreen", &ResetScreenHandler::HandleOnHideRollback);
206  AddCallback(
207      "showConfirmationOnReset", &ResetScreenHandler::HandleOnShowConfirm);
208}
209
210void ResetScreenHandler::HandleOnCancel() {
211  if (preparing_for_rollback_)
212    return;
213  if (delegate_)
214    delegate_->OnExit();
215  DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
216}
217
218void ResetScreenHandler::HandleOnRestart() {
219  PrefService* prefs = g_browser_process->local_state();
220  prefs->SetBoolean(prefs::kFactoryResetRequested, true);
221  prefs->CommitPendingWrite();
222
223  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
224}
225
226void ResetScreenHandler::HandleOnPowerwash(bool rollback_checked) {
227  if (rollback_available_ && rollback_checked) {
228      preparing_for_rollback_ = true;
229      CallJS("updateViewOnRollbackCall");
230      DBusThreadManager::Get()->GetUpdateEngineClient()->AddObserver(this);
231      VLOG(1) << "Starting Rollback";
232      chromeos::DBusThreadManager::Get()->GetUpdateEngineClient()->Rollback();
233  } else {
234    if (rollback_checked && !rollback_available_) {
235      NOTREACHED() <<
236          "Rollback was checked but not available. Starting powerwash.";
237    }
238    VLOG(1) << "Starting Powerwash";
239    chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
240        StartDeviceWipe();
241  }
242}
243
244void ResetScreenHandler::HandleOnLearnMore() {
245  if (!help_app_.get())
246    help_app_ = new HelpAppLauncher(GetNativeWindow());
247  help_app_->ShowHelpTopic(HelpAppLauncher::HELP_POWERWASH);
248}
249
250void ResetScreenHandler::HandleOnShowRollback() {
251  VLOG(1) << "Requested rollback availability" << rollback_available_;
252  if (rollback_available_) {
253    UMA_HISTOGRAM_ENUMERATION(
254        "Reset.ChromeOS.PowerwashDialogShown",
255        reset::DIALOG_SHORTCUT_OFFERING_ROLLBACK_AVAILABLE,
256        reset::DIALOG_VIEW_TYPE_SIZE);
257    CallJS("showRollbackOption");
258    rollback_checked_ = true;
259  }
260}
261
262void ResetScreenHandler::HandleOnHideRollback() {
263  if (rollback_available_ && rollback_checked_) {
264    CallJS("hideRollbackOption");
265    rollback_checked_ = false;
266  }
267}
268
269void ResetScreenHandler::HandleOnShowConfirm() {
270  int dialog_type = rollback_checked_ ?
271      reset::DIALOG_SHORTCUT_CONFIRMING_POWERWASH_AND_ROLLBACK :
272      reset::DIALOG_SHORTCUT_CONFIRMING_POWERWASH_ONLY;
273  UMA_HISTOGRAM_ENUMERATION(
274      "Reset.ChromeOS.PowerwashDialogShown",
275      dialog_type,
276      reset::DIALOG_VIEW_TYPE_SIZE);
277}
278
279void ResetScreenHandler::UpdateStatusChanged(
280    const UpdateEngineClient::Status& status) {
281  VLOG(1) << "Update status change to " << status.status;
282  if (status.status == UpdateEngineClient::UPDATE_STATUS_ERROR ||
283      status.status ==
284          UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT) {
285    preparing_for_rollback_ = false;
286    // Show error screen.
287    base::DictionaryValue params;
288    params.SetInteger("uiState", kErrorUIStateRollback);
289    ShowScreen(OobeUI::kScreenErrorMessage, &params);
290  } else if (status.status ==
291      UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) {
292    DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
293  }
294}
295
296}  // namespace chromeos
297