Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/webui/chromeos/sim_unlock_ui.h"
      6 
      7 #include <string>
      8 
      9 #include "base/logging.h"
     10 #include "base/memory/weak_ptr.h"
     11 #include "base/string_piece.h"
     12 #include "base/values.h"
     13 #include "chrome/browser/chromeos/cros/cros_library.h"
     14 #include "chrome/browser/chromeos/cros/network_library.h"
     15 #include "chrome/browser/chromeos/sim_dialog_delegate.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/ui/webui/chrome_url_data_manager.h"
     18 #include "chrome/common/jstemplate_builder.h"
     19 #include "chrome/common/url_constants.h"
     20 #include "content/browser/browser_thread.h"
     21 #include "content/browser/tab_contents/tab_contents.h"
     22 #include "content/common/notification_service.h"
     23 #include "content/common/notification_type.h"
     24 #include "grit/browser_resources.h"
     25 #include "grit/generated_resources.h"
     26 #include "ui/base/l10n/l10n_util.h"
     27 #include "ui/base/resource/resource_bundle.h"
     28 
     29 namespace {
     30 
     31 // JS API callbacks names.
     32 const char kJsApiCancel[] = "cancel";
     33 const char kJsApiChangePinCode[] = "changePinCode";
     34 const char kJsApiEnterPinCode[] = "enterPinCode";
     35 const char kJsApiEnterPukCode[] = "enterPukCode";
     36 const char kJsApiProceedToPukInput[] = "proceedToPukInput";
     37 const char kJsApiSimStatusInitialize[] = "simStatusInitialize";
     38 
     39 // Page JS API function names.
     40 const char kJsApiSimStatusChanged[] = "mobile.SimUnlock.simStateChanged";
     41 
     42 // SIM state variables which are passed to the page.
     43 const char kState[] = "state";
     44 const char kError[] = "error";
     45 const char kTriesLeft[] = "tries";
     46 
     47 // Error constants, passed to the page.
     48 const char kErrorPin[] = "incorrectPin";
     49 const char kErrorPuk[] = "incorrectPuk";
     50 const char kErrorOk[] = "ok";
     51 
     52 const chromeos::NetworkDevice* GetCellularDevice() {
     53   chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
     54       GetNetworkLibrary();
     55   CHECK(lib);
     56   return lib->FindCellularDevice();
     57 }
     58 
     59 }  // namespace
     60 
     61 namespace chromeos {
     62 
     63 class SimUnlockUIHTMLSource : public ChromeURLDataManager::DataSource {
     64  public:
     65   SimUnlockUIHTMLSource();
     66 
     67   // Called when the network layer has requested a resource underneath
     68   // the path we registered.
     69   virtual void StartDataRequest(const std::string& path,
     70                                 bool is_incognito,
     71                                 int request_id);
     72   virtual std::string GetMimeType(const std::string&) const {
     73     return "text/html";
     74   }
     75 
     76  private:
     77   virtual ~SimUnlockUIHTMLSource() {}
     78 
     79   std::string service_path_;
     80   DISALLOW_COPY_AND_ASSIGN(SimUnlockUIHTMLSource);
     81 };
     82 
     83 // The handler for Javascript messages related to the "sim-unlock" view.
     84 class SimUnlockHandler : public WebUIMessageHandler,
     85                          public base::SupportsWeakPtr<SimUnlockHandler>,
     86                          public NetworkLibrary::NetworkDeviceObserver,
     87                          public NetworkLibrary::PinOperationObserver {
     88  public:
     89   SimUnlockHandler();
     90   virtual ~SimUnlockHandler();
     91 
     92   // Init work after Attach.
     93   void Init(TabContents* contents);
     94 
     95   // WebUIMessageHandler implementation.
     96   virtual WebUIMessageHandler* Attach(WebUI* web_ui);
     97   virtual void RegisterMessages();
     98 
     99   // NetworkLibrary::NetworkDeviceObserver implementation.
    100   virtual void OnNetworkDeviceChanged(NetworkLibrary* cros,
    101                                       const NetworkDevice* device);
    102 
    103   // NetworkLibrary::PinOperationObserver implementation.
    104   virtual void OnPinOperationCompleted(NetworkLibrary* cros,
    105                                        PinOperationError error);
    106 
    107  private:
    108   // Should keep this state enum in sync with similar one in JS code.
    109   // SIM_NOT_LOCKED_ASK_PIN - SIM card is not locked but we ask user
    110   // for PIN input because PinRequired preference change was requested.
    111   // SIM_NOT_LOCKED_CHANGE_PIN - SIM card is not locked, ask user for old PIN
    112   // and new PIN to change it.
    113   typedef enum SimUnlockState {
    114     SIM_UNLOCK_LOADING           = -1,
    115     SIM_ABSENT_NOT_LOCKED        =  0,
    116     SIM_NOT_LOCKED_ASK_PIN       =  1,
    117     SIM_NOT_LOCKED_CHANGE_PIN    =  2,
    118     SIM_LOCKED_PIN               =  3,
    119     SIM_LOCKED_NO_PIN_TRIES_LEFT =  4,
    120     SIM_LOCKED_PUK               =  5,
    121     SIM_LOCKED_NO_PUK_TRIES_LEFT =  6,
    122     SIM_DISABLED                 =  7,
    123   } SimUnlockState;
    124 
    125   // Type of the SIM unlock code.
    126   typedef enum SimUnlockCode {
    127     CODE_PIN,
    128     CODE_PUK,
    129   } SimUnlockCode;
    130 
    131   class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> {
    132    public:
    133     explicit TaskProxy(const base::WeakPtr<SimUnlockHandler>& handler)
    134         : handler_(handler) {
    135     }
    136 
    137     TaskProxy(const base::WeakPtr<SimUnlockHandler>& handler,
    138               const std::string& code,
    139               SimUnlockCode code_type)
    140         : handler_(handler),
    141           code_(code),
    142           code_type_(code_type) {
    143     }
    144 
    145     void HandleCancel() {
    146       if (handler_)
    147         handler_->CancelDialog();
    148     }
    149 
    150     void HandleEnterCode() {
    151       if (handler_)
    152         handler_->EnterCode(code_, code_type_);
    153     }
    154 
    155     void HandleInitialize() {
    156       if (handler_)
    157         handler_->InitializeSimStatus();
    158     }
    159 
    160     void HandleProceedToPukInput() {
    161       if (handler_)
    162         handler_->ProceedToPukInput();
    163     }
    164 
    165    private:
    166     base::WeakPtr<SimUnlockHandler> handler_;
    167 
    168     // Pending code input (PIN/PUK).
    169     std::string code_;
    170 
    171     // Pending code type.
    172     SimUnlockCode code_type_;
    173 
    174     DISALLOW_COPY_AND_ASSIGN(TaskProxy);
    175   };
    176 
    177   // Processing for the cases when dialog was cancelled.
    178   void CancelDialog();
    179 
    180   // Pass PIN/PUK code to flimflam and check status.
    181   void EnterCode(const std::string& code, SimUnlockCode code_type);
    182 
    183   // Single handler for PIN/PUK code operations.
    184   void HandleEnterCode(SimUnlockCode code_type, const std::string& code);
    185 
    186   // Handlers for JS WebUI messages.
    187   void HandleCancel(const ListValue* args);
    188   void HandleChangePinCode(const ListValue* args);
    189   void HandleEnterPinCode(const ListValue* args);
    190   void HandleEnterPukCode(const ListValue* args);
    191   void HandleProceedToPukInput(const ListValue* args);
    192   void HandleSimStatusInitialize(const ListValue* args);
    193 
    194   // Initialize current SIM card status, passes that to page.
    195   void InitializeSimStatus();
    196 
    197   // Notifies SIM Security tab handler that RequirePin preference change
    198   // has been ended (either updated or cancelled).
    199   void NotifyOnRequirePinChangeEnded(bool new_value);
    200 
    201   // Notifies observers that the EnterPin or EnterPuk dialog has been
    202   // completed (either cancelled or with entry of PIN/PUK).
    203   void NotifyOnEnterPinEnded(bool cancelled);
    204 
    205   // Checks whether SIM card is in PUK locked state and proceeds to PUK input.
    206   void ProceedToPukInput();
    207 
    208   // Processes current SIM card state and update internal state/page.
    209   void ProcessSimCardState(const chromeos::NetworkDevice* cellular);
    210 
    211   // Updates page with the current state/SIM card info/error.
    212   void UpdatePage(const chromeos::NetworkDevice* cellular,
    213                   const std::string& error_msg);
    214 
    215   TabContents* tab_contents_;
    216   SimUnlockState state_;
    217 
    218   // Path of the Cellular device that we monitor property updates from.
    219   std::string cellular_device_path_;
    220 
    221   // Type of the dialog: generic unlock/change pin/change PinRequire.
    222   SimDialogDelegate::SimDialogMode dialog_mode_;
    223 
    224   // New PIN value for the case when we unblock SIM card or change PIN.
    225   std::string new_pin_;
    226 
    227   DISALLOW_COPY_AND_ASSIGN(SimUnlockHandler);
    228 };
    229 
    230 // SimUnlockUIHTMLSource -------------------------------------------------------
    231 
    232 SimUnlockUIHTMLSource::SimUnlockUIHTMLSource()
    233     : DataSource(chrome::kChromeUISimUnlockHost, MessageLoop::current()) {
    234 }
    235 
    236 void SimUnlockUIHTMLSource::StartDataRequest(const std::string& path,
    237                                              bool is_incognito,
    238                                              int request_id) {
    239   DictionaryValue strings;
    240   strings.SetString("title",
    241       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PIN_TITLE));
    242   strings.SetString("ok", l10n_util::GetStringUTF16(IDS_OK));
    243   strings.SetString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL));
    244   strings.SetString("enterPinTitle",
    245       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PIN_TITLE));
    246   strings.SetString("enterPinMessage",
    247       l10n_util::GetStringUTF16(IDS_SIM_ENTER_PIN_MESSAGE));
    248   strings.SetString("enterPinTriesMessage",
    249       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PIN_TRIES_MESSAGE));
    250   strings.SetString("incorrectPinTriesMessage",
    251       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_INCORRECT_PIN_TRIES_MESSAGE));
    252   strings.SetString("incorrectPinTitle",
    253       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_INCORRECT_PIN_TITLE));
    254   // TODO(nkostylev): Pass carrier name if we know that.
    255   strings.SetString("noPinTriesLeft", l10n_util::GetStringFUTF16(
    256       IDS_SIM_UNLOCK_NO_PIN_TRIES_LEFT_MESSAGE,
    257       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_DEFAULT_CARRIER)));
    258   strings.SetString("enterPukButton",
    259       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PUK_BUTTON));
    260   strings.SetString("enterPukTitle",
    261       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PUK_TITLE));
    262   strings.SetString("enterPukWarning",
    263       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PUK_WARNING));
    264   // TODO(nkostylev): Pass carrier name if we know that.
    265   strings.SetString("enterPukMessage", l10n_util::GetStringFUTF16(
    266       IDS_SIM_UNLOCK_ENTER_PUK_MESSAGE,
    267       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_DEFAULT_CARRIER)));
    268   strings.SetString("choosePinTitle",
    269       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_CHOOSE_PIN_TITLE));
    270   strings.SetString("choosePinMessage",
    271       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_CHOOSE_PIN_MESSAGE));
    272   strings.SetString("newPin", l10n_util::GetStringUTF16(
    273       IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_NEW_PIN));
    274   strings.SetString("retypeNewPin", l10n_util::GetStringUTF16(
    275       IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_RETYPE_PIN));
    276   strings.SetString("pinsDontMatchMessage", l10n_util::GetStringUTF16(
    277       IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_PINS_DONT_MATCH_ERROR));
    278   strings.SetString("noPukTriesLeft",
    279       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_NO_PUK_TRIES_LEFT_MESSAGE));
    280   strings.SetString("simDisabledTitle",
    281       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_SIM_DISABLED_TITLE));
    282   strings.SetString("simDisabledMessage",
    283       l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_SIM_DISABLED_MESSAGE));
    284 
    285   strings.SetString("changePinTitle", l10n_util::GetStringUTF16(
    286       IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_TITLE));
    287   strings.SetString("changePinMessage", l10n_util::GetStringUTF16(
    288       IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_MESSAGE));
    289   strings.SetString("oldPin", l10n_util::GetStringUTF16(
    290       IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_OLD_PIN));
    291 
    292   SetFontAndTextDirection(&strings);
    293 
    294   static const base::StringPiece html(
    295       ResourceBundle::GetSharedInstance().GetRawDataResource(
    296           IDR_SIM_UNLOCK_HTML));
    297 
    298   const std::string& full_html = jstemplate_builder::GetI18nTemplateHtml(
    299       html, &strings);
    300 
    301   scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes());
    302   html_bytes->data.resize(full_html.size());
    303   std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());
    304 
    305   SendResponse(request_id, html_bytes);
    306 }
    307 
    308 // SimUnlockHandler ------------------------------------------------------------
    309 
    310 SimUnlockHandler::SimUnlockHandler()
    311     : tab_contents_(NULL),
    312       state_(SIM_UNLOCK_LOADING),
    313       dialog_mode_(SimDialogDelegate::SIM_DIALOG_UNLOCK) {
    314   const chromeos::NetworkDevice* cellular = GetCellularDevice();
    315   // One could just call us directly via chrome://sim-unlock.
    316   if (cellular) {
    317     cellular_device_path_ = cellular->device_path();
    318     CrosLibrary::Get()->GetNetworkLibrary()->AddNetworkDeviceObserver(
    319         cellular_device_path_, this);
    320     CrosLibrary::Get()->GetNetworkLibrary()->AddPinOperationObserver(this);
    321   }
    322 }
    323 
    324 SimUnlockHandler::~SimUnlockHandler() {
    325   if (!cellular_device_path_.empty()) {
    326     CrosLibrary::Get()->GetNetworkLibrary()->RemoveNetworkDeviceObserver(
    327         cellular_device_path_, this);
    328     CrosLibrary::Get()->GetNetworkLibrary()->RemovePinOperationObserver(this);
    329   }
    330 }
    331 
    332 WebUIMessageHandler* SimUnlockHandler::Attach(WebUI* web_ui) {
    333   return WebUIMessageHandler::Attach(web_ui);
    334 }
    335 
    336 void SimUnlockHandler::Init(TabContents* contents) {
    337   tab_contents_ = contents;
    338 }
    339 
    340 void SimUnlockHandler::RegisterMessages() {
    341   web_ui_->RegisterMessageCallback(kJsApiCancel,
    342         NewCallback(this, &SimUnlockHandler::HandleCancel));
    343   web_ui_->RegisterMessageCallback(kJsApiChangePinCode,
    344       NewCallback(this, &SimUnlockHandler::HandleChangePinCode));
    345   web_ui_->RegisterMessageCallback(kJsApiEnterPinCode,
    346       NewCallback(this, &SimUnlockHandler::HandleEnterPinCode));
    347   web_ui_->RegisterMessageCallback(kJsApiEnterPukCode,
    348       NewCallback(this, &SimUnlockHandler::HandleEnterPukCode));
    349   web_ui_->RegisterMessageCallback(kJsApiProceedToPukInput,
    350       NewCallback(this, &SimUnlockHandler::HandleProceedToPukInput));
    351   web_ui_->RegisterMessageCallback(kJsApiSimStatusInitialize,
    352       NewCallback(this, &SimUnlockHandler::HandleSimStatusInitialize));
    353 }
    354 
    355 void SimUnlockHandler::OnNetworkDeviceChanged(NetworkLibrary* cros,
    356                                               const NetworkDevice* device) {
    357   chromeos::SIMLockState lock_state = device->sim_lock_state();
    358   int retries_left = device->sim_retries_left();
    359   VLOG(1) << "OnNetworkDeviceChanged, lock: " << lock_state
    360           << ", retries: " << retries_left;
    361   ProcessSimCardState(GetCellularDevice());
    362 }
    363 
    364 void SimUnlockHandler::OnPinOperationCompleted(NetworkLibrary* cros,
    365                                                PinOperationError error) {
    366   DCHECK(cros);
    367   const NetworkDevice* cellular = cros->FindCellularDevice();
    368   DCHECK(cellular);
    369   VLOG(1) << "OnPinOperationCompleted, error: " << error;
    370   if (state_ == SIM_NOT_LOCKED_ASK_PIN && error == PIN_ERROR_NONE) {
    371     CHECK(dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON ||
    372           dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_OFF);
    373     // Async change RequirePin operation has finished OK.
    374     NotifyOnRequirePinChangeEnded(
    375         dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON);
    376     // Dialog will close itself.
    377     state_ = SIM_ABSENT_NOT_LOCKED;
    378   } else if (state_ == SIM_NOT_LOCKED_CHANGE_PIN && error == PIN_ERROR_NONE) {
    379     CHECK(dialog_mode_ == SimDialogDelegate::SIM_DIALOG_CHANGE_PIN);
    380     // Dialog will close itself.
    381     state_ = SIM_ABSENT_NOT_LOCKED;
    382   }
    383   // If previous EnterPIN was last PIN attempt and SIMLock state was already
    384   // processed by OnNetworkDeviceChanged, let dialog stay on
    385   // NO_PIN_RETRIES_LEFT step.
    386   if (!(state_ == SIM_LOCKED_NO_PIN_TRIES_LEFT && error == PIN_ERROR_BLOCKED))
    387     ProcessSimCardState(cellular);
    388   if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_UNLOCK &&
    389       state_ == SIM_ABSENT_NOT_LOCKED)
    390     NotifyOnEnterPinEnded(false);
    391 }
    392 
    393 void SimUnlockHandler::CancelDialog() {
    394   if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON ||
    395       dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_OFF) {
    396     // When async change RequirePin operation is performed,
    397     // dialog UI controls such as Cancel button are disabled.
    398     // If dialog was cancelled that means RequirePin preference hasn't been
    399     // changed and is not in process of changing at the moment.
    400     NotifyOnRequirePinChangeEnded(
    401         !(dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON));
    402   } else if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_UNLOCK) {
    403     NotifyOnEnterPinEnded(true);
    404   }
    405 }
    406 
    407 void SimUnlockHandler::EnterCode(const std::string& code,
    408                                  SimUnlockCode code_type) {
    409   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    410   NetworkLibrary* lib = chromeos::CrosLibrary::Get()->GetNetworkLibrary();
    411   CHECK(lib);
    412 
    413   const NetworkDevice* cellular = GetCellularDevice();
    414   chromeos::SIMLockState lock_state = cellular->sim_lock_state();
    415 
    416   switch (code_type) {
    417     case CODE_PIN:
    418       if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON ||
    419           dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_OFF) {
    420         if (lock_state != chromeos::SIM_UNLOCKED) {
    421           // If SIM is locked/absent, change RequirePin UI is not accessible.
    422           NOTREACHED() <<
    423               "Changing RequirePin pref on locked / uninitialized SIM.";
    424         }
    425         lib->ChangeRequirePin(
    426             dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON,
    427             code);
    428       } else if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_CHANGE_PIN) {
    429         if (lock_state != chromeos::SIM_UNLOCKED) {
    430           // If SIM is locked/absent, changing PIN UI is not accessible.
    431           NOTREACHED() << "Changing PIN on locked / uninitialized SIM.";
    432         }
    433         lib->ChangePin(code, new_pin_);
    434       } else {
    435         lib->EnterPin(code);
    436       }
    437       break;
    438     case CODE_PUK:
    439       DCHECK(!new_pin_.empty());
    440       lib->UnblockPin(code, new_pin_);
    441       break;
    442   }
    443 }
    444 
    445 void SimUnlockHandler::NotifyOnEnterPinEnded(bool cancelled) {
    446   NotificationService::current()->Notify(
    447       NotificationType::ENTER_PIN_ENDED,
    448       NotificationService::AllSources(),
    449       Details<bool>(&cancelled));
    450 }
    451 
    452 void SimUnlockHandler::NotifyOnRequirePinChangeEnded(bool new_value) {
    453   NotificationService::current()->Notify(
    454       NotificationType::REQUIRE_PIN_SETTING_CHANGE_ENDED,
    455       NotificationService::AllSources(),
    456       Details<bool>(&new_value));
    457 }
    458 
    459 void SimUnlockHandler::HandleCancel(const ListValue* args) {
    460   const size_t kEnterCodeParamCount = 0;
    461   if (args->GetSize() != kEnterCodeParamCount) {
    462     NOTREACHED();
    463     return;
    464   }
    465   scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr());
    466   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    467       NewRunnableMethod(task.get(), &TaskProxy::HandleCancel));
    468 }
    469 
    470 void SimUnlockHandler::HandleChangePinCode(const ListValue* args) {
    471   const size_t kChangePinParamCount = 2;
    472   std::string pin;
    473   std::string new_pin;
    474   if (args->GetSize() != kChangePinParamCount ||
    475       !args->GetString(0, &pin) ||
    476       !args->GetString(1, &new_pin)) {
    477     NOTREACHED();
    478     return;
    479   }
    480   new_pin_ = new_pin;
    481   HandleEnterCode(CODE_PIN, pin);
    482 }
    483 
    484 void SimUnlockHandler::HandleEnterCode(SimUnlockCode code_type,
    485                                        const std::string& code) {
    486   scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), code, code_type);
    487   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    488       NewRunnableMethod(task.get(), &TaskProxy::HandleEnterCode));
    489 }
    490 
    491 void SimUnlockHandler::HandleEnterPinCode(const ListValue* args) {
    492   const size_t kEnterPinParamCount = 1;
    493   std::string pin;
    494   if (args->GetSize() != kEnterPinParamCount || !args->GetString(0, &pin)) {
    495     NOTREACHED();
    496     return;
    497   }
    498   HandleEnterCode(CODE_PIN, pin);
    499 }
    500 
    501 void SimUnlockHandler::HandleEnterPukCode(const ListValue* args) {
    502   const size_t kEnterPukParamCount = 2;
    503   std::string puk;
    504   std::string new_pin;
    505   if (args->GetSize() != kEnterPukParamCount ||
    506       !args->GetString(0, &puk) ||
    507       !args->GetString(1, &new_pin)) {
    508     NOTREACHED();
    509     return;
    510   }
    511   new_pin_ = new_pin;
    512   HandleEnterCode(CODE_PUK, puk);
    513 }
    514 
    515 void SimUnlockHandler::HandleProceedToPukInput(const ListValue* args) {
    516   const size_t kProceedToPukInputParamCount = 0;
    517   if (args->GetSize() != kProceedToPukInputParamCount) {
    518     NOTREACHED();
    519     return;
    520   }
    521   scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr());
    522   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    523       NewRunnableMethod(task.get(), &TaskProxy::HandleProceedToPukInput));
    524 }
    525 
    526 void SimUnlockHandler::HandleSimStatusInitialize(const ListValue* args) {
    527   const size_t kSimStatusInitializeParamCount = 1;
    528   double mode;
    529   if (args->GetSize() != kSimStatusInitializeParamCount ||
    530       !args->GetDouble(0, &mode)) {
    531     NOTREACHED();
    532     return;
    533   }
    534   dialog_mode_ = static_cast<SimDialogDelegate::SimDialogMode>(mode);
    535   VLOG(1) << "Initializing SIM dialog in mode: " << dialog_mode_;
    536   scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr());
    537   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    538       NewRunnableMethod(task.get(), &TaskProxy::HandleInitialize));
    539 }
    540 
    541 void SimUnlockHandler::InitializeSimStatus() {
    542   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    543   ProcessSimCardState(GetCellularDevice());
    544 }
    545 
    546 void SimUnlockHandler::ProceedToPukInput() {
    547   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    548   ProcessSimCardState(GetCellularDevice());
    549 }
    550 
    551 void SimUnlockHandler::ProcessSimCardState(
    552     const chromeos::NetworkDevice* cellular) {
    553   std::string error_msg;
    554   if (cellular) {
    555     chromeos::SIMLockState lock_state = cellular->sim_lock_state();
    556     int retries_left = cellular->sim_retries_left();
    557     VLOG(1) << "Current state: " << state_ << " lock_state: " << lock_state
    558             << " retries: " << retries_left;
    559     switch (state_) {
    560       case SIM_UNLOCK_LOADING:
    561         if (lock_state == chromeos::SIM_LOCKED_PIN) {
    562           state_ = SIM_LOCKED_PIN;
    563         } else if (lock_state == chromeos::SIM_LOCKED_PUK) {
    564           if (retries_left > 0)
    565             state_ = SIM_LOCKED_PUK;
    566           else
    567             state_ = SIM_DISABLED;
    568         } else if (lock_state == chromeos::SIM_UNLOCKED) {
    569           if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON ||
    570               dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_OFF) {
    571             state_ = SIM_NOT_LOCKED_ASK_PIN;
    572           } else if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_CHANGE_PIN) {
    573             state_ = SIM_NOT_LOCKED_CHANGE_PIN;
    574           } else {
    575             state_ = SIM_ABSENT_NOT_LOCKED;
    576           }
    577         } else {
    578           // SIM_UNKNOWN: when SIM status is not initialized (should not happen,
    579           // since this UI is accessible when SIM is initialized)
    580           // or SIM card is absent. In latter case just close dialog.
    581           state_ = SIM_ABSENT_NOT_LOCKED;
    582         }
    583         break;
    584       case SIM_ABSENT_NOT_LOCKED:
    585         // Dialog will close itself in this case.
    586         break;
    587       case SIM_NOT_LOCKED_ASK_PIN:
    588       case SIM_NOT_LOCKED_CHANGE_PIN:
    589         // We always start in these states when SIM is unlocked.
    590         // So if we get here while still being UNLOCKED,
    591         // that means entered PIN was incorrect.
    592         if (lock_state == chromeos::SIM_UNLOCKED) {
    593           error_msg = kErrorPin;
    594         } else if (lock_state == chromeos::SIM_LOCKED_PUK) {
    595           state_ = SIM_LOCKED_NO_PIN_TRIES_LEFT;
    596         } else {
    597           NOTREACHED()
    598               << "Change PIN / Set lock mode with unexpected SIM lock state";
    599           state_ = SIM_ABSENT_NOT_LOCKED;
    600         }
    601         break;
    602       case SIM_LOCKED_PIN:
    603         if (lock_state == chromeos::SIM_UNLOCKED ||
    604             lock_state == chromeos::SIM_UNKNOWN) {
    605           state_ = SIM_ABSENT_NOT_LOCKED;
    606         } else if (lock_state == chromeos::SIM_LOCKED_PUK) {
    607           state_ = SIM_LOCKED_NO_PIN_TRIES_LEFT;
    608         } else {
    609           // Still blocked with PIN.
    610           error_msg = kErrorPin;
    611         }
    612         break;
    613       case SIM_LOCKED_NO_PIN_TRIES_LEFT:
    614         // Proceed user to PUK input.
    615         state_ = SIM_LOCKED_PUK;
    616         break;
    617       case SIM_LOCKED_PUK:
    618         if (lock_state == chromeos::SIM_UNLOCKED ||
    619             lock_state == chromeos::SIM_UNKNOWN) {
    620           state_ = SIM_ABSENT_NOT_LOCKED;
    621         } else if (retries_left == 0) {
    622           state_ = SIM_LOCKED_NO_PUK_TRIES_LEFT;
    623         }
    624         // Otherwise SIM card is still locked with PUK code.
    625         // Dialog will display enter PUK screen with an updated retries count.
    626         break;
    627       case SIM_LOCKED_NO_PUK_TRIES_LEFT:
    628       case SIM_DISABLED:
    629         // User will close dialog manually.
    630         break;
    631     }
    632   } else {
    633     VLOG(1) << "Cellular device is absent.";
    634     // No cellular device, should close dialog.
    635     state_ = SIM_ABSENT_NOT_LOCKED;
    636   }
    637   VLOG(1) << "New state: " << state_;
    638   UpdatePage(cellular, error_msg);
    639 }
    640 
    641 void SimUnlockHandler::UpdatePage(const chromeos::NetworkDevice* cellular,
    642                                   const std::string& error_msg) {
    643   DictionaryValue sim_dict;
    644   if (cellular)
    645     sim_dict.SetInteger(kTriesLeft, cellular->sim_retries_left());
    646   sim_dict.SetInteger(kState, state_);
    647   if (!error_msg.empty())
    648     sim_dict.SetString(kError, error_msg);
    649   else
    650     sim_dict.SetString(kError, kErrorOk);
    651   web_ui_->CallJavascriptFunction(kJsApiSimStatusChanged, sim_dict);
    652 }
    653 
    654 // SimUnlockUI -----------------------------------------------------------------
    655 
    656 SimUnlockUI::SimUnlockUI(TabContents* contents) : WebUI(contents) {
    657   SimUnlockHandler* handler = new SimUnlockHandler();
    658   AddMessageHandler((handler)->Attach(this));
    659   handler->Init(contents);
    660   SimUnlockUIHTMLSource* html_source = new SimUnlockUIHTMLSource();
    661 
    662   // Set up the chrome://sim-unlock/ source.
    663   contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
    664 }
    665 
    666 }  // namespace chromeos
    667