普通文本  |  1334行  |  48.36 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/chromeos/mobile_setup_ui.h"

#include <algorithm>
#include <map>
#include <string>

#include "base/callback.h"
#include "base/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "base/timer.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/network_library.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
#include "chrome/common/jstemplate_builder.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/browser/browser_thread.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "googleurl/src/gurl.h"
#include "grit/browser_resources.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"

namespace {

// Host page JS API function names.
const char kJsApiStartActivation[] = "startActivation";
const char kJsApiSetTransactionStatus[] = "setTransactionStatus";

const char kJsDeviceStatusChangedHandler[] =
    "mobile.MobileSetup.deviceStateChanged";

// Error codes matching codes defined in the cellular config file.
const char kErrorDefault[] = "default";
const char kErrorBadConnectionPartial[] = "bad_connection_partial";
const char kErrorBadConnectionActivated[] = "bad_connection_activated";
const char kErrorRoamingOnConnection[] = "roaming_connection";
const char kErrorNoEVDO[] = "no_evdo";
const char kErrorRoamingActivation[] = "roaming_activation";
const char kErrorRoamingPartiallyActivated[] = "roaming_partially_activated";
const char kErrorNoService[] = "no_service";
const char kErrorDisabled[] = "disabled";
const char kErrorNoDevice[] = "no_device";
const char kFailedPaymentError[] = "failed_payment";
const char kFailedConnectivity[] = "connectivity";
const char kErrorAlreadyRunning[] = "already_running";

// Cellular configuration file path.
const char kCellularConfigPath[] =
    "/usr/share/chromeos-assets/mobile/mobile_config.json";

// Cellular config file field names.
const char kVersionField[] = "version";
const char kErrorsField[] = "errors";

// Number of times we will retry to restart the activation process in case
// there is no connectivity in the restricted pool.
const int kMaxActivationAttempt = 3;
// Number of times we will retry to reconnect if connection fails.
const int kMaxConnectionRetry = 10;
// Number of times we will retry to reconnect if connection fails.
const int kMaxConnectionRetryOTASP = 30;
// Number of times we will retry to reconnect after payment is processed.
const int kMaxReconnectAttemptOTASP = 30;
// Reconnect retry delay (after payment is processed).
const int kPostPaymentReconnectDelayMS = 30000;   // 30s.
// Connection timeout in seconds.
const int kConnectionTimeoutSeconds = 45;
// Reconnect delay.
const int kReconnectDelayMS = 3000;
// Reconnect timer delay.
const int kReconnectTimerDelayMS = 5000;
// Reconnect delay after previous failure.
const int kFailedReconnectDelayMS = 10000;
// Retry delay after failed OTASP attempt.
const int kOTASPRetryDelay = 20000;

chromeos::CellularNetwork* GetCellularNetwork() {
  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
      GetNetworkLibrary();
  if (lib->cellular_networks().begin() != lib->cellular_networks().end()) {
    return *(lib->cellular_networks().begin());
  }
  return NULL;
}

chromeos::CellularNetwork* GetCellularNetwork(
    const std::string& service_path) {
  return chromeos::CrosLibrary::Get()->
      GetNetworkLibrary()->FindCellularNetworkByPath(service_path);
}

}  // namespace

class CellularConfigDocument {
 public:
  CellularConfigDocument() {}

  // Return error message for a given code.
  std::string GetErrorMessage(const std::string& code);
  const std::string& version() { return version_; }

  bool LoadFromFile(const FilePath& config_path);

 private:
  std::string version_;
  std::map<std::string, std::string> error_map_;

  DISALLOW_COPY_AND_ASSIGN(CellularConfigDocument);
};

static std::map<std::string, std::string> error_messages_;

class MobileSetupUIHTMLSource : public ChromeURLDataManager::DataSource {
 public:
  explicit MobileSetupUIHTMLSource(const std::string& service_path);

  // Called when the network layer has requested a resource underneath
  // the path we registered.
  virtual void StartDataRequest(const std::string& path,
                                bool is_incognito,
                                int request_id);
  virtual std::string GetMimeType(const std::string&) const {
    return "text/html";
  }

 private:
  virtual ~MobileSetupUIHTMLSource() {}

  std::string service_path_;
  DISALLOW_COPY_AND_ASSIGN(MobileSetupUIHTMLSource);
};

// The handler for Javascript messages related to the "register" view.
class MobileSetupHandler
  : public WebUIMessageHandler,
    public chromeos::NetworkLibrary::NetworkManagerObserver,
    public chromeos::NetworkLibrary::NetworkObserver,
    public base::SupportsWeakPtr<MobileSetupHandler> {
 public:
  explicit MobileSetupHandler(const std::string& service_path);
  virtual ~MobileSetupHandler();

  // Init work after Attach.
  void Init(TabContents* contents);

  // WebUIMessageHandler implementation.
  virtual WebUIMessageHandler* Attach(WebUI* web_ui);
  virtual void RegisterMessages();

  // NetworkLibrary::NetworkManagerObserver implementation.
  virtual void OnNetworkManagerChanged(chromeos::NetworkLibrary* obj);
  // NetworkLibrary::NetworkObserver implementation.
  virtual void OnNetworkChanged(chromeos::NetworkLibrary* obj,
                                const chromeos::Network* network);

 private:
  typedef enum PlanActivationState {
    PLAN_ACTIVATION_PAGE_LOADING            = -1,
    PLAN_ACTIVATION_START                   = 0,
    PLAN_ACTIVATION_TRYING_OTASP            = 1,
    PLAN_ACTIVATION_RECONNECTING_OTASP_TRY  = 2,
    PLAN_ACTIVATION_INITIATING_ACTIVATION   = 3,
    PLAN_ACTIVATION_RECONNECTING            = 4,
    PLAN_ACTIVATION_SHOWING_PAYMENT         = 5,
    PLAN_ACTIVATION_DELAY_OTASP             = 6,
    PLAN_ACTIVATION_START_OTASP             = 7,
    PLAN_ACTIVATION_OTASP                   = 8,
    PLAN_ACTIVATION_RECONNECTING_OTASP      = 9,
    PLAN_ACTIVATION_DONE                    = 10,
    PLAN_ACTIVATION_ERROR                   = 0xFF,
  } PlanActivationState;

  class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> {
   public:
    TaskProxy(const base::WeakPtr<MobileSetupHandler>& handler, int delay)
        : handler_(handler), delay_(delay) {
    }
    TaskProxy(const base::WeakPtr<MobileSetupHandler>& handler,
              const std::string& status)
        : handler_(handler), status_(status) {
    }
    void HandleStartActivation() {
      if (handler_)
        handler_->StartActivation();
    }
    void HandleSetTransactionStatus() {
      if (handler_)
        handler_->SetTransactionStatus(status_);
    }
    void ContinueConnecting() {
      if (handler_)
        handler_->ContinueConnecting(delay_);
    }
    void RetryOTASP() {
      if (handler_)
        handler_->RetryOTASP();
    }
   private:
    base::WeakPtr<MobileSetupHandler> handler_;
    std::string status_;
    int delay_;
    DISALLOW_COPY_AND_ASSIGN(TaskProxy);
  };

  // Handlers for JS WebUI messages.
  void HandleSetTransactionStatus(const ListValue* args);
  void HandleStartActivation(const ListValue* args);
  void SetTransactionStatus(const std::string& status);
  // Schedules activation process via task proxy.
  void InitiateActivation();
  // Starts activation.
  void StartActivation();
  // Retried OTASP.
  void RetryOTASP();
  // Continues activation process. This method is called after we disconnect
  // due to detected connectivity issue to kick off reconnection.
  void ContinueConnecting(int delay);

  // Sends message to host registration page with system/user info data.
  void SendDeviceInfo();

  // Callback for when |reconnect_timer_| fires.
  void ReconnectTimerFired();
  // Starts OTASP process.
  void StartOTASP();
  // Checks if we need to reconnect due to failed connection attempt.
  bool NeedsReconnecting(chromeos::CellularNetwork* network,
                         PlanActivationState* new_state,
                         std::string* error_description);
  // Disconnect from network.
  void DisconnectFromNetwork(chromeos::CellularNetwork* network);
  // Connects to cellular network, resets connection timer.
  bool ConnectToNetwork(chromeos::CellularNetwork* network, int delay);
  // Forces disconnect / reconnect when we detect portal connectivity issues.
  void ForceReconnect(chromeos::CellularNetwork* network, int delay);
  // Reports connection timeout.
  bool ConnectionTimeout();
  // Verify the state of cellular network and modify internal state.
  void EvaluateCellularNetwork(chromeos::CellularNetwork* network);
  // Check the current cellular network for error conditions.
  bool GotActivationError(chromeos::CellularNetwork* network,
                          std::string* error);
  // Sends status updates to WebUI page.
  void UpdatePage(chromeos::CellularNetwork* network,
                  const std::string& error_description);
  // Changes internal state.
  void ChangeState(chromeos::CellularNetwork* network,
                   PlanActivationState new_state,
                   const std::string& error_description);
  // Prepares network devices for cellular activation process.
  void SetupActivationProcess(chromeos::CellularNetwork* network);
  // Disables ethernet and wifi newtorks since they interefere with
  // detection of restricted pool on cellular side.
  void DisableOtherNetworks();
  // Resets network devices after cellular activation process.
  // |network| should be NULL if the activation process failed.
  void CompleteActivation(chromeos::CellularNetwork* network);
  // Control routines for handling other types of connections during
  // cellular activation.
  void ReEnableOtherConnections();

  // Converts the currently active CellularNetwork device into a JS object.
  static void GetDeviceInfo(chromeos::CellularNetwork* network,
                            DictionaryValue* value);
  static bool ShouldReportDeviceState(std::string* state, std::string* error);

  // Performs activation state cellular device evaluation.
  // Returns false if device activation failed. In this case |error|
  // will contain error message to be reported to Web UI.
  static bool EvaluateCellularDeviceState(bool* report_status,
                                          std::string* state,
                                          std::string* error);

  // Return error message for a given code.
  static std::string GetErrorMessage(const std::string& code);
  static void LoadCellularConfig();

  // Returns next reconnection state based on the current activation phase.
  static PlanActivationState GetNextReconnectState(PlanActivationState state);
  static const char* GetStateDescription(PlanActivationState state);

  static scoped_ptr<CellularConfigDocument> cellular_config_;

  TabContents* tab_contents_;
  // Internal handler state.
  PlanActivationState state_;
  std::string service_path_;
  // Flags that control if wifi and ethernet connection needs to be restored
  // after the activation of cellular network.
  bool reenable_wifi_;
  bool reenable_ethernet_;
  bool reenable_cert_check_;
  bool evaluating_;
  // True if we think that another tab is already running activation.
  bool already_running_;
  // Connection retry counter.
  int connection_retry_count_;
  // Post payment reconnect wait counters.
  int reconnect_wait_count_;
  // Activation retry attempt count;
  int activation_attempt_;
  // Connection start time.
  base::Time connection_start_time_;
  // Timer that monitors reconnection timeouts.
  base::RepeatingTimer<MobileSetupHandler> reconnect_timer_;

  DISALLOW_COPY_AND_ASSIGN(MobileSetupHandler);
};

scoped_ptr<CellularConfigDocument> MobileSetupHandler::cellular_config_;

////////////////////////////////////////////////////////////////////////////////
//
// CellularConfigDocument
//
////////////////////////////////////////////////////////////////////////////////

std::string CellularConfigDocument::GetErrorMessage(const std::string& code) {
  std::map<std::string, std::string>::iterator iter = error_map_.find(code);
  if (iter == error_map_.end())
    return code;
  return iter->second;
}

bool CellularConfigDocument::LoadFromFile(const FilePath& config_path) {
  error_map_.clear();

  std::string config;
  {
    // Reading config file causes us to do blocking IO on UI thread.
    // Temporarily allow it until we fix http://crosbug.com/11535
    base::ThreadRestrictions::ScopedAllowIO allow_io;
    if (!file_util::ReadFileToString(config_path, &config))
      return false;
  }

  scoped_ptr<Value> root(base::JSONReader::Read(config, true));
  DCHECK(root.get() != NULL);
  if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) {
    LOG(WARNING) << "Bad cellular config file";
    return false;
  }

  DictionaryValue* root_dict = static_cast<DictionaryValue*>(root.get());
  if (!root_dict->GetString(kVersionField, &version_)) {
    LOG(WARNING) << "Cellular config file missing version";
    return false;
  }
  DictionaryValue* errors = NULL;
  if (!root_dict->GetDictionary(kErrorsField, &errors))
    return false;
  for (DictionaryValue::key_iterator keys = errors->begin_keys();
       keys != errors->end_keys();
       ++keys) {
    std::string value;
    if (!errors->GetString(*keys, &value)) {
      LOG(WARNING) << "Bad cellular config error value";
      error_map_.clear();
      return false;
    }

    error_map_.insert(std::pair<std::string, std::string>(*keys, value));
  }
  return true;
}

////////////////////////////////////////////////////////////////////////////////
//
// MobileSetupUIHTMLSource
//
////////////////////////////////////////////////////////////////////////////////

MobileSetupUIHTMLSource::MobileSetupUIHTMLSource(
    const std::string& service_path)
    : DataSource(chrome::kChromeUIMobileSetupHost, MessageLoop::current()),
      service_path_(service_path) {
}

void MobileSetupUIHTMLSource::StartDataRequest(const std::string& path,
                                               bool is_incognito,
                                               int request_id) {
  chromeos::CellularNetwork* network = GetCellularNetwork(service_path_);
  DCHECK(network);
  DictionaryValue strings;
  strings.SetString("title", l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE));
  strings.SetString("connecting_header",
                    l10n_util::GetStringFUTF16(IDS_MOBILE_CONNECTING_HEADER,
                        network ? UTF8ToUTF16(network->name()) : string16()));
  strings.SetString("error_header",
                    l10n_util::GetStringUTF16(IDS_MOBILE_ERROR_HEADER));
  strings.SetString("activating_header",
                    l10n_util::GetStringUTF16(IDS_MOBILE_ACTIVATING_HEADER));
  strings.SetString("completed_header",
                    l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_HEADER));
  strings.SetString("please_wait",
                    l10n_util::GetStringUTF16(IDS_MOBILE_PLEASE_WAIT));
  strings.SetString("completed_text",
                    l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_TEXT));
  strings.SetString("close_button",
                    l10n_util::GetStringUTF16(IDS_CLOSE));
  SetFontAndTextDirection(&strings);

  static const base::StringPiece html(
      ResourceBundle::GetSharedInstance().GetRawDataResource(
          IDR_MOBILE_SETUP_PAGE_HTML));

  const std::string full_html = jstemplate_builder::GetI18nTemplateHtml(
      html, &strings);

  scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
  html_bytes->data.resize(full_html.size());
  std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());

  SendResponse(request_id, html_bytes);
}

////////////////////////////////////////////////////////////////////////////////
//
// MobileSetupHandler
//
////////////////////////////////////////////////////////////////////////////////
MobileSetupHandler::MobileSetupHandler(const std::string& service_path)
    : tab_contents_(NULL),
      state_(PLAN_ACTIVATION_PAGE_LOADING),
      service_path_(service_path),
      reenable_wifi_(false),
      reenable_ethernet_(false),
      reenable_cert_check_(false),
      evaluating_(false),
      already_running_(false),
      connection_retry_count_(0),
      reconnect_wait_count_(0),
      activation_attempt_(0) {
}

MobileSetupHandler::~MobileSetupHandler() {
  reconnect_timer_.Stop();
  chromeos::NetworkLibrary* lib =
      chromeos::CrosLibrary::Get()->GetNetworkLibrary();
  lib->RemoveNetworkManagerObserver(this);
  lib->RemoveObserverForAllNetworks(this);
  if (lib->IsLocked())
    lib->Unlock();
  ReEnableOtherConnections();
}

WebUIMessageHandler* MobileSetupHandler::Attach(WebUI* web_ui) {
  return WebUIMessageHandler::Attach(web_ui);
}

void MobileSetupHandler::Init(TabContents* contents) {
  tab_contents_ = contents;
  LoadCellularConfig();
  if (!chromeos::CrosLibrary::Get()->GetNetworkLibrary()->IsLocked())
    SetupActivationProcess(GetCellularNetwork(service_path_));
  else
    already_running_ = true;
}

void MobileSetupHandler::RegisterMessages() {
  web_ui_->RegisterMessageCallback(kJsApiStartActivation,
      NewCallback(this, &MobileSetupHandler::HandleStartActivation));
  web_ui_->RegisterMessageCallback(kJsApiSetTransactionStatus,
      NewCallback(this, &MobileSetupHandler::HandleSetTransactionStatus));
}

void MobileSetupHandler::OnNetworkManagerChanged(
    chromeos::NetworkLibrary* cros) {
  if (state_ == PLAN_ACTIVATION_PAGE_LOADING)
    return;
  // Note that even though we get here when the service has
  // reappeared after disappearing earlier in the activation
  // process, there's no need to re-establish the NetworkObserver,
  // because the service path remains the same.
  EvaluateCellularNetwork(GetCellularNetwork(service_path_));
}

void MobileSetupHandler::OnNetworkChanged(chromeos::NetworkLibrary* cros,
                                          const chromeos::Network* network) {
  if (state_ == PLAN_ACTIVATION_PAGE_LOADING)
    return;
  DCHECK(network && network->type() == chromeos::TYPE_CELLULAR);
  EvaluateCellularNetwork(GetCellularNetwork(network->service_path()));
}

void MobileSetupHandler::HandleStartActivation(const ListValue* args) {
  InitiateActivation();
  UMA_HISTOGRAM_COUNTS("Cellular.MobileSetupStart", 1);
}

void MobileSetupHandler::HandleSetTransactionStatus(const ListValue* args) {
  const size_t kSetTransactionStatusParamCount = 1;
  if (args->GetSize() != kSetTransactionStatusParamCount)
    return;
  // Get change callback function name.
  std::string status;
  if (!args->GetString(0, &status))
    return;
  scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), status);
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(task.get(), &TaskProxy::HandleSetTransactionStatus));
}

void MobileSetupHandler::InitiateActivation() {
  scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), 0);
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(task.get(), &TaskProxy::HandleStartActivation));
}

void MobileSetupHandler::StartActivation() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  chromeos::NetworkLibrary* lib =
      chromeos::CrosLibrary::Get()->GetNetworkLibrary();
  chromeos::CellularNetwork* network = GetCellularNetwork(service_path_);
  // Check if we can start activation process.
  if (!network || already_running_) {
    std::string error;
    if (already_running_)
      error = kErrorAlreadyRunning;
    else if (!lib->cellular_available())
      error = kErrorNoDevice;
    else if (!lib->cellular_enabled())
      error = kErrorDisabled;
    else
      error = kErrorNoService;
    ChangeState(NULL, PLAN_ACTIVATION_ERROR, GetErrorMessage(error));
    return;
  }

  // Start monitoring network property changes.
  lib->AddNetworkManagerObserver(this);
  lib->RemoveObserverForAllNetworks(this);
  lib->AddNetworkObserver(network->service_path(), this);
  // Try to start with OTASP immediately if we have received payment recently.
  state_ = lib->HasRecentCellularPlanPayment() ?
               PLAN_ACTIVATION_START_OTASP :
               PLAN_ACTIVATION_START;
  EvaluateCellularNetwork(network);
}

void MobileSetupHandler::RetryOTASP() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(state_ == PLAN_ACTIVATION_DELAY_OTASP);
  StartOTASP();
}

void MobileSetupHandler::ContinueConnecting(int delay) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  chromeos::CellularNetwork* network = GetCellularNetwork(service_path_);
  if (network && network->connecting_or_connected()) {
    EvaluateCellularNetwork(network);
  } else {
    ConnectToNetwork(network, delay);
  }
}

void MobileSetupHandler::SetTransactionStatus(const std::string& status) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // The payment is received, try to reconnect and check the status all over
  // again.
  if (LowerCaseEqualsASCII(status, "ok") &&
      state_ == PLAN_ACTIVATION_SHOWING_PAYMENT) {
    chromeos::NetworkLibrary* lib =
        chromeos::CrosLibrary::Get()->GetNetworkLibrary();
    lib->SignalCellularPlanPayment();
    UMA_HISTOGRAM_COUNTS("Cellular.PaymentReceived", 1);
    StartOTASP();
  } else {
    UMA_HISTOGRAM_COUNTS("Cellular.PaymentFailed", 1);
  }
}

void MobileSetupHandler::StartOTASP() {
  state_ = PLAN_ACTIVATION_START_OTASP;
  chromeos::CellularNetwork* network = GetCellularNetwork();
  if (network &&
      network->connected() &&
      network->activation_state() == chromeos::ACTIVATION_STATE_ACTIVATED) {
    chromeos::CrosLibrary::Get()->GetNetworkLibrary()->
        DisconnectFromNetwork(network);
  } else {
    EvaluateCellularNetwork(network);
  }
}

void MobileSetupHandler::ReconnectTimerFired() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // Permit network connection changes only in reconnecting states.
  if (state_ != PLAN_ACTIVATION_RECONNECTING_OTASP_TRY &&
      state_ != PLAN_ACTIVATION_RECONNECTING &&
      state_ != PLAN_ACTIVATION_RECONNECTING_OTASP)
    return;
  chromeos::CellularNetwork* network = GetCellularNetwork(service_path_);
  if (!network) {
    // No service, try again since this is probably just transient condition.
    LOG(WARNING) << "Service not present at reconnect attempt.";
  }
  EvaluateCellularNetwork(network);
}

void MobileSetupHandler::DisconnectFromNetwork(
    chromeos::CellularNetwork* network) {
  DCHECK(network);
  LOG(INFO) << "Disconnecting from: " << network->service_path();
  chromeos::CrosLibrary::Get()->GetNetworkLibrary()->
      DisconnectFromNetwork(network);
  // Disconnect will force networks to be reevaluated, so
  // we don't want to continue processing on this path anymore.
  evaluating_ = false;
}

bool MobileSetupHandler::NeedsReconnecting(
    chromeos::CellularNetwork* network,
    PlanActivationState* new_state,
    std::string* error_description) {
  DCHECK(network);
  if (!network->failed() && !ConnectionTimeout())
    return false;

  // Try to reconnect again if reconnect failed, or if for some
  // reasons we are still not connected after 45 seconds.
  int max_retries = (state_ == PLAN_ACTIVATION_RECONNECTING_OTASP) ?
                        kMaxConnectionRetryOTASP : kMaxConnectionRetry;
  if (connection_retry_count_ < max_retries) {
    UMA_HISTOGRAM_COUNTS("Cellular.ConnectionRetry", 1);
    ConnectToNetwork(network, kFailedReconnectDelayMS);
    return true;
  }
  // We simply can't connect anymore after all these tries.
  UMA_HISTOGRAM_COUNTS("Cellular.ConnectionFailed", 1);
  *new_state = PLAN_ACTIVATION_ERROR;
  *error_description = GetErrorMessage(kFailedConnectivity);
  return false;
}

bool MobileSetupHandler::ConnectToNetwork(
    chromeos::CellularNetwork* network,
    int delay) {
  if (network && network->connecting_or_connected())
    return true;
  // Permit network connection changes only in reconnecting states.
  if (state_ != PLAN_ACTIVATION_RECONNECTING_OTASP_TRY &&
      state_ != PLAN_ACTIVATION_RECONNECTING &&
      state_ != PLAN_ACTIVATION_RECONNECTING_OTASP)
    return false;
  if (network)
    LOG(INFO) << "Connecting to: " << network->service_path();
  connection_retry_count_++;
  connection_start_time_ = base::Time::Now();
  if (!network) {
    LOG(WARNING) << "Connect failed."
                 << (network ? network->service_path().c_str() : "no service");
    // If we coudn't connect during reconnection phase, try to reconnect
    // with a delay (and try to reconnect if needed).
    scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(),
                                                  delay);
    BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
        NewRunnableMethod(task.get(), &TaskProxy::ContinueConnecting),
        delay);
    return false;
  }
  chromeos::CrosLibrary::Get()->GetNetworkLibrary()->
      ConnectToCellularNetwork(network);
  return true;
}

