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
29namespace {
30
31// JS API callbacks names.
32const char kJsApiCancel[] = "cancel";
33const char kJsApiChangePinCode[] = "changePinCode";
34const char kJsApiEnterPinCode[] = "enterPinCode";
35const char kJsApiEnterPukCode[] = "enterPukCode";
36const char kJsApiProceedToPukInput[] = "proceedToPukInput";
37const char kJsApiSimStatusInitialize[] = "simStatusInitialize";
38
39// Page JS API function names.
40const char kJsApiSimStatusChanged[] = "mobile.SimUnlock.simStateChanged";
41
42// SIM state variables which are passed to the page.
43const char kState[] = "state";
44const char kError[] = "error";
45const char kTriesLeft[] = "tries";
46
47// Error constants, passed to the page.
48const char kErrorPin[] = "incorrectPin";
49const char kErrorPuk[] = "incorrectPuk";
50const char kErrorOk[] = "ok";
51
52const chromeos::NetworkDevice* GetCellularDevice() {
53  chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->
54      GetNetworkLibrary();
55  CHECK(lib);
56  return lib->FindCellularDevice();
57}
58
59}  // namespace
60
61namespace chromeos {
62
63class 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.
84class 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
232SimUnlockUIHTMLSource::SimUnlockUIHTMLSource()
233    : DataSource(chrome::kChromeUISimUnlockHost, MessageLoop::current()) {
234}
235
236void 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
310SimUnlockHandler::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
324SimUnlockHandler::~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
332WebUIMessageHandler* SimUnlockHandler::Attach(WebUI* web_ui) {
333  return WebUIMessageHandler::Attach(web_ui);
334}
335
336void SimUnlockHandler::Init(TabContents* contents) {
337  tab_contents_ = contents;
338}
339
340void 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
355void 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
364void 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
393void 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
407void 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
445void SimUnlockHandler::NotifyOnEnterPinEnded(bool cancelled) {
446  NotificationService::current()->Notify(
447      NotificationType::ENTER_PIN_ENDED,
448      NotificationService::AllSources(),
449      Details<bool>(&cancelled));
450}
451
452void 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
459void 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
470void 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
484void 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
491void 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
501void 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
515void 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
526void 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
541void SimUnlockHandler::InitializeSimStatus() {
542  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
543  ProcessSimCardState(GetCellularDevice());
544}
545
546void SimUnlockHandler::ProceedToPukInput() {
547  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
548  ProcessSimCardState(GetCellularDevice());
549}
550
551void 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
641void 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
656SimUnlockUI::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