11305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Copyright (c) 2011 The Chromium Authors. All rights reserved.
21305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Use of this source code is governed by a BSD-style license that can be
31305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// found in the LICENSE file.
41305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
51305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/browser/ui/webui/chromeos/mobile_setup_ui.h"
61305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
71305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <algorithm>
81305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <map>
91305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <string>
101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/callback.h"
121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/file_util.h"
131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/json/json_reader.h"
141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/json/json_writer.h"
151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/logging.h"
161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/memory/weak_ptr.h"
171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/metrics/histogram.h"
181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/string_piece.h"
191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/string_util.h"
201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/threading/thread_restrictions.h"
211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/timer.h"
221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/utf_string_conversions.h"
231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "base/values.h"
241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/browser/browser_process.h"
251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/browser/chromeos/cros/cros_library.h"
261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/browser/chromeos/cros/network_library.h"
271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/browser/prefs/pref_service.h"
281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/browser/profiles/profile.h"
291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/browser/ui/browser_list.h"
301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/common/jstemplate_builder.h"
321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/common/pref_names.h"
331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "chrome/common/url_constants.h"
341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "content/browser/browser_thread.h"
351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "content/browser/tab_contents/tab_contents.h"
361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "googleurl/src/gurl.h"
371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "grit/browser_resources.h"
381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "grit/chromium_strings.h"
391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "grit/generated_resources.h"
401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "grit/locale_settings.h"
411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "ui/base/l10n/l10n_util.h"
421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "ui/base/resource/resource_bundle.h"
431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodnamespace {
451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Host page JS API function names.
471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kJsApiStartActivation[] = "startActivation";
481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kJsApiSetTransactionStatus[] = "setTransactionStatus";
491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kJsDeviceStatusChangedHandler[] =
511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    "mobile.MobileSetup.deviceStateChanged";
521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Error codes matching codes defined in the cellular config file.
541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorDefault[] = "default";
551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorBadConnectionPartial[] = "bad_connection_partial";
561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorBadConnectionActivated[] = "bad_connection_activated";
571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorRoamingOnConnection[] = "roaming_connection";
581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorNoEVDO[] = "no_evdo";
591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorRoamingActivation[] = "roaming_activation";
601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorRoamingPartiallyActivated[] = "roaming_partially_activated";
611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorNoService[] = "no_service";
621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorDisabled[] = "disabled";
631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorNoDevice[] = "no_device";
641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kFailedPaymentError[] = "failed_payment";
651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kFailedConnectivity[] = "connectivity";
661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorAlreadyRunning[] = "already_running";
671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Cellular configuration file path.
691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kCellularConfigPath[] =
701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    "/usr/share/chromeos-assets/mobile/mobile_config.json";
711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Cellular config file field names.
731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kVersionField[] = "version";
741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst char kErrorsField[] = "errors";
751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Number of times we will retry to restart the activation process in case
771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// there is no connectivity in the restricted pool.
781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst int kMaxActivationAttempt = 3;
791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Number of times we will retry to reconnect if connection fails.
801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst int kMaxConnectionRetry = 10;
811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Number of times we will retry to reconnect if connection fails.
821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst int kMaxConnectionRetryOTASP = 30;
831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Number of times we will retry to reconnect after payment is processed.
841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst int kMaxReconnectAttemptOTASP = 30;
851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Reconnect retry delay (after payment is processed).
861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst int kPostPaymentReconnectDelayMS = 30000;   // 30s.
871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Connection timeout in seconds.
881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst int kConnectionTimeoutSeconds = 45;
891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Reconnect delay.
901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst int kReconnectDelayMS = 3000;
911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Reconnect timer delay.
921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst int kReconnectTimerDelayMS = 5000;
931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Reconnect delay after previous failure.
941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst int kFailedReconnectDelayMS = 10000;
951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// Retry delay after failed OTASP attempt.
961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodconst int kOTASPRetryDelay = 20000;
971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodchromeos::CellularNetwork* GetCellularNetwork() {
991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
1001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood      GetNetworkLibrary();
1011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  if (lib->cellular_networks().begin() != lib->cellular_networks().end()) {
1021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    return *(lib->cellular_networks().begin());
1031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  }
1041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  return NULL;
1051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
1061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodchromeos::CellularNetwork* GetCellularNetwork(
1081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    const std::string& service_path) {
1091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  return chromeos::CrosLibrary::Get()->
1101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood      GetNetworkLibrary()->FindCellularNetworkByPath(service_path);
1111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
1121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}  // namespace
1141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodclass CellularConfigDocument {
1161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood public:
1171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  CellularConfigDocument() {}
1181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  // Return error message for a given code.
1201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  std::string GetErrorMessage(const std::string& code);
1211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  const std::string& version() { return version_; }
1221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  bool LoadFromFile(const FilePath& config_path);
1241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood private:
1261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  std::string version_;
1271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  std::map<std::string, std::string> error_map_;
1281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  DISALLOW_COPY_AND_ASSIGN(CellularConfigDocument);
1301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood};
1311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic std::map<std::string, std::string> error_messages_;
1331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodclass MobileSetupUIHTMLSource : public ChromeURLDataManager::DataSource {
1351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood public:
1361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  explicit MobileSetupUIHTMLSource(const std::string& service_path);
1371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  // Called when the network layer has requested a resource underneath
1391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  // the path we registered.
1401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  virtual void StartDataRequest(const std::string& path,
1411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood                                bool is_incognito,
1421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood                                int request_id);
1431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  virtual std::string GetMimeType(const std::string&) const {
1441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    return "text/html";
1451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  }
1461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood private:
1481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  virtual ~MobileSetupUIHTMLSource() {}
1491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  std::string service_path_;
1511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  DISALLOW_COPY_AND_ASSIGN(MobileSetupUIHTMLSource);
1521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood};
1531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood// The handler for Javascript messages related to the "register" view.
1551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodclass MobileSetupHandler
1561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  : public WebUIMessageHandler,
1571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    public chromeos::NetworkLibrary::NetworkManagerObserver,
1581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    public chromeos::NetworkLibrary::NetworkObserver,
1591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    public base::SupportsWeakPtr<MobileSetupHandler> {
1601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood public:
1611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  explicit MobileSetupHandler(const std::string& service_path);
1621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  virtual ~MobileSetupHandler();
1631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  // Init work after Attach.
1651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  void Init(TabContents* contents);
1661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  // WebUIMessageHandler implementation.
1681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood  virtual WebUIMessageHandler* Attach(WebUI* web_ui);
169  virtual void RegisterMessages();
170
171  // NetworkLibrary::NetworkManagerObserver implementation.
172  virtual void OnNetworkManagerChanged(chromeos::NetworkLibrary* obj);
173  // NetworkLibrary::NetworkObserver implementation.
174  virtual void OnNetworkChanged(chromeos::NetworkLibrary* obj,
175                                const chromeos::Network* network);
176
177 private:
178  typedef enum PlanActivationState {
179    PLAN_ACTIVATION_PAGE_LOADING            = -1,
180    PLAN_ACTIVATION_START                   = 0,
181    PLAN_ACTIVATION_TRYING_OTASP            = 1,
182    PLAN_ACTIVATION_RECONNECTING_OTASP_TRY  = 2,
183    PLAN_ACTIVATION_INITIATING_ACTIVATION   = 3,
184    PLAN_ACTIVATION_RECONNECTING            = 4,
185    PLAN_ACTIVATION_SHOWING_PAYMENT         = 5,
186    PLAN_ACTIVATION_DELAY_OTASP             = 6,
187    PLAN_ACTIVATION_START_OTASP             = 7,
188    PLAN_ACTIVATION_OTASP                   = 8,
189    PLAN_ACTIVATION_RECONNECTING_OTASP      = 9,
190    PLAN_ACTIVATION_DONE                    = 10,
191    PLAN_ACTIVATION_ERROR                   = 0xFF,
192  } PlanActivationState;
193
194  class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> {
195   public:
196    TaskProxy(const base::WeakPtr<MobileSetupHandler>& handler, int delay)
197        : handler_(handler), delay_(delay) {
198    }
199    TaskProxy(const base::WeakPtr<MobileSetupHandler>& handler,
200              const std::string& status)
201        : handler_(handler), status_(status) {
202    }
203    void HandleStartActivation() {
204      if (handler_)
205        handler_->StartActivation();
206    }
207    void HandleSetTransactionStatus() {
208      if (handler_)
209        handler_->SetTransactionStatus(status_);
210    }
211    void ContinueConnecting() {
212      if (handler_)
213        handler_->ContinueConnecting(delay_);
214    }
215    void RetryOTASP() {
216      if (handler_)
217        handler_->RetryOTASP();
218    }
219   private:
220    base::WeakPtr<MobileSetupHandler> handler_;
221    std::string status_;
222    int delay_;
223    DISALLOW_COPY_AND_ASSIGN(TaskProxy);
224  };
225
226  // Handlers for JS WebUI messages.
227  void HandleSetTransactionStatus(const ListValue* args);
228  void HandleStartActivation(const ListValue* args);
229  void SetTransactionStatus(const std::string& status);
230  // Schedules activation process via task proxy.
231  void InitiateActivation();
232  // Starts activation.
233  void StartActivation();
234  // Retried OTASP.
235  void RetryOTASP();
236  // Continues activation process. This method is called after we disconnect
237  // due to detected connectivity issue to kick off reconnection.
238  void ContinueConnecting(int delay);
239
240  // Sends message to host registration page with system/user info data.
241  void SendDeviceInfo();
242
243  // Callback for when |reconnect_timer_| fires.
244  void ReconnectTimerFired();
245  // Starts OTASP process.
246  void StartOTASP();
247  // Checks if we need to reconnect due to failed connection attempt.
248  bool NeedsReconnecting(chromeos::CellularNetwork* network,
249                         PlanActivationState* new_state,
250                         std::string* error_description);
251  // Disconnect from network.
252  void DisconnectFromNetwork(chromeos::CellularNetwork* network);
253  // Connects to cellular network, resets connection timer.
254  bool ConnectToNetwork(chromeos::CellularNetwork* network, int delay);
255  // Forces disconnect / reconnect when we detect portal connectivity issues.
256  void ForceReconnect(chromeos::CellularNetwork* network, int delay);
257  // Reports connection timeout.
258  bool ConnectionTimeout();
259  // Verify the state of cellular network and modify internal state.
260  void EvaluateCellularNetwork(chromeos::CellularNetwork* network);
261  // Check the current cellular network for error conditions.
262  bool GotActivationError(chromeos::CellularNetwork* network,
263                          std::string* error);
264  // Sends status updates to WebUI page.
265  void UpdatePage(chromeos::CellularNetwork* network,
266                  const std::string& error_description);
267  // Changes internal state.
268  void ChangeState(chromeos::CellularNetwork* network,
269                   PlanActivationState new_state,
270                   const std::string& error_description);
271  // Prepares network devices for cellular activation process.
272  void SetupActivationProcess(chromeos::CellularNetwork* network);
273  // Disables ethernet and wifi newtorks since they interefere with
274  // detection of restricted pool on cellular side.
275  void DisableOtherNetworks();
276  // Resets network devices after cellular activation process.
277  // |network| should be NULL if the activation process failed.
278  void CompleteActivation(chromeos::CellularNetwork* network);
279  // Control routines for handling other types of connections during
280  // cellular activation.
281  void ReEnableOtherConnections();
282
283  // Converts the currently active CellularNetwork device into a JS object.
284  static void GetDeviceInfo(chromeos::CellularNetwork* network,
285                            DictionaryValue* value);
286  static bool ShouldReportDeviceState(std::string* state, std::string* error);
287
288  // Performs activation state cellular device evaluation.
289  // Returns false if device activation failed. In this case |error|
290  // will contain error message to be reported to Web UI.
291  static bool EvaluateCellularDeviceState(bool* report_status,
292                                          std::string* state,
293                                          std::string* error);
294
295  // Return error message for a given code.
296  static std::string GetErrorMessage(const std::string& code);
297  static void LoadCellularConfig();
298
299  // Returns next reconnection state based on the current activation phase.
300  static PlanActivationState GetNextReconnectState(PlanActivationState state);
301  static const char* GetStateDescription(PlanActivationState state);
302
303  static scoped_ptr<CellularConfigDocument> cellular_config_;
304
305  TabContents* tab_contents_;
306  // Internal handler state.
307  PlanActivationState state_;
308  std::string service_path_;
309  // Flags that control if wifi and ethernet connection needs to be restored
310  // after the activation of cellular network.
311  bool reenable_wifi_;
312  bool reenable_ethernet_;
313  bool reenable_cert_check_;
314  bool evaluating_;
315  // True if we think that another tab is already running activation.
316  bool already_running_;
317  // Connection retry counter.
318  int connection_retry_count_;
319  // Post payment reconnect wait counters.
320  int reconnect_wait_count_;
321  // Activation retry attempt count;
322  int activation_attempt_;
323  // Connection start time.
324  base::Time connection_start_time_;
325  // Timer that monitors reconnection timeouts.
326  base::RepeatingTimer<MobileSetupHandler> reconnect_timer_;
327
328  DISALLOW_COPY_AND_ASSIGN(MobileSetupHandler);
329};
330
331scoped_ptr<CellularConfigDocument> MobileSetupHandler::cellular_config_;
332
333////////////////////////////////////////////////////////////////////////////////
334//
335// CellularConfigDocument
336//
337////////////////////////////////////////////////////////////////////////////////
338
339std::string CellularConfigDocument::GetErrorMessage(const std::string& code) {
340  std::map<std::string, std::string>::iterator iter = error_map_.find(code);
341  if (iter == error_map_.end())
342    return code;
343  return iter->second;
344}
345
346bool CellularConfigDocument::LoadFromFile(const FilePath& config_path) {
347  error_map_.clear();
348
349  std::string config;
350  {
351    // Reading config file causes us to do blocking IO on UI thread.
352    // Temporarily allow it until we fix http://crosbug.com/11535
353    base::ThreadRestrictions::ScopedAllowIO allow_io;
354    if (!file_util::ReadFileToString(config_path, &config))
355      return false;
356  }
357
358  scoped_ptr<Value> root(base::JSONReader::Read(config, true));
359  DCHECK(root.get() != NULL);
360  if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) {
361    LOG(WARNING) << "Bad cellular config file";
362    return false;
363  }
364
365  DictionaryValue* root_dict = static_cast<DictionaryValue*>(root.get());
366  if (!root_dict->GetString(kVersionField, &version_)) {
367    LOG(WARNING) << "Cellular config file missing version";
368    return false;
369  }
370  DictionaryValue* errors = NULL;
371  if (!root_dict->GetDictionary(kErrorsField, &errors))
372    return false;
373  for (DictionaryValue::key_iterator keys = errors->begin_keys();
374       keys != errors->end_keys();
375       ++keys) {
376    std::string value;
377    if (!errors->GetString(*keys, &value)) {
378      LOG(WARNING) << "Bad cellular config error value";
379      error_map_.clear();
380      return false;
381    }
382
383    error_map_.insert(std::pair<std::string, std::string>(*keys, value));
384  }
385  return true;
386}
387
388////////////////////////////////////////////////////////////////////////////////
389//
390// MobileSetupUIHTMLSource
391//
392////////////////////////////////////////////////////////////////////////////////
393
394MobileSetupUIHTMLSource::MobileSetupUIHTMLSource(
395    const std::string& service_path)
396    : DataSource(chrome::kChromeUIMobileSetupHost, MessageLoop::current()),
397      service_path_(service_path) {
398}
399
400void MobileSetupUIHTMLSource::StartDataRequest(const std::string& path,
401                                               bool is_incognito,
402                                               int request_id) {
403  chromeos::CellularNetwork* network = GetCellularNetwork(service_path_);
404  DCHECK(network);
405  DictionaryValue strings;
406  strings.SetString("title", l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE));
407  strings.SetString("connecting_header",
408                    l10n_util::GetStringFUTF16(IDS_MOBILE_CONNECTING_HEADER,
409                        network ? UTF8ToUTF16(network->name()) : string16()));
410  strings.SetString("error_header",
411                    l10n_util::GetStringUTF16(IDS_MOBILE_ERROR_HEADER));
412  strings.SetString("activating_header",
413                    l10n_util::GetStringUTF16(IDS_MOBILE_ACTIVATING_HEADER));
414  strings.SetString("completed_header",
415                    l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_HEADER));
416  strings.SetString("please_wait",
417                    l10n_util::GetStringUTF16(IDS_MOBILE_PLEASE_WAIT));
418  strings.SetString("completed_text",
419                    l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_TEXT));
420  strings.SetString("close_button",
421                    l10n_util::GetStringUTF16(IDS_CLOSE));
422  SetFontAndTextDirection(&strings);
423
424  static const base::StringPiece html(
425      ResourceBundle::GetSharedInstance().GetRawDataResource(
426          IDR_MOBILE_SETUP_PAGE_HTML));
427
428  const std::string full_html = jstemplate_builder::GetI18nTemplateHtml(
429      html, &strings);
430
431  scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
432  html_bytes->data.resize(full_html.size());
433  std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());
434
435  SendResponse(request_id, html_bytes);
436}
437
438////////////////////////////////////////////////////////////////////////////////
439//
440// MobileSetupHandler
441//
442////////////////////////////////////////////////////////////////////////////////
443MobileSetupHandler::MobileSetupHandler(const std::string& service_path)
444    : tab_contents_(NULL),
445      state_(PLAN_ACTIVATION_PAGE_LOADING),
446      service_path_(service_path),
447      reenable_wifi_(false),
448      reenable_ethernet_(false),
449      reenable_cert_check_(false),
450      evaluating_(false),
451      already_running_(false),
452      connection_retry_count_(0),
453      reconnect_wait_count_(0),
454      activation_attempt_(0) {
455}
456
457MobileSetupHandler::~MobileSetupHandler() {
458  reconnect_timer_.Stop();
459  chromeos::NetworkLibrary* lib =
460      chromeos::CrosLibrary::Get()->GetNetworkLibrary();
461  lib->RemoveNetworkManagerObserver(this);
462  lib->RemoveObserverForAllNetworks(this);
463  if (lib->IsLocked())
464    lib->Unlock();
465  ReEnableOtherConnections();
466}
467
468WebUIMessageHandler* MobileSetupHandler::Attach(WebUI* web_ui) {
469  return WebUIMessageHandler::Attach(web_ui);
470}
471
472void MobileSetupHandler::Init(TabContents* contents) {
473  tab_contents_ = contents;
474  LoadCellularConfig();
475  if (!chromeos::CrosLibrary::Get()->GetNetworkLibrary()->IsLocked())
476    SetupActivationProcess(GetCellularNetwork(service_path_));
477  else
478    already_running_ = true;
479}
480
481void MobileSetupHandler::RegisterMessages() {
482  web_ui_->RegisterMessageCallback(kJsApiStartActivation,
483      NewCallback(this, &MobileSetupHandler::HandleStartActivation));
484  web_ui_->RegisterMessageCallback(kJsApiSetTransactionStatus,
485      NewCallback(this, &MobileSetupHandler::HandleSetTransactionStatus));
486}
487
488void MobileSetupHandler::OnNetworkManagerChanged(
489    chromeos::NetworkLibrary* cros) {
490  if (state_ == PLAN_ACTIVATION_PAGE_LOADING)
491    return;
492  // Note that even though we get here when the service has
493  // reappeared after disappearing earlier in the activation
494  // process, there's no need to re-establish the NetworkObserver,
495  // because the service path remains the same.
496  EvaluateCellularNetwork(GetCellularNetwork(service_path_));
497}
498
499void MobileSetupHandler::OnNetworkChanged(chromeos::NetworkLibrary* cros,
500                                          const chromeos::Network* network) {
501  if (state_ == PLAN_ACTIVATION_PAGE_LOADING)
502    return;
503  DCHECK(network && network->type() == chromeos::TYPE_CELLULAR);
504  EvaluateCellularNetwork(GetCellularNetwork(network->service_path()));
505}
506
507void MobileSetupHandler::HandleStartActivation(const ListValue* args) {
508  InitiateActivation();
509  UMA_HISTOGRAM_COUNTS("Cellular.MobileSetupStart", 1);
510}
511
512void MobileSetupHandler::HandleSetTransactionStatus(const ListValue* args) {
513  const size_t kSetTransactionStatusParamCount = 1;
514  if (args->GetSize() != kSetTransactionStatusParamCount)
515    return;
516  // Get change callback function name.
517  std::string status;
518  if (!args->GetString(0, &status))
519    return;
520  scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), status);
521  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
522      NewRunnableMethod(task.get(), &TaskProxy::HandleSetTransactionStatus));
523}
524
525void MobileSetupHandler::InitiateActivation() {
526  scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), 0);
527  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
528      NewRunnableMethod(task.get(), &TaskProxy::HandleStartActivation));
529}
530
531void MobileSetupHandler::StartActivation() {
532  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
533  chromeos::NetworkLibrary* lib =
534      chromeos::CrosLibrary::Get()->GetNetworkLibrary();
535  chromeos::CellularNetwork* network = GetCellularNetwork(service_path_);
536  // Check if we can start activation process.
537  if (!network || already_running_) {
538    std::string error;
539    if (already_running_)
540      error = kErrorAlreadyRunning;
541    else if (!lib->cellular_available())
542      error = kErrorNoDevice;
543    else if (!lib->cellular_enabled())
544      error = kErrorDisabled;
545    else
546      error = kErrorNoService;
547    ChangeState(NULL, PLAN_ACTIVATION_ERROR, GetErrorMessage(error));
548    return;
549  }
550
551  // Start monitoring network property changes.
552  lib->AddNetworkManagerObserver(this);
553  lib->RemoveObserverForAllNetworks(this);
554  lib->AddNetworkObserver(network->service_path(), this);
555  // Try to start with OTASP immediately if we have received payment recently.
556  state_ = lib->HasRecentCellularPlanPayment() ?
557               PLAN_ACTIVATION_START_OTASP :
558               PLAN_ACTIVATION_START;
559  EvaluateCellularNetwork(network);
560}
561
562void MobileSetupHandler::RetryOTASP() {
563  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
564  DCHECK(state_ == PLAN_ACTIVATION_DELAY_OTASP);
565  StartOTASP();
566}
567
568void MobileSetupHandler::ContinueConnecting(int delay) {
569  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
570  chromeos::CellularNetwork* network = GetCellularNetwork(service_path_);
571  if (network && network->connecting_or_connected()) {
572    EvaluateCellularNetwork(network);
573  } else {
574    ConnectToNetwork(network, delay);
575  }
576}
577
578void MobileSetupHandler::SetTransactionStatus(const std::string& status) {
579  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
580  // The payment is received, try to reconnect and check the status all over
581  // again.
582  if (LowerCaseEqualsASCII(status, "ok") &&
583      state_ == PLAN_ACTIVATION_SHOWING_PAYMENT) {
584    chromeos::NetworkLibrary* lib =
585        chromeos::CrosLibrary::Get()->GetNetworkLibrary();
586    lib->SignalCellularPlanPayment();
587    UMA_HISTOGRAM_COUNTS("Cellular.PaymentReceived", 1);
588    StartOTASP();
589  } else {
590    UMA_HISTOGRAM_COUNTS("Cellular.PaymentFailed", 1);
591  }
592}
593
594void MobileSetupHandler::StartOTASP() {
595  state_ = PLAN_ACTIVATION_START_OTASP;
596  chromeos::CellularNetwork* network = GetCellularNetwork();
597  if (network &&
598      network->connected() &&
599      network->activation_state() == chromeos::ACTIVATION_STATE_ACTIVATED) {
600    chromeos::CrosLibrary::Get()->GetNetworkLibrary()->
601        DisconnectFromNetwork(network);
602  } else {
603    EvaluateCellularNetwork(network);
604  }
605}
606
607void MobileSetupHandler::ReconnectTimerFired() {
608  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
609  // Permit network connection changes only in reconnecting states.
610  if (state_ != PLAN_ACTIVATION_RECONNECTING_OTASP_TRY &&
611      state_ != PLAN_ACTIVATION_RECONNECTING &&
612      state_ != PLAN_ACTIVATION_RECONNECTING_OTASP)
613    return;
614  chromeos::CellularNetwork* network = GetCellularNetwork(service_path_);
615  if (!network) {
616    // No service, try again since this is probably just transient condition.
617    LOG(WARNING) << "Service not present at reconnect attempt.";
618  }
619  EvaluateCellularNetwork(network);
620}
621
622void MobileSetupHandler::DisconnectFromNetwork(
623    chromeos::CellularNetwork* network) {
624  DCHECK(network);
625  LOG(INFO) << "Disconnecting from: " << network->service_path();
626  chromeos::CrosLibrary::Get()->GetNetworkLibrary()->
627      DisconnectFromNetwork(network);
628  // Disconnect will force networks to be reevaluated, so
629  // we don't want to continue processing on this path anymore.
630  evaluating_ = false;
631}
632
633bool MobileSetupHandler::NeedsReconnecting(
634    chromeos::CellularNetwork* network,
635    PlanActivationState* new_state,
636    std::string* error_description) {
637  DCHECK(network);
638  if (!network->failed() && !ConnectionTimeout())
639    return false;
640
641  // Try to reconnect again if reconnect failed, or if for some
642  // reasons we are still not connected after 45 seconds.
643  int max_retries = (state_ == PLAN_ACTIVATION_RECONNECTING_OTASP) ?
644                        kMaxConnectionRetryOTASP : kMaxConnectionRetry;
645  if (connection_retry_count_ < max_retries) {
646    UMA_HISTOGRAM_COUNTS("Cellular.ConnectionRetry", 1);
647    ConnectToNetwork(network, kFailedReconnectDelayMS);
648    return true;
649  }
650  // We simply can't connect anymore after all these tries.
651  UMA_HISTOGRAM_COUNTS("Cellular.ConnectionFailed", 1);
652  *new_state = PLAN_ACTIVATION_ERROR;
653  *error_description = GetErrorMessage(kFailedConnectivity);
654  return false;
655}
656
657bool MobileSetupHandler::ConnectToNetwork(
658    chromeos::CellularNetwork* network,
659    int delay) {
660  if (network && network->connecting_or_connected())
661    return true;
662  // Permit network connection changes only in reconnecting states.
663  if (state_ != PLAN_ACTIVATION_RECONNECTING_OTASP_TRY &&
664      state_ != PLAN_ACTIVATION_RECONNECTING &&
665      state_ != PLAN_ACTIVATION_RECONNECTING_OTASP)
666    return false;
667  if (network)
668    LOG(INFO) << "Connecting to: " << network->service_path();
669  connection_retry_count_++;
670  connection_start_time_ = base::Time::Now();
671  if (!network) {
672    LOG(WARNING) << "Connect failed."
673                 << (network ? network->service_path().c_str() : "no service");
674    // If we coudn't connect during reconnection phase, try to reconnect
675    // with a delay (and try to reconnect if needed).
676    scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(),
677                                                  delay);
678    BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
679        NewRunnableMethod(task.get(), &TaskProxy::ContinueConnecting),
680        delay);
681    return false;
682  }
683  chromeos::CrosLibrary::Get()->GetNetworkLibrary()->
684      ConnectToCellularNetwork(network);
685  return true;
686}
687
688void MobileSetupHandler::ForceReconnect(
689    chromeos::CellularNetwork* network,
690    int delay) {
691  DCHECK(network);
692  UMA_HISTOGRAM_COUNTS("Cellular.ActivationRetry", 1);
693  // Reset reconnect metrics.
694  connection_retry_count_ = 0;
695  connection_start_time_ = base::Time();
696  // First, disconnect...
697  LOG(INFO) << "Disconnecting from " << network->service_path();
698  chromeos::CrosLibrary::Get()->GetNetworkLibrary()->
699      DisconnectFromNetwork(network);
700  // Check the network state 3s after we disconnect to make sure.
701  scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(),
702                                                delay);
703  BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
704      NewRunnableMethod(task.get(), &TaskProxy::ContinueConnecting),
705      delay);
706}
707
708bool MobileSetupHandler::ConnectionTimeout() {
709  return (base::Time::Now() -
710            connection_start_time_).InSeconds() > kConnectionTimeoutSeconds;
711}
712
713void MobileSetupHandler::EvaluateCellularNetwork(
714    chromeos::CellularNetwork* network) {
715  if (!web_ui_)
716    return;
717
718  PlanActivationState new_state = state_;
719  if (!network) {
720    LOG(WARNING) << "Cellular service lost";
721    if (state_ == PLAN_ACTIVATION_RECONNECTING_OTASP_TRY ||
722        state_ == PLAN_ACTIVATION_RECONNECTING ||
723        state_ == PLAN_ACTIVATION_RECONNECTING_OTASP) {
724      // This might be the legit case when service is lost after activation.
725      // We need to make sure we force reconnection as soon as it shows up.
726      LOG(INFO) << "Force service reconnection";
727      connection_start_time_ = base::Time();
728    }
729    return;
730  }
731
732  // Prevent this method from being called if it is already on the stack.
733  // This might happen on some state transitions (ie. connect, disconnect).
734  if (evaluating_)
735    return;
736  evaluating_ = true;
737  std::string error_description;
738
739  LOG(WARNING) << "Cellular:\n  service=" << network->GetStateString().c_str()
740      << "\n  ui=" << GetStateDescription(state_)
741      << "\n  activation=" << network->GetActivationStateString().c_str()
742      << "\n  connectivity="
743      << network->GetConnectivityStateString().c_str()
744      << "\n  error=" << network->GetErrorString().c_str()
745      << "\n  setvice_path=" << network->service_path().c_str();
746  switch (state_) {
747    case PLAN_ACTIVATION_START: {
748      switch (network->activation_state()) {
749        case chromeos::ACTIVATION_STATE_ACTIVATED: {
750          if (network->failed_or_disconnected()) {
751            new_state = PLAN_ACTIVATION_RECONNECTING;
752          } else if (network->connected()) {
753            if (network->restricted_pool()) {
754              new_state = PLAN_ACTIVATION_SHOWING_PAYMENT;
755            } else {
756              new_state = PLAN_ACTIVATION_DONE;
757            }
758          }
759          break;
760        }
761        default: {
762          if (network->failed_or_disconnected() ||
763              network->state() == chromeos::STATE_ACTIVATION_FAILURE) {
764            new_state = (network->activation_state() ==
765                         chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) ?
766                            PLAN_ACTIVATION_TRYING_OTASP :
767                            PLAN_ACTIVATION_INITIATING_ACTIVATION;
768          } else if (network->connected()) {
769            DisconnectFromNetwork(network);
770            return;
771          }
772          break;
773        }
774      }
775      break;
776    }
777    case PLAN_ACTIVATION_START_OTASP: {
778      switch (network->activation_state()) {
779        case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED: {
780          if (network->failed_or_disconnected()) {
781            new_state = PLAN_ACTIVATION_OTASP;
782          } else if (network->connected()) {
783            DisconnectFromNetwork(network);
784            return;
785          }
786          break;
787        }
788        case chromeos::ACTIVATION_STATE_ACTIVATED:
789          new_state = PLAN_ACTIVATION_RECONNECTING_OTASP;
790          break;
791        default: {
792          LOG(WARNING) << "Unexpected activation state for device "
793                       << network->service_path().c_str();
794          break;
795        }
796      }
797      break;
798    }
799    case PLAN_ACTIVATION_DELAY_OTASP:
800      // Just ignore any changes until the OTASP retry timer kicks in.
801      evaluating_ = false;
802      return;
803    case PLAN_ACTIVATION_INITIATING_ACTIVATION:
804    case PLAN_ACTIVATION_OTASP:
805    case PLAN_ACTIVATION_TRYING_OTASP: {
806      switch (network->activation_state()) {
807        case chromeos::ACTIVATION_STATE_ACTIVATED:
808          if (network->failed_or_disconnected()) {
809            new_state = GetNextReconnectState(state_);
810          } else if (network->connected()) {
811            if (network->restricted_pool()) {
812              new_state = PLAN_ACTIVATION_SHOWING_PAYMENT;
813            } else {
814              new_state = PLAN_ACTIVATION_DONE;
815            }
816          }
817          break;
818        case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED:
819          if (network->connected()) {
820            if (network->restricted_pool())
821              new_state = PLAN_ACTIVATION_SHOWING_PAYMENT;
822          } else {
823            new_state = GetNextReconnectState(state_);
824          }
825          break;
826        case chromeos::ACTIVATION_STATE_NOT_ACTIVATED:
827        case chromeos::ACTIVATION_STATE_ACTIVATING:
828          // Wait in this state until activation state changes.
829          break;
830        default:
831          break;
832      }
833      break;
834    }
835    case PLAN_ACTIVATION_RECONNECTING_OTASP_TRY:
836    case PLAN_ACTIVATION_RECONNECTING: {
837      if (network->connected()) {
838        // Make sure other networks are not interfering with our detection of
839        // restricted pool.
840        DisableOtherNetworks();
841        // Wait until the service shows up and gets activated.
842        switch (network->activation_state()) {
843          case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED:
844          case chromeos::ACTIVATION_STATE_ACTIVATED:
845            if (network->connectivity_state() ==
846                         chromeos::CONN_STATE_NONE) {
847              LOG(WARNING) << "No connectivity for device "
848                           << network->service_path().c_str();
849              // If we are connected but there is no connectivity at all,
850              // restart the whole process again.
851              if (activation_attempt_ < kMaxActivationAttempt) {
852                activation_attempt_++;
853                LOG(WARNING) << "Reconnect attempt #"
854                             << activation_attempt_;
855                ForceReconnect(network, kFailedReconnectDelayMS);
856                evaluating_ = false;
857                return;
858              } else {
859                new_state = PLAN_ACTIVATION_ERROR;
860                UMA_HISTOGRAM_COUNTS("Cellular.ActivationRetryFailure", 1);
861                error_description = GetErrorMessage(kFailedConnectivity);
862              }
863            } else if (network->connectivity_state() ==
864                           chromeos::CONN_STATE_RESTRICTED) {
865              // If we have already received payment, don't show the payment
866              // page again. We should try to reconnect after some time instead.
867              new_state = PLAN_ACTIVATION_SHOWING_PAYMENT;
868            } else if (network->activation_state() ==
869                           chromeos::ACTIVATION_STATE_ACTIVATED) {
870              new_state = PLAN_ACTIVATION_DONE;
871            }
872            break;
873          default:
874            break;
875        }
876      } else if (NeedsReconnecting(network, &new_state, &error_description)) {
877        evaluating_ = false;
878        return;
879      }
880      break;
881    }
882    case PLAN_ACTIVATION_RECONNECTING_OTASP: {
883      if (network->connected()) {
884        // Make sure other networks are not interfering with our detection of
885        // restricted pool.
886        DisableOtherNetworks();
887        // Wait until the service shows up and gets activated.
888        switch (network->activation_state()) {
889          case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED:
890          case chromeos::ACTIVATION_STATE_ACTIVATED:
891            if (network->connectivity_state() == chromeos::CONN_STATE_NONE ||
892                network->connectivity_state() ==
893                    chromeos::CONN_STATE_RESTRICTED) {
894              LOG(WARNING) << "Still no connectivity after OTASP for device "
895                           << network->service_path().c_str();
896              // If we have already received payment, don't show the payment
897              // page again. We should try to reconnect after some time instead.
898              if (reconnect_wait_count_ < kMaxReconnectAttemptOTASP) {
899                reconnect_wait_count_++;
900                LOG(WARNING) << "OTASP reconnect attempt #"
901                             << reconnect_wait_count_;
902                ForceReconnect(network, kPostPaymentReconnectDelayMS);
903                evaluating_ = false;
904                return;
905              } else {
906                new_state = PLAN_ACTIVATION_ERROR;
907                UMA_HISTOGRAM_COUNTS("Cellular.PostPaymentConnectFailure", 1);
908                error_description = GetErrorMessage(kFailedConnectivity);
909              }
910            } else if (network->connectivity_state() ==
911                           chromeos::CONN_STATE_UNRESTRICTED) {
912              new_state = PLAN_ACTIVATION_DONE;
913            }
914            break;
915          default:
916            break;
917        }
918      } else if (NeedsReconnecting(network, &new_state, &error_description)) {
919        evaluating_ = false;
920        return;
921      }
922      break;
923    }
924    case PLAN_ACTIVATION_PAGE_LOADING:
925      break;
926    // Just ignore all signals until the site confirms payment.
927    case PLAN_ACTIVATION_SHOWING_PAYMENT:
928      // Activation completed/failed, ignore network changes.
929    case PLAN_ACTIVATION_DONE:
930    case PLAN_ACTIVATION_ERROR:
931      break;
932  }
933
934  if (new_state != PLAN_ACTIVATION_ERROR &&
935      GotActivationError(network, &error_description)) {
936    // Check for this special case when we try to do activate partially
937    // activated device. If that attempt failed, try to disconnect to clear the
938    // state and reconnect again.
939    if ((network->activation_state() ==
940            chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED ||
941        network->activation_state() == chromeos::ACTIVATION_STATE_ACTIVATING) &&
942        (network->error() == chromeos::ERROR_NO_ERROR ||
943            network->error() == chromeos::ERROR_OTASP_FAILED) &&
944        network->state() == chromeos::STATE_ACTIVATION_FAILURE) {
945      LOG(WARNING) << "Activation failure detected "
946                   << network->service_path().c_str();
947      switch (state_) {
948        case PLAN_ACTIVATION_OTASP:
949        case PLAN_ACTIVATION_RECONNECTING_OTASP:
950          new_state = PLAN_ACTIVATION_DELAY_OTASP;
951          break;
952        case PLAN_ACTIVATION_TRYING_OTASP:
953          new_state = PLAN_ACTIVATION_RECONNECTING_OTASP_TRY;
954          break;
955        case PLAN_ACTIVATION_INITIATING_ACTIVATION:
956          new_state = PLAN_ACTIVATION_RECONNECTING;
957          break;
958        case PLAN_ACTIVATION_START:
959          // We are just starting, so this must be previous activation attempt
960          // failure.
961          new_state = PLAN_ACTIVATION_TRYING_OTASP;
962          break;
963        case PLAN_ACTIVATION_DELAY_OTASP:
964        case PLAN_ACTIVATION_RECONNECTING_OTASP_TRY:
965        case PLAN_ACTIVATION_RECONNECTING:
966          new_state = state_;
967          break;
968        default:
969          new_state = PLAN_ACTIVATION_ERROR;
970          break;
971      }
972    } else {
973      LOG(WARNING) << "Unexpected activation failure for "
974                   << network->service_path().c_str();
975      new_state = PLAN_ACTIVATION_ERROR;
976    }
977  }
978
979  if (new_state == PLAN_ACTIVATION_ERROR && !error_description.length())
980    error_description = GetErrorMessage(kErrorDefault);
981
982  ChangeState(network, new_state, error_description);
983  evaluating_ = false;
984}
985
986MobileSetupHandler::PlanActivationState
987    MobileSetupHandler::GetNextReconnectState(
988        MobileSetupHandler::PlanActivationState state) {
989  switch (state) {
990    case PLAN_ACTIVATION_INITIATING_ACTIVATION:
991      return PLAN_ACTIVATION_RECONNECTING;
992    case PLAN_ACTIVATION_OTASP:
993      return PLAN_ACTIVATION_RECONNECTING_OTASP;
994    case PLAN_ACTIVATION_TRYING_OTASP:
995      return PLAN_ACTIVATION_RECONNECTING_OTASP_TRY;
996    default:
997      return PLAN_ACTIVATION_RECONNECTING;
998  }
999}
1000
1001// Debugging helper function, will take it out at the end.
1002const char* MobileSetupHandler::GetStateDescription(
1003    PlanActivationState state) {
1004  switch (state) {
1005    case PLAN_ACTIVATION_PAGE_LOADING:
1006      return "PAGE_LOADING";
1007    case PLAN_ACTIVATION_START:
1008      return "ACTIVATION_START";
1009    case PLAN_ACTIVATION_TRYING_OTASP:
1010      return "TRYING_OTASP";
1011    case PLAN_ACTIVATION_RECONNECTING_OTASP_TRY:
1012      return "RECONNECTING_OTASP_TRY";
1013    case PLAN_ACTIVATION_INITIATING_ACTIVATION:
1014      return "INITIATING_ACTIVATION";
1015    case PLAN_ACTIVATION_RECONNECTING:
1016      return "RECONNECTING";
1017    case PLAN_ACTIVATION_SHOWING_PAYMENT:
1018      return "SHOWING_PAYMENT";
1019    case PLAN_ACTIVATION_START_OTASP:
1020      return "START_OTASP";
1021    case PLAN_ACTIVATION_DELAY_OTASP:
1022      return "DELAY_OTASP";
1023    case PLAN_ACTIVATION_OTASP:
1024      return "OTASP";
1025    case PLAN_ACTIVATION_RECONNECTING_OTASP:
1026      return "RECONNECTING_OTASP";
1027    case PLAN_ACTIVATION_DONE:
1028      return "DONE";
1029    case PLAN_ACTIVATION_ERROR:
1030      return "ERROR";
1031  }
1032  return "UNKNOWN";
1033}
1034
1035
1036void MobileSetupHandler::CompleteActivation(
1037    chromeos::CellularNetwork* network) {
1038  // Remove observers, we are done with this page.
1039  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
1040      GetNetworkLibrary();
1041  lib->RemoveNetworkManagerObserver(this);
1042  lib->RemoveObserverForAllNetworks(this);
1043  if (lib->IsLocked())
1044    lib->Unlock();
1045  // If we have successfully activated the connection, set autoconnect flag.
1046  if (network)
1047    network->SetAutoConnect(true);
1048  // Reactivate other types of connections if we have
1049  // shut them down previously.
1050  ReEnableOtherConnections();
1051}
1052
1053void MobileSetupHandler::UpdatePage(
1054    chromeos::CellularNetwork* network,
1055    const std::string& error_description) {
1056  DictionaryValue device_dict;
1057  if (network)
1058    GetDeviceInfo(network, &device_dict);
1059  device_dict.SetInteger("state", state_);
1060  if (error_description.length())
1061    device_dict.SetString("error", error_description);
1062  web_ui_->CallJavascriptFunction(
1063      kJsDeviceStatusChangedHandler, device_dict);
1064}
1065
1066
1067void MobileSetupHandler::ChangeState(chromeos::CellularNetwork* network,
1068                                     PlanActivationState new_state,
1069                                     const std::string& error_description) {
1070  static bool first_time = true;
1071  if (state_ == new_state && !first_time)
1072    return;
1073  LOG(WARNING) << "Activation state flip old = "
1074      << GetStateDescription(state_)
1075      << ", new = " << GetStateDescription(new_state);
1076  first_time = false;
1077
1078  // Pick action that should happen on leaving the old state.
1079  switch (state_) {
1080    case PLAN_ACTIVATION_RECONNECTING_OTASP_TRY:
1081    case PLAN_ACTIVATION_RECONNECTING:
1082    case PLAN_ACTIVATION_RECONNECTING_OTASP:
1083      if (reconnect_timer_.IsRunning()) {
1084        reconnect_timer_.Stop();
1085      }
1086      break;
1087    default:
1088      break;
1089  }
1090  state_ = new_state;
1091
1092  // Signal to JS layer that the state is changing.
1093  UpdatePage(network, error_description);
1094
1095  // Pick action that should happen on entering the new state.
1096  switch (new_state) {
1097    case PLAN_ACTIVATION_START:
1098      break;
1099    case PLAN_ACTIVATION_DELAY_OTASP: {
1100      UMA_HISTOGRAM_COUNTS("Cellular.RetryOTASP", 1);
1101      scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), 0);
1102      BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
1103          NewRunnableMethod(task.get(), &TaskProxy::RetryOTASP),
1104          kOTASPRetryDelay);
1105      break;
1106    }
1107    case PLAN_ACTIVATION_INITIATING_ACTIVATION:
1108    case PLAN_ACTIVATION_TRYING_OTASP:
1109    case PLAN_ACTIVATION_OTASP:
1110      DCHECK(network);
1111      LOG(WARNING) << "Activating service " << network->service_path().c_str();
1112      UMA_HISTOGRAM_COUNTS("Cellular.ActivationTry", 1);
1113      if (!network->StartActivation()) {
1114        UMA_HISTOGRAM_COUNTS("Cellular.ActivationFailure", 1);
1115        if (new_state == PLAN_ACTIVATION_OTASP) {
1116          ChangeState(network, PLAN_ACTIVATION_DELAY_OTASP, std::string());
1117        } else {
1118          ChangeState(network, PLAN_ACTIVATION_ERROR,
1119                      GetErrorMessage(kFailedConnectivity));
1120        }
1121      }
1122      break;
1123    case PLAN_ACTIVATION_RECONNECTING_OTASP_TRY:
1124    case PLAN_ACTIVATION_RECONNECTING:
1125    case PLAN_ACTIVATION_RECONNECTING_OTASP: {
1126      // Start reconnect timer. This will ensure that we are not left in
1127      // limbo by the network library.
1128      if (!reconnect_timer_.IsRunning()) {
1129        reconnect_timer_.Start(
1130            base::TimeDelta::FromMilliseconds(kReconnectTimerDelayMS),
1131            this, &MobileSetupHandler::ReconnectTimerFired);
1132      }
1133      // Reset connection metrics and try to connect.
1134      reconnect_wait_count_ = 0;
1135      connection_retry_count_ = 0;
1136      connection_start_time_ = base::Time::Now();
1137      ConnectToNetwork(network, kReconnectDelayMS);
1138      break;
1139    }
1140    case PLAN_ACTIVATION_PAGE_LOADING:
1141      return;
1142    case PLAN_ACTIVATION_SHOWING_PAYMENT:
1143      // Fix for fix SSL for the walled gardens where cert chain verification
1144      // might not work.
1145      break;
1146    case PLAN_ACTIVATION_DONE:
1147      DCHECK(network);
1148      CompleteActivation(network);
1149      UMA_HISTOGRAM_COUNTS("Cellular.MobileSetupSucceeded", 1);
1150      break;
1151    case PLAN_ACTIVATION_ERROR:
1152      CompleteActivation(NULL);
1153      UMA_HISTOGRAM_COUNTS("Cellular.PlanFailed", 1);
1154      break;
1155    default:
1156      break;
1157  }
1158}
1159
1160void MobileSetupHandler::ReEnableOtherConnections() {
1161  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
1162      GetNetworkLibrary();
1163  if (reenable_ethernet_) {
1164    reenable_ethernet_ = false;
1165    lib->EnableEthernetNetworkDevice(true);
1166  }
1167  if (reenable_wifi_) {
1168    reenable_wifi_ = false;
1169    lib->EnableWifiNetworkDevice(true);
1170  }
1171
1172  PrefService* prefs = g_browser_process->local_state();
1173  if (reenable_cert_check_) {
1174    prefs->SetBoolean(prefs::kCertRevocationCheckingEnabled,
1175                      true);
1176    reenable_cert_check_ = false;
1177  }
1178}
1179
1180void MobileSetupHandler::SetupActivationProcess(
1181    chromeos::CellularNetwork* network) {
1182  if (!network)
1183    return;
1184
1185  // Disable SSL cert checks since we will be doing this in
1186  // restricted pool.
1187  PrefService* prefs = g_browser_process->local_state();
1188  if (!reenable_cert_check_ &&
1189      prefs->GetBoolean(
1190          prefs::kCertRevocationCheckingEnabled)) {
1191    reenable_cert_check_ = true;
1192    prefs->SetBoolean(prefs::kCertRevocationCheckingEnabled, false);
1193  }
1194
1195  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
1196      GetNetworkLibrary();
1197  // Disable autoconnect to cellular network.
1198  network->SetAutoConnect(false);
1199
1200  // Prevent any other network interference.
1201  DisableOtherNetworks();
1202  lib->Lock();
1203}
1204
1205void MobileSetupHandler::DisableOtherNetworks() {
1206  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
1207      GetNetworkLibrary();
1208  // Disable ethernet and wifi.
1209  if (lib->ethernet_enabled()) {
1210    reenable_ethernet_ = true;
1211    lib->EnableEthernetNetworkDevice(false);
1212  }
1213  if (lib->wifi_enabled()) {
1214    reenable_wifi_ = true;
1215    lib->EnableWifiNetworkDevice(false);
1216  }
1217}
1218
1219bool MobileSetupHandler::GotActivationError(
1220    chromeos::CellularNetwork* network, std::string* error) {
1221  DCHECK(network);
1222  bool got_error = false;
1223  const char* error_code = kErrorDefault;
1224
1225  // This is the magic for detection of errors in during activation process.
1226  if (network->state() == chromeos::STATE_FAILURE &&
1227      network->error() == chromeos::ERROR_AAA_FAILED) {
1228    if (network->activation_state() ==
1229            chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) {
1230      error_code = kErrorBadConnectionPartial;
1231    } else if (network->activation_state() ==
1232            chromeos::ACTIVATION_STATE_ACTIVATED) {
1233      if (network->roaming_state() == chromeos::ROAMING_STATE_HOME) {
1234        error_code = kErrorBadConnectionActivated;
1235      } else if (network->roaming_state() == chromeos::ROAMING_STATE_ROAMING) {
1236        error_code = kErrorRoamingOnConnection;
1237      }
1238    }
1239    got_error = true;
1240  } else if (network->state() == chromeos::STATE_ACTIVATION_FAILURE) {
1241    if (network->error() == chromeos::ERROR_NEED_EVDO) {
1242      if (network->activation_state() ==
1243              chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED)
1244        error_code = kErrorNoEVDO;
1245    } else if (network->error() == chromeos::ERROR_NEED_HOME_NETWORK) {
1246      if (network->activation_state() ==
1247              chromeos::ACTIVATION_STATE_NOT_ACTIVATED) {
1248        error_code = kErrorRoamingActivation;
1249      } else if (network->activation_state() ==
1250                    chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) {
1251        error_code = kErrorRoamingPartiallyActivated;
1252      }
1253    }
1254    got_error = true;
1255  }
1256
1257  if (got_error)
1258    *error = GetErrorMessage(error_code);
1259
1260  return got_error;
1261}
1262
1263void MobileSetupHandler::GetDeviceInfo(chromeos::CellularNetwork* network,
1264                                       DictionaryValue* value) {
1265  DCHECK(network);
1266  chromeos::NetworkLibrary* cros =
1267      chromeos::CrosLibrary::Get()->GetNetworkLibrary();
1268  if (!cros)
1269    return;
1270  value->SetString("carrier", network->name());
1271  value->SetString("payment_url", network->payment_url());
1272  const chromeos::NetworkDevice* device =
1273      cros->FindNetworkDeviceByPath(network->device_path());
1274  if (device) {
1275    value->SetString("MEID", device->meid());
1276    value->SetString("IMEI", device->imei());
1277    value->SetString("MDN", device->mdn());
1278  }
1279}
1280
1281std::string MobileSetupHandler::GetErrorMessage(const std::string& code) {
1282  if (!cellular_config_.get())
1283    return "";
1284  return cellular_config_->GetErrorMessage(code);
1285}
1286
1287void MobileSetupHandler::LoadCellularConfig() {
1288  static bool config_loaded = false;
1289  if (config_loaded)
1290    return;
1291  config_loaded = true;
1292  // Load partner customization startup manifest if it is available.
1293  FilePath config_path(kCellularConfigPath);
1294  bool config_exists = false;
1295  {
1296    // Reading config file causes us to do blocking IO on UI thread.
1297    // Temporarily allow it until we fix http://crosbug.com/11535
1298    base::ThreadRestrictions::ScopedAllowIO allow_io;
1299    config_exists = file_util::PathExists(config_path);
1300  }
1301  if (config_exists) {
1302    scoped_ptr<CellularConfigDocument> config(new CellularConfigDocument());
1303    bool config_loaded = config->LoadFromFile(config_path);
1304    if (config_loaded) {
1305      LOG(INFO) << "Cellular config file loaded: " << kCellularConfigPath;
1306      // lock
1307      cellular_config_.reset(config.release());
1308    } else {
1309      LOG(ERROR) << "Error loading cellular config file: " <<
1310          kCellularConfigPath;
1311    }
1312  }
1313}
1314
1315
1316////////////////////////////////////////////////////////////////////////////////
1317//
1318// MobileSetupUI
1319//
1320////////////////////////////////////////////////////////////////////////////////
1321
1322MobileSetupUI::MobileSetupUI(TabContents* contents) : WebUI(contents) {
1323  chromeos::CellularNetwork* network = GetCellularNetwork();
1324  std::string service_path = network ? network->service_path() : std::string();
1325  MobileSetupHandler* handler = new MobileSetupHandler(service_path);
1326  AddMessageHandler((handler)->Attach(this));
1327  handler->Init(contents);
1328  MobileSetupUIHTMLSource* html_source =
1329      new MobileSetupUIHTMLSource(service_path);
1330
1331  // Set up the chrome://mobilesetup/ source.
1332  contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
1333}
1334