void MobileSetupHandler::ForceReconnect(
    chromeos::CellularNetwork* network,
    int delay) {
  DCHECK(network);
  UMA_HISTOGRAM_COUNTS("Cellular.ActivationRetry", 1);
  // Reset reconnect metrics.
  connection_retry_count_ = 0;
  connection_start_time_ = base::Time();
  // First, disconnect...
  LOG(INFO) << "Disconnecting from " << network->service_path();
  chromeos::CrosLibrary::Get()->GetNetworkLibrary()->
      DisconnectFromNetwork(network);
  // Check the network state 3s after we disconnect to make sure.
  scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(),
                                                delay);
  BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(task.get(), &TaskProxy::ContinueConnecting),
      delay);
}

bool MobileSetupHandler::ConnectionTimeout() {
  return (base::Time::Now() -
            connection_start_time_).InSeconds() > kConnectionTimeoutSeconds;
}

void MobileSetupHandler::EvaluateCellularNetwork(
    chromeos::CellularNetwork* network) {
  if (!web_ui_)
    return;

  PlanActivationState new_state = state_;
  if (!network) {
    LOG(WARNING) << "Cellular service lost";
    if (state_ == PLAN_ACTIVATION_RECONNECTING_OTASP_TRY ||
        state_ == PLAN_ACTIVATION_RECONNECTING ||
        state_ == PLAN_ACTIVATION_RECONNECTING_OTASP) {
      // This might be the legit case when service is lost after activation.
      // We need to make sure we force reconnection as soon as it shows up.
      LOG(INFO) << "Force service reconnection";
      connection_start_time_ = base::Time();
    }
    return;
  }

  // Prevent this method from being called if it is already on the stack.
  // This might happen on some state transitions (ie. connect, disconnect).
  if (evaluating_)
    return;
  evaluating_ = true;
  std::string error_description;

  LOG(WARNING) << "Cellular:\n  service=" << network->GetStateString().c_str()
      << "\n  ui=" << GetStateDescription(state_)
      << "\n  activation=" << network->GetActivationStateString().c_str()
      << "\n  connectivity="
      << network->GetConnectivityStateString().c_str()
      << "\n  error=" << network->GetErrorString().c_str()
      << "\n  setvice_path=" << network->service_path().c_str();
  switch (state_) {
    case PLAN_ACTIVATION_START: {
      switch (network->activation_state()) {
        case chromeos::ACTIVATION_STATE_ACTIVATED: {
          if (network->failed_or_disconnected()) {
            new_state = PLAN_ACTIVATION_RECONNECTING;
          } else if (network->connected()) {
            if (network->restricted_pool()) {
              new_state = PLAN_ACTIVATION_SHOWING_PAYMENT;
            } else {
              new_state = PLAN_ACTIVATION_DONE;
            }
          }
          break;
        }
        default: {
          if (network->failed_or_disconnected() ||
              network->state() == chromeos::STATE_ACTIVATION_FAILURE) {
            new_state = (network->activation_state() ==
                         chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) ?
                            PLAN_ACTIVATION_TRYING_OTASP :
                            PLAN_ACTIVATION_INITIATING_ACTIVATION;
          } else if (network->connected()) {
            DisconnectFromNetwork(network);
            return;
          }
          break;
        }
      }
      break;
    }
    case PLAN_ACTIVATION_START_OTASP: {
      switch (network->activation_state()) {
        case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED: {
          if (network->failed_or_disconnected()) {
            new_state = PLAN_ACTIVATION_OTASP;
          } else if (network->connected()) {
            DisconnectFromNetwork(network);
            return;
          }
          break;
        }
        case chromeos::ACTIVATION_STATE_ACTIVATED:
          new_state = PLAN_ACTIVATION_RECONNECTING_OTASP;
          break;
        default: {
          LOG(WARNING) << "Unexpected activation state for device "
                       << network->service_path().c_str();
          break;
        }
      }
      break;
    }
    case PLAN_ACTIVATION_DELAY_OTASP:
      // Just ignore any changes until the OTASP retry timer kicks in.
      evaluating_ = false;
      return;
    case PLAN_ACTIVATION_INITIATING_ACTIVATION:
    case PLAN_ACTIVATION_OTASP:
    case PLAN_ACTIVATION_TRYING_OTASP: {
      switch (network->activation_state()) {
        case chromeos::ACTIVATION_STATE_ACTIVATED:
          if (network->failed_or_disconnected()) {
            new_state = GetNextReconnectState(state_);
          } else if (network->connected()) {
            if (network->restricted_pool()) {
              new_state = PLAN_ACTIVATION_SHOWING_PAYMENT;
            } else {
              new_state = PLAN_ACTIVATION_DONE;
            }
          }
          break;
        case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED:
          if (network->connected()) {
            if (network->restricted_pool())
              new_state = PLAN_ACTIVATION_SHOWING_PAYMENT;
          } else {
            new_state = GetNextReconnectState(state_);
          }
          break;
        case chromeos::ACTIVATION_STATE_NOT_ACTIVATED:
        case chromeos::ACTIVATION_STATE_ACTIVATING:
          // Wait in this state until activation state changes.
          break;
        default:
          break;
      }
      break;
    }
    case PLAN_ACTIVATION_RECONNECTING_OTASP_TRY:
    case PLAN_ACTIVATION_RECONNECTING: {
      if (network->connected()) {
        // Make sure other networks are not interfering with our detection of
        // restricted pool.
        DisableOtherNetworks();
        // Wait until the service shows up and gets activated.
        switch (network->activation_state()) {
          case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED:
          case chromeos::ACTIVATION_STATE_ACTIVATED:
            if (network->connectivity_state() ==
                         chromeos::CONN_STATE_NONE) {
              LOG(WARNING) << "No connectivity for device "
                           << network->service_path().c_str();
              // If we are connected but there is no connectivity at all,
              // restart the whole process again.
              if (activation_attempt_ < kMaxActivationAttempt) {
                activation_attempt_++;
                LOG(WARNING) << "Reconnect attempt #"
                             << activation_attempt_;
                ForceReconnect(network, kFailedReconnectDelayMS);
                evaluating_ = false;
                return;
              } else {
                new_state = PLAN_ACTIVATION_ERROR;
                UMA_HISTOGRAM_COUNTS("Cellular.ActivationRetryFailure", 1);
                error_description = GetErrorMessage(kFailedConnectivity);
              }
            } else if (network->connectivity_state() ==
                           chromeos::CONN_STATE_RESTRICTED) {
              // If we have already received payment, don't show the payment
              // page again. We should try to reconnect after some time instead.
              new_state = PLAN_ACTIVATION_SHOWING_PAYMENT;
            } else if (network->activation_state() ==
                           chromeos::ACTIVATION_STATE_ACTIVATED) {
              new_state = PLAN_ACTIVATION_DONE;
            }
            break;
          default:
            break;
        }
      } else if (NeedsReconnecting(network, &new_state, &error_description)) {
        evaluating_ = false;
        return;
      }
      break;
    }
    case PLAN_ACTIVATION_RECONNECTING_OTASP: {
      if (network->connected()) {
        // Make sure other networks are not interfering with our detection of
        // restricted pool.
        DisableOtherNetworks();
        // Wait until the service shows up and gets activated.
        switch (network->activation_state()) {
          case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED:
          case chromeos::ACTIVATION_STATE_ACTIVATED:
            if (network->connectivity_state() == chromeos::CONN_STATE_NONE ||
                network->connectivity_state() ==
                    chromeos::CONN_STATE_RESTRICTED) {
              LOG(WARNING) << "Still no connectivity after OTASP for device "
                           << network->service_path().c_str();
              // If we have already received payment, don't show the payment
              // page again. We should try to reconnect after some time instead.
              if (reconnect_wait_count_ < kMaxReconnectAttemptOTASP) {
                reconnect_wait_count_++;
                LOG(WARNING) << "OTASP reconnect attempt #"
                             << reconnect_wait_count_;
                ForceReconnect(network, kPostPaymentReconnectDelayMS);
                evaluating_ = false;
                return;
              } else {
                new_state = PLAN_ACTIVATION_ERROR;
                UMA_HISTOGRAM_COUNTS("Cellular.PostPaymentConnectFailure", 1);
                error_description = GetErrorMessage(kFailedConnectivity);
              }
            } else if (network->connectivity_state() ==
                           chromeos::CONN_STATE_UNRESTRICTED) {
              new_state = PLAN_ACTIVATION_DONE;
            }
            break;
          default:
            break;
        }
      } else if (NeedsReconnecting(network, &new_state, &error_description)) {
        evaluating_ = false;
        return;
      }
      break;
    }
    case PLAN_ACTIVATION_PAGE_LOADING:
      break;
    // Just ignore all signals until the site confirms payment.
    case PLAN_ACTIVATION_SHOWING_PAYMENT:
      // Activation completed/failed, ignore network changes.
    case PLAN_ACTIVATION_DONE:
    case PLAN_ACTIVATION_ERROR:
      break;
  }

  if (new_state != PLAN_ACTIVATION_ERROR &&
      GotActivationError(network, &error_description)) {
    // Check for this special case when we try to do activate partially
    // activated device. If that attempt failed, try to disconnect to clear the
    // state and reconnect again.
    if ((network->activation_state() ==
            chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED ||
        network->activation_state() == chromeos::ACTIVATION_STATE_ACTIVATING) &&
        (network->error() == chromeos::ERROR_NO_ERROR ||
            network->error() == chromeos::ERROR_OTASP_FAILED) &&
        network->state() == chromeos::STATE_ACTIVATION_FAILURE) {
      LOG(WARNING) << "Activation failure detected "
                   << network->service_path().c_str();
      switch (state_) {
        case PLAN_ACTIVATION_OTASP:
        case PLAN_ACTIVATION_RECONNECTING_OTASP:
          new_state = PLAN_ACTIVATION_DELAY_OTASP;
          break;
        case PLAN_ACTIVATION_TRYING_OTASP:
          new_state = PLAN_ACTIVATION_RECONNECTING_OTASP_TRY;
          break;
        case PLAN_ACTIVATION_INITIATING_ACTIVATION:
          new_state = PLAN_ACTIVATION_RECONNECTING;
          break;
        case PLAN_ACTIVATION_START:
          // We are just starting, so this must be previous activation attempt
          // failure.
          new_state = PLAN_ACTIVATION_TRYING_OTASP;
          break;
        case PLAN_ACTIVATION_DELAY_OTASP:
        case PLAN_ACTIVATION_RECONNECTING_OTASP_TRY:
        case PLAN_ACTIVATION_RECONNECTING:
          new_state = state_;
          break;
        default:
          new_state = PLAN_ACTIVATION_ERROR;
          break;
      }
    } else {
      LOG(WARNING) << "Unexpected activation failure for "
                   << network->service_path().c_str();
      new_state = PLAN_ACTIVATION_ERROR;
    }
  }

  if (new_state == PLAN_ACTIVATION_ERROR && !error_description.length())
    error_description = GetErrorMessage(kErrorDefault);

  ChangeState(network, new_state, error_description);
  evaluating_ = false;
}

