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/help/version_updater_chromeos.h"
6
7#include <cmath>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "chrome/browser/browser_process.h"
12#include "chrome/browser/chromeos/login/startup_utils.h"
13#include "chrome/browser/chromeos/login/wizard_controller.h"
14#include "chrome/browser/chromeos/settings/cros_settings.h"
15#include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
16#include "chrome/grit/generated_resources.h"
17#include "chromeos/dbus/dbus_thread_manager.h"
18#include "chromeos/dbus/power_manager_client.h"
19#include "chromeos/network/network_handler.h"
20#include "chromeos/network/network_state.h"
21#include "chromeos/network/network_state_handler.h"
22#include "chromeos/settings/cros_settings_names.h"
23#include "components/user_manager/user_manager.h"
24#include "third_party/cros_system_api/dbus/service_constants.h"
25#include "ui/base/l10n/l10n_util.h"
26
27using chromeos::CrosSettings;
28using chromeos::DBusThreadManager;
29using chromeos::UpdateEngineClient;
30using user_manager::UserManager;
31using chromeos::WizardController;
32
33namespace {
34
35// Network status in the context of device update.
36enum NetworkStatus {
37  // It's allowed in device policy to use current network for update.
38  NETWORK_STATUS_ALLOWED = 0,
39  // It's disallowed in device policy to use current network for update.
40  NETWORK_STATUS_DISALLOWED,
41  // Device is in offline state.
42  NETWORK_STATUS_OFFLINE
43};
44
45const bool kDefaultAutoUpdateDisabled = false;
46
47NetworkStatus GetNetworkStatus(const chromeos::NetworkState* network) {
48  if (!network || !network->IsConnectedState())  // Offline state.
49    return NETWORK_STATUS_OFFLINE;
50
51  // The connection type checking strategy must be the same as the one
52  // used in update engine.
53  if (network->type() == shill::kTypeBluetooth)
54    return NETWORK_STATUS_DISALLOWED;
55  if (network->type() == shill::kTypeCellular &&
56      !help_utils_chromeos::IsUpdateOverCellularAllowed()) {
57    return NETWORK_STATUS_DISALLOWED;
58  }
59  return NETWORK_STATUS_ALLOWED;
60}
61
62// Returns true if auto-update is disabled by the system administrator.
63bool IsAutoUpdateDisabled() {
64  bool update_disabled = kDefaultAutoUpdateDisabled;
65  chromeos::CrosSettings* settings = chromeos::CrosSettings::Get();
66  if (!settings)
67    return update_disabled;
68  const base::Value* update_disabled_value =
69      settings->GetPref(chromeos::kUpdateDisabled);
70  if (update_disabled_value)
71    CHECK(update_disabled_value->GetAsBoolean(&update_disabled));
72  return update_disabled;
73}
74
75// Returns whether an update is allowed. If not, it calls the callback with
76// the appropriate status.
77bool EnsureCanUpdate(const VersionUpdater::StatusCallback& callback) {
78  if (IsAutoUpdateDisabled()) {
79    callback.Run(VersionUpdater::FAILED, 0,
80                 l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY));
81    return false;
82  }
83
84  chromeos::NetworkStateHandler* network_state_handler =
85      chromeos::NetworkHandler::Get()->network_state_handler();
86  const chromeos::NetworkState* network =
87      network_state_handler->DefaultNetwork();
88
89  // Don't allow an update if we're currently offline or connected
90  // to a network for which updates are disallowed.
91  NetworkStatus status = GetNetworkStatus(network);
92  if (status == NETWORK_STATUS_OFFLINE) {
93    callback.Run(VersionUpdater::FAILED_OFFLINE, 0,
94                  l10n_util::GetStringUTF16(IDS_UPGRADE_OFFLINE));
95    return false;
96  } else if (status == NETWORK_STATUS_DISALLOWED) {
97    base::string16 message =
98        l10n_util::GetStringFUTF16(
99            IDS_UPGRADE_DISALLOWED,
100            help_utils_chromeos::GetConnectionTypeAsUTF16(network->type()));
101    callback.Run(VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED, 0, message);
102    return false;
103  }
104
105  return true;
106}
107
108}  // namespace
109
110VersionUpdater* VersionUpdater::Create() {
111  return new VersionUpdaterCros;
112}
113
114void VersionUpdaterCros::GetUpdateStatus(const StatusCallback& callback) {
115  callback_ = callback;
116
117  if (!EnsureCanUpdate(callback))
118    return;
119
120  UpdateEngineClient* update_engine_client =
121      DBusThreadManager::Get()->GetUpdateEngineClient();
122  if (!update_engine_client->HasObserver(this))
123    update_engine_client->AddObserver(this);
124
125  this->UpdateStatusChanged(
126      DBusThreadManager::Get()->GetUpdateEngineClient()->GetLastStatus());
127}
128
129void VersionUpdaterCros::CheckForUpdate(const StatusCallback& callback) {
130  callback_ = callback;
131
132  if (!EnsureCanUpdate(callback))
133    return;
134
135  UpdateEngineClient* update_engine_client =
136      DBusThreadManager::Get()->GetUpdateEngineClient();
137  if (!update_engine_client->HasObserver(this))
138    update_engine_client->AddObserver(this);
139
140  if (update_engine_client->GetLastStatus().status !=
141      UpdateEngineClient::UPDATE_STATUS_IDLE) {
142    check_for_update_when_idle_ = true;
143    return;
144  }
145  check_for_update_when_idle_ = false;
146
147  // Make sure that libcros is loaded and OOBE is complete.
148  if (!WizardController::default_controller() ||
149      chromeos::StartupUtils::IsDeviceRegistered()) {
150    update_engine_client->RequestUpdateCheck(base::Bind(
151        &VersionUpdaterCros::OnUpdateCheck, weak_ptr_factory_.GetWeakPtr()));
152  }
153}
154
155void VersionUpdaterCros::RelaunchBrowser() const {
156  DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
157}
158
159void VersionUpdaterCros::SetChannel(const std::string& channel,
160                                    bool is_powerwash_allowed) {
161  if (user_manager::UserManager::Get()->IsCurrentUserOwner()) {
162    // For local owner set the field in the policy blob.
163    CrosSettings::Get()->SetString(chromeos::kReleaseChannel, channel);
164  }
165  DBusThreadManager::Get()->GetUpdateEngineClient()->
166      SetChannel(channel, is_powerwash_allowed);
167}
168
169void VersionUpdaterCros::GetChannel(bool get_current_channel,
170                                    const ChannelCallback& cb) {
171  UpdateEngineClient* update_engine_client =
172      DBusThreadManager::Get()->GetUpdateEngineClient();
173
174  // Request the channel information.
175  update_engine_client->GetChannel(get_current_channel, cb);
176}
177
178VersionUpdaterCros::VersionUpdaterCros()
179    : last_operation_(UpdateEngineClient::UPDATE_STATUS_IDLE),
180      check_for_update_when_idle_(false),
181      weak_ptr_factory_(this) {
182}
183
184VersionUpdaterCros::~VersionUpdaterCros() {
185  UpdateEngineClient* update_engine_client =
186      DBusThreadManager::Get()->GetUpdateEngineClient();
187  update_engine_client->RemoveObserver(this);
188}
189
190void VersionUpdaterCros::UpdateStatusChanged(
191    const UpdateEngineClient::Status& status) {
192  Status my_status = UPDATED;
193  int progress = 0;
194  base::string16 message;
195
196  // If the updater is currently idle, just show the last operation (unless it
197  // was previously checking for an update -- in that case, the system is
198  // up-to-date now).  See http://crbug.com/120063 for details.
199  UpdateEngineClient::UpdateStatusOperation operation_to_show = status.status;
200  if (status.status == UpdateEngineClient::UPDATE_STATUS_IDLE &&
201      last_operation_ !=
202      UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE) {
203    operation_to_show = last_operation_;
204  }
205
206  switch (operation_to_show) {
207    case UpdateEngineClient::UPDATE_STATUS_ERROR:
208    case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT:
209    case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
210      // This path previously used the FAILED status and IDS_UPGRADE_ERROR, but
211      // the update engine reports errors for some conditions that shouldn't
212      // actually be displayed as errors to users: http://crbug.com/146919.
213      // Just use the UPDATED status instead.
214      break;
215    case UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE:
216      my_status = CHECKING;
217      break;
218    case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING:
219      progress = static_cast<int>(round(status.download_progress * 100));
220      // Fall through.
221    case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE:
222      my_status = UPDATING;
223      break;
224    case UpdateEngineClient::UPDATE_STATUS_VERIFYING:
225    case UpdateEngineClient::UPDATE_STATUS_FINALIZING:
226      // Once the download is finished, keep the progress at 100; it shouldn't
227      // go down while the status is the same.
228      progress = 100;
229      my_status = UPDATING;
230      break;
231    case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT:
232      my_status = NEARLY_UPDATED;
233      break;
234    default:
235      break;
236  }
237
238  callback_.Run(my_status, progress, message);
239  last_operation_ = status.status;
240
241  if (check_for_update_when_idle_ &&
242      status.status == UpdateEngineClient::UPDATE_STATUS_IDLE) {
243    CheckForUpdate(callback_);
244  }
245}
246
247void VersionUpdaterCros::OnUpdateCheck(
248    UpdateEngineClient::UpdateCheckResult result) {
249  // If version updating is not implemented, this binary is the most up-to-date
250  // possible with respect to automatic updating.
251  if (result == UpdateEngineClient::UPDATE_RESULT_NOTIMPLEMENTED)
252    callback_.Run(UPDATED, 0, base::string16());
253}
254