MobileSetupHandler::PlanActivationState
    MobileSetupHandler::GetNextReconnectState(
        MobileSetupHandler::PlanActivationState state) {
  switch (state) {
    case PLAN_ACTIVATION_INITIATING_ACTIVATION:
      return PLAN_ACTIVATION_RECONNECTING;
    case PLAN_ACTIVATION_OTASP:
      return PLAN_ACTIVATION_RECONNECTING_OTASP;
    case PLAN_ACTIVATION_TRYING_OTASP:
      return PLAN_ACTIVATION_RECONNECTING_OTASP_TRY;
    default:
      return PLAN_ACTIVATION_RECONNECTING;
  }
}

// Debugging helper function, will take it out at the end.
const char* MobileSetupHandler::GetStateDescription(
    PlanActivationState state) {
  switch (state) {
    case PLAN_ACTIVATION_PAGE_LOADING:
      return "PAGE_LOADING";
    case PLAN_ACTIVATION_START:
      return "ACTIVATION_START";
    case PLAN_ACTIVATION_TRYING_OTASP:
      return "TRYING_OTASP";
    case PLAN_ACTIVATION_RECONNECTING_OTASP_TRY:
      return "RECONNECTING_OTASP_TRY";
    case PLAN_ACTIVATION_INITIATING_ACTIVATION:
      return "INITIATING_ACTIVATION";
    case PLAN_ACTIVATION_RECONNECTING:
      return "RECONNECTING";
    case PLAN_ACTIVATION_SHOWING_PAYMENT:
      return "SHOWING_PAYMENT";
    case PLAN_ACTIVATION_START_OTASP:
      return "START_OTASP";
    case PLAN_ACTIVATION_DELAY_OTASP:
      return "DELAY_OTASP";
    case PLAN_ACTIVATION_OTASP:
      return "OTASP";
    case PLAN_ACTIVATION_RECONNECTING_OTASP:
      return "RECONNECTING_OTASP";
    case PLAN_ACTIVATION_DONE:
      return "DONE";
    case PLAN_ACTIVATION_ERROR:
      return "ERROR";
  }
  return "UNKNOWN";
}


void MobileSetupHandler::CompleteActivation(
    chromeos::CellularNetwork* network) {
  // Remove observers, we are done with this page.
  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
      GetNetworkLibrary();
  lib->RemoveNetworkManagerObserver(this);
  lib->RemoveObserverForAllNetworks(this);
  if (lib->IsLocked())
    lib->Unlock();
  // If we have successfully activated the connection, set autoconnect flag.
  if (network)
    network->SetAutoConnect(true);
  // Reactivate other types of connections if we have
  // shut them down previously.
  ReEnableOtherConnections();
}

void MobileSetupHandler::UpdatePage(
    chromeos::CellularNetwork* network,
    const std::string& error_description) {
  DictionaryValue device_dict;
  if (network)
    GetDeviceInfo(network, &device_dict);
  device_dict.SetInteger("state", state_);
  if (error_description.length())
    device_dict.SetString("error", error_description);
  web_ui_->CallJavascriptFunction(
      kJsDeviceStatusChangedHandler, device_dict);
}


void MobileSetupHandler::ChangeState(chromeos::CellularNetwork* network,
                                     PlanActivationState new_state,
                                     const std::string& error_description) {
  static bool first_time = true;
  if (state_ == new_state && !first_time)
    return;
  LOG(WARNING) << "Activation state flip old = "
      << GetStateDescription(state_)
      << ", new = " << GetStateDescription(new_state);
  first_time = false;

  // Pick action that should happen on leaving the old state.
  switch (state_) {
    case PLAN_ACTIVATION_RECONNECTING_OTASP_TRY:
    case PLAN_ACTIVATION_RECONNECTING:
    case PLAN_ACTIVATION_RECONNECTING_OTASP:
      if (reconnect_timer_.IsRunning()) {
        reconnect_timer_.Stop();
      }
      break;
    default:
      break;
  }
  state_ = new_state;

  // Signal to JS layer that the state is changing.
  UpdatePage(network, error_description);

  // Pick action that should happen on entering the new state.
  switch (new_state) {
    case PLAN_ACTIVATION_START:
      break;
    case PLAN_ACTIVATION_DELAY_OTASP: {
      UMA_HISTOGRAM_COUNTS("Cellular.RetryOTASP", 1);
      scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), 0);
      BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
          NewRunnableMethod(task.get(), &TaskProxy::RetryOTASP),
          kOTASPRetryDelay);
      break;
    }
    case PLAN_ACTIVATION_INITIATING_ACTIVATION:
    case PLAN_ACTIVATION_TRYING_OTASP:
    case PLAN_ACTIVATION_OTASP:
      DCHECK(network);
      LOG(WARNING) << "Activating service " << network->service_path().c_str();
      UMA_HISTOGRAM_COUNTS("Cellular.ActivationTry", 1);
      if (!network->StartActivation()) {
        UMA_HISTOGRAM_COUNTS("Cellular.ActivationFailure", 1);
        if (new_state == PLAN_ACTIVATION_OTASP) {
          ChangeState(network, PLAN_ACTIVATION_DELAY_OTASP, std::string());
        } else {
          ChangeState(network, PLAN_ACTIVATION_ERROR,
                      GetErrorMessage(kFailedConnectivity));
        }
      }
      break;
    case PLAN_ACTIVATION_RECONNECTING_OTASP_TRY:
    case PLAN_ACTIVATION_RECONNECTING:
    case PLAN_ACTIVATION_RECONNECTING_OTASP: {
      // Start reconnect timer. This will ensure that we are not left in
      // limbo by the network library.
      if (!reconnect_timer_.IsRunning()) {
        reconnect_timer_.Start(
            base::TimeDelta::FromMilliseconds(kReconnectTimerDelayMS),
            this, &MobileSetupHandler::ReconnectTimerFired);
      }
      // Reset connection metrics and try to connect.
      reconnect_wait_count_ = 0;
      connection_retry_count_ = 0;
      connection_start_time_ = base::Time::Now();
      ConnectToNetwork(network, kReconnectDelayMS);
      break;
    }
    case PLAN_ACTIVATION_PAGE_LOADING:
      return;
    case PLAN_ACTIVATION_SHOWING_PAYMENT:
      // Fix for fix SSL for the walled gardens where cert chain verification
      // might not work.
      break;
    case PLAN_ACTIVATION_DONE:
      DCHECK(network);
      CompleteActivation(network);
      UMA_HISTOGRAM_COUNTS("Cellular.MobileSetupSucceeded", 1);
      break;
    case PLAN_ACTIVATION_ERROR:
      CompleteActivation(NULL);
      UMA_HISTOGRAM_COUNTS("Cellular.PlanFailed", 1);
      break;
    default:
      break;
  }
}

void MobileSetupHandler::ReEnableOtherConnections() {
  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
      GetNetworkLibrary();
  if (reenable_ethernet_) {
    reenable_ethernet_ = false;
    lib->EnableEthernetNetworkDevice(true);
  }
  if (reenable_wifi_) {
    reenable_wifi_ = false;
    lib->EnableWifiNetworkDevice(true);
  }

  PrefService* prefs = g_browser_process->local_state();
  if (reenable_cert_check_) {
    prefs->SetBoolean(prefs::kCertRevocationCheckingEnabled,
                      true);
    reenable_cert_check_ = false;
  }
}

void MobileSetupHandler::SetupActivationProcess(
    chromeos::CellularNetwork* network) {
  if (!network)
    return;

  // Disable SSL cert checks since we will be doing this in
  // restricted pool.
  PrefService* prefs = g_browser_process->local_state();
  if (!reenable_cert_check_ &&
      prefs->GetBoolean(
          prefs::kCertRevocationCheckingEnabled)) {
    reenable_cert_check_ = true;
    prefs->SetBoolean(prefs::kCertRevocationCheckingEnabled, false);
  }

  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
      GetNetworkLibrary();
  // Disable autoconnect to cellular network.
  network->SetAutoConnect(false);

  // Prevent any other network interference.
  DisableOtherNetworks();
  lib->Lock();
}

void MobileSetupHandler::DisableOtherNetworks() {
  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
      GetNetworkLibrary();
  // Disable ethernet and wifi.
  if (lib->ethernet_enabled()) {
    reenable_ethernet_ = true;
    lib->EnableEthernetNetworkDevice(false);
  }
  if (lib->wifi_enabled()) {
    reenable_wifi_ = true;
    lib->EnableWifiNetworkDevice(false);
  }
}

bool MobileSetupHandler::GotActivationError(
    chromeos::CellularNetwork* network, std::string* error) {
  DCHECK(network);
  bool got_error = false;
  const char* error_code = kErrorDefault;

  // This is the magic for detection of errors in during activation process.
  if (network->state() == chromeos::STATE_FAILURE &&
      network->error() == chromeos::ERROR_AAA_FAILED) {
    if (network->activation_state() ==
            chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) {
      error_code = kErrorBadConnectionPartial;
    } else if (network->activation_state() ==
            chromeos::ACTIVATION_STATE_ACTIVATED) {
      if (network->roaming_state() == chromeos::ROAMING_STATE_HOME) {
        error_code = kErrorBadConnectionActivated;
      } else if (network->roaming_state() == chromeos::ROAMING_STATE_ROAMING) {
        error_code = kErrorRoamingOnConnection;
      }
    }
    got_error = true;
  } else if (network->state() == chromeos::STATE_ACTIVATION_FAILURE) {
    if (network->error() == chromeos::ERROR_NEED_EVDO) {
      if (network->activation_state() ==
              chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED)
        error_code = kErrorNoEVDO;
    } else if (network->error() == chromeos::ERROR_NEED_HOME_NETWORK) {
      if (network->activation_state() ==
              chromeos::ACTIVATION_STATE_NOT_ACTIVATED) {
        error_code = kErrorRoamingActivation;
      } else if (network->activation_state() ==
                    chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) {
        error_code = kErrorRoamingPartiallyActivated;
      }
    }
    got_error = true;
  }

  if (got_error)
    *error = GetErrorMessage(error_code);

  return got_error;
}

void MobileSetupHandler::GetDeviceInfo(chromeos::CellularNetwork* network,
                                       DictionaryValue* value) {
  DCHECK(network);
  chromeos::NetworkLibrary* cros =
      chromeos::CrosLibrary::Get()->GetNetworkLibrary();
  if (!cros)
    return;
  value->SetString("carrier", network->name());
  value->SetString("payment_url", network->payment_url());
  const chromeos::NetworkDevice* device =
      cros->FindNetworkDeviceByPath(network->device_path());
  if (device) {
    value->SetString("MEID", device->meid());
    value->SetString("IMEI", device->imei());
    value->SetString("MDN", device->mdn());
  }
}

std::string MobileSetupHandler::GetErrorMessage(const std::string& code) {
  if (!cellular_config_.get())
    return "";
  return cellular_config_->GetErrorMessage(code);
}

void MobileSetupHandler::LoadCellularConfig() {
  static bool config_loaded = false;
  if (config_loaded)
    return;
  config_loaded = true;
  // Load partner customization startup manifest if it is available.
  FilePath config_path(kCellularConfigPath);
  bool config_exists = false;
  {
    // Reading config file causes us to do blocking IO on UI thread.
    // Temporarily allow it until we fix http://crosbug.com/11535
    base::ThreadRestrictions::ScopedAllowIO allow_io;
    config_exists = file_util::PathExists(config_path);
  }
  if (config_exists) {
    scoped_ptr<CellularConfigDocument> config(new CellularConfigDocument());
    bool config_loaded = config->LoadFromFile(config_path);
    if (config_loaded) {
      LOG(INFO) << "Cellular config file loaded: " << kCellularConfigPath;
      // lock
      cellular_config_.reset(config.release());
    } else {
      LOG(ERROR) << "Error loading cellular config file: " <<
          kCellularConfigPath;
    }
  }
}


////////////////////////////////////////////////////////////////////////////////
//
// MobileSetupUI
//
////////////////////////////////////////////////////////////////////////////////

MobileSetupUI::MobileSetupUI(TabContents* contents) : WebUI(contents) {
  chromeos::CellularNetwork* network = GetCellularNetwork();
  std::string service_path = network ? network->service_path() : std::string();
  MobileSetupHandler* handler = new MobileSetupHandler(service_path);
  AddMessageHandler((handler)->Attach(this));
  handler->Init(contents);
  MobileSetupUIHTMLSource* html_source =
      new MobileSetupUIHTMLSource(service_path);

  // Set up the chrome://mobilesetup/ source.
  contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
}