1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/mobile/mobile_activator.h"
6
7#include <algorithm>
8#include <map>
9#include <string>
10
11#include "ash/system/chromeos/network/network_connect.h"
12#include "base/bind.h"
13#include "base/bind_helpers.h"
14#include "base/file_util.h"
15#include "base/json/json_reader.h"
16#include "base/logging.h"
17#include "base/memory/ref_counted_memory.h"
18#include "base/message_loop/message_loop.h"
19#include "base/metrics/histogram.h"
20#include "base/observer_list_threadsafe.h"
21#include "base/prefs/pref_service.h"
22#include "base/strings/string_piece.h"
23#include "base/strings/string_util.h"
24#include "base/strings/utf_string_conversions.h"
25#include "base/timer/timer.h"
26#include "base/values.h"
27#include "chrome/browser/browser_process.h"
28#include "chrome/browser/chromeos/cros/network_library.h"
29#include "chrome/common/pref_names.h"
30#include "content/public/browser/browser_thread.h"
31
32using content::BrowserThread;
33
34namespace {
35
36// Cellular configuration file path.
37const char kCellularConfigPath[] =
38    "/usr/share/chromeos-assets/mobile/mobile_config.json";
39
40// Cellular config file field names.
41const char kVersionField[] = "version";
42const char kErrorsField[] = "errors";
43
44// Number of times we'll try an OTASP before failing the activation process.
45const int kMaxOTASPTries = 3;
46// Number of times we will retry to reconnect and reload payment portal page.
47const int kMaxPortalReconnectCount = 2;
48// Time between connection attempts when forcing a reconnect.
49const int kReconnectDelayMS = 3000;
50// Retry delay after failed OTASP attempt.
51const int kOTASPRetryDelay = 40000;
52// Maximum amount of time we'll wait for a service to reconnect.
53const int kMaxReconnectTime = 30000;
54
55// Error codes matching codes defined in the cellular config file.
56const char kErrorDefault[] = "default";
57const char kErrorBadConnectionPartial[] = "bad_connection_partial";
58const char kErrorBadConnectionActivated[] = "bad_connection_activated";
59const char kErrorRoamingOnConnection[] = "roaming_connection";
60const char kErrorNoEVDO[] = "no_evdo";
61const char kErrorRoamingActivation[] = "roaming_activation";
62const char kErrorRoamingPartiallyActivated[] = "roaming_partially_activated";
63const char kErrorNoService[] = "no_service";
64const char kErrorDisabled[] = "disabled";
65const char kErrorNoDevice[] = "no_device";
66const char kFailedPaymentError[] = "failed_payment";
67const char kFailedConnectivity[] = "connectivity";
68
69}  // namespace
70
71namespace chromeos {
72
73////////////////////////////////////////////////////////////////////////////////
74//
75// CellularConfigDocument
76//
77////////////////////////////////////////////////////////////////////////////////
78CellularConfigDocument::CellularConfigDocument() {}
79
80std::string CellularConfigDocument::GetErrorMessage(const std::string& code) {
81  base::AutoLock create(config_lock_);
82  ErrorMap::iterator iter = error_map_.find(code);
83  if (iter == error_map_.end())
84    return code;
85  return iter->second;
86}
87
88void CellularConfigDocument::LoadCellularConfigFile() {
89  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
90  // Load partner customization startup manifest if it is available.
91  base::FilePath config_path(kCellularConfigPath);
92  if (!base::PathExists(config_path))
93    return;
94
95  if (LoadFromFile(config_path))
96    DVLOG(1) << "Cellular config file loaded: " << kCellularConfigPath;
97  else
98    LOG(ERROR) << "Error loading cellular config file: " << kCellularConfigPath;
99}
100
101CellularConfigDocument::~CellularConfigDocument() {}
102
103void CellularConfigDocument::SetErrorMap(
104    const ErrorMap& map) {
105  base::AutoLock create(config_lock_);
106  error_map_.clear();
107  error_map_.insert(map.begin(), map.end());
108}
109
110bool CellularConfigDocument::LoadFromFile(const base::FilePath& config_path) {
111  std::string config;
112  if (!file_util::ReadFileToString(config_path, &config))
113    return false;
114
115  scoped_ptr<Value> root(
116      base::JSONReader::Read(config, base::JSON_ALLOW_TRAILING_COMMAS));
117  DCHECK(root.get() != NULL);
118  if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) {
119    LOG(WARNING) << "Bad cellular config file";
120    return false;
121  }
122
123  DictionaryValue* root_dict = static_cast<DictionaryValue*>(root.get());
124  if (!root_dict->GetString(kVersionField, &version_)) {
125    LOG(WARNING) << "Cellular config file missing version";
126    return false;
127  }
128  ErrorMap error_map;
129  DictionaryValue* errors = NULL;
130  if (!root_dict->GetDictionary(kErrorsField, &errors))
131    return false;
132  for (DictionaryValue::Iterator it(*errors); !it.IsAtEnd(); it.Advance()) {
133    std::string value;
134    if (!it.value().GetAsString(&value)) {
135      LOG(WARNING) << "Bad cellular config error value";
136      return false;
137    }
138    error_map.insert(ErrorMap::value_type(it.key(), value));
139  }
140  SetErrorMap(error_map);
141  return true;
142}
143
144////////////////////////////////////////////////////////////////////////////////
145//
146// MobileActivator
147//
148////////////////////////////////////////////////////////////////////////////////
149MobileActivator::MobileActivator()
150    : cellular_config_(new CellularConfigDocument()),
151      state_(PLAN_ACTIVATION_PAGE_LOADING),
152      reenable_cert_check_(false),
153      terminated_(true),
154      connection_retry_count_(0),
155      payment_reconnect_count_(0) {
156}
157
158MobileActivator::~MobileActivator() {
159  TerminateActivation();
160}
161
162MobileActivator* MobileActivator::GetInstance() {
163  return Singleton<MobileActivator>::get();
164}
165
166void MobileActivator::TerminateActivation() {
167  // We're exiting; don't continue with termination.
168  if (!NetworkLibrary::Get())
169    return;
170
171  state_duration_timer_.Stop();
172  continue_reconnect_timer_.Stop();
173  reconnect_timeout_timer_.Stop();
174
175  NetworkLibrary* lib = GetNetworkLibrary();
176  lib->RemoveNetworkManagerObserver(this);
177  lib->RemoveObserverForAllNetworks(this);
178  ReEnableCertRevocationChecking();
179  meid_.clear();
180  iccid_.clear();
181  service_path_.clear();
182  state_ = PLAN_ACTIVATION_PAGE_LOADING;
183  reenable_cert_check_ = false;
184  terminated_ = true;
185  // Release the previous cellular config and setup a new empty one.
186  cellular_config_ = new CellularConfigDocument();
187}
188
189void MobileActivator::OnNetworkManagerChanged(NetworkLibrary* cros) {
190  if (state_ == PLAN_ACTIVATION_PAGE_LOADING ||
191      state_ == PLAN_ACTIVATION_DONE ||
192      state_ == PLAN_ACTIVATION_ERROR) {
193    return;
194  }
195
196  CellularNetwork* network = FindMatchingCellularNetwork(true);
197  if (network && network->activate_over_non_cellular_network()) {
198    bool waiting = (state_ == PLAN_ACTIVATION_WAITING_FOR_CONNECTION);
199    bool is_online =
200        cros->connected_network() && cros->connected_network()->online();
201    if (waiting && is_online) {
202      ChangeState(network, post_reconnect_state_, "");
203    } else if (!waiting && !is_online) {
204      ChangeState(network, PLAN_ACTIVATION_WAITING_FOR_CONNECTION, "");
205    }
206  }
207
208  EvaluateCellularNetwork(network);
209}
210
211void MobileActivator::OnNetworkChanged(NetworkLibrary* cros,
212                                       const Network* network) {
213  if (state_ == PLAN_ACTIVATION_PAGE_LOADING)
214    return;
215
216  if (!network || network->type() != TYPE_CELLULAR) {
217    NOTREACHED();
218    return;
219  }
220
221  EvaluateCellularNetwork(
222      static_cast<CellularNetwork*>(const_cast<Network*>(network)));
223}
224
225void MobileActivator::AddObserver(MobileActivator::Observer* observer) {
226  DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::UI));
227  observers_.AddObserver(observer);
228}
229
230void MobileActivator::RemoveObserver(MobileActivator::Observer* observer) {
231  DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::UI));
232  observers_.RemoveObserver(observer);
233}
234
235void MobileActivator::InitiateActivation(const std::string& service_path) {
236  DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::UI));
237  NetworkLibrary* lib = GetNetworkLibrary();
238  CellularNetwork* network = lib->FindCellularNetworkByPath(service_path);
239  if (!network) {
240    LOG(ERROR) << "Cellular service can't be found: " << service_path;
241    return;
242  }
243
244  const chromeos::NetworkDevice* device =
245      lib->FindNetworkDeviceByPath(network->device_path());
246  if (!device) {
247    LOG(ERROR) << "Cellular device can't be found: " << network->device_path();
248    return;
249  }
250
251  terminated_ = false;
252  meid_ = device->meid();
253  iccid_ = device->iccid();
254  service_path_ = service_path;
255
256  ChangeState(network, PLAN_ACTIVATION_PAGE_LOADING, "");
257
258  BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE,
259      base::Bind(&CellularConfigDocument::LoadCellularConfigFile,
260                 cellular_config_.get()),
261      base::Bind(&MobileActivator::ContinueActivation, AsWeakPtr()));
262}
263
264void MobileActivator::ContinueActivation() {
265  CellularNetwork* network = FindMatchingCellularNetwork(false);
266  if (!network || !network->SupportsActivation())
267    return;
268
269  DisableCertRevocationChecking();
270  // We want shill to connect us after activations.
271  network->SetAutoConnect(true);
272
273  StartActivation();
274}
275
276void MobileActivator::OnSetTransactionStatus(bool success) {
277  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
278      base::Bind(&MobileActivator::HandleSetTransactionStatus,
279                 AsWeakPtr(), success));
280}
281
282void MobileActivator::HandleSetTransactionStatus(bool success) {
283  // The payment is received, try to reconnect and check the status all over
284  // again.
285  if (success && state_ == PLAN_ACTIVATION_SHOWING_PAYMENT) {
286    NetworkLibrary* lib = GetNetworkLibrary();
287    lib->SignalCellularPlanPayment();
288    UMA_HISTOGRAM_COUNTS("Cellular.PaymentReceived", 1);
289    CellularNetwork* network = FindMatchingCellularNetwork(true);
290    if (network && network->activate_over_non_cellular_network()) {
291      state_ = PLAN_ACTIVATION_DONE;
292      network->CompleteActivation();
293    } else {
294      StartOTASP();
295    }
296  } else {
297    UMA_HISTOGRAM_COUNTS("Cellular.PaymentFailed", 1);
298  }
299}
300
301void MobileActivator::OnPortalLoaded(bool success) {
302  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
303      base::Bind(&MobileActivator::HandlePortalLoaded,
304                 AsWeakPtr(), success));
305}
306
307void MobileActivator::HandlePortalLoaded(bool success) {
308  CellularNetwork* network = FindMatchingCellularNetwork(true);
309  if (!network) {
310    ChangeState(NULL, PLAN_ACTIVATION_ERROR,
311                GetErrorMessage(kErrorNoService));
312    return;
313  }
314  if (state_ == PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING ||
315      state_ == PLAN_ACTIVATION_SHOWING_PAYMENT) {
316    if (success) {
317      payment_reconnect_count_ = 0;
318      ChangeState(network, PLAN_ACTIVATION_SHOWING_PAYMENT, std::string());
319    } else {
320      // There is no point in forcing reconnecting the cellular network if the
321      // activation should not be done over it.
322      if (network->activate_over_non_cellular_network())
323        return;
324
325      payment_reconnect_count_++;
326      if (payment_reconnect_count_ > kMaxPortalReconnectCount) {
327        ChangeState(NULL, PLAN_ACTIVATION_ERROR,
328                    GetErrorMessage(kErrorNoService));
329        return;
330      }
331
332      // Reconnect and try and load the frame again.
333      ChangeState(network,
334                  PLAN_ACTIVATION_RECONNECTING,
335                  GetErrorMessage(kFailedPaymentError));
336    }
337  } else {
338    NOTREACHED() << "Called paymentPortalLoad while in unexpected state: "
339                 << GetStateDescription(state_);
340  }
341}
342
343CellularNetwork* MobileActivator::FindMatchingCellularNetwork(
344    bool reattach_observer) {
345  NetworkLibrary* lib = GetNetworkLibrary();
346  for (CellularNetworkVector::const_iterator it =
347           lib->cellular_networks().begin();
348       it != lib->cellular_networks().end(); ++it) {
349    const chromeos::NetworkDevice* device =
350        lib->FindNetworkDeviceByPath((*it)->device_path());
351    if (device && ((!meid_.empty() && meid_ == device->meid()) ||
352                   (!iccid_.empty() && iccid_ == device->iccid()))) {
353      CellularNetwork* network = *it;
354      // If service path has changed, reattach the event observer for this
355      // network service.
356      if (reattach_observer && service_path_ != network->service_path()) {
357        lib->RemoveObserverForAllNetworks(this);
358        lib->AddNetworkObserver(network->service_path(), this);
359        service_path_ = network->service_path();
360      }
361      return network;
362    }
363  }
364  return NULL;
365}
366
367void MobileActivator::StartOTASPTimer() {
368  state_duration_timer_.Start(
369      FROM_HERE,
370      base::TimeDelta::FromMilliseconds(kOTASPRetryDelay),
371      this, &MobileActivator::HandleOTASPTimeout);
372}
373
374void MobileActivator::StartActivation() {
375  UMA_HISTOGRAM_COUNTS("Cellular.MobileSetupStart", 1);
376  NetworkLibrary* lib = GetNetworkLibrary();
377  CellularNetwork* network = FindMatchingCellularNetwork(true);
378  // Check if we can start activation process.
379  if (!network) {
380    std::string error;
381    if (!lib->cellular_available())
382      error = kErrorNoDevice;
383    else if (!lib->cellular_enabled())
384      error = kErrorDisabled;
385    else
386      error = kErrorNoService;
387    ChangeState(NULL, PLAN_ACTIVATION_ERROR, GetErrorMessage(error));
388    return;
389  }
390
391  // Start monitoring network property changes.
392  lib->AddNetworkManagerObserver(this);
393  if (network->activate_over_non_cellular_network()) {
394    // Fast forward to payment portal loading if the activation is performed
395    // over a non-cellular network.
396    ChangeState(network,
397                (network->activation_state() == ACTIVATION_STATE_ACTIVATED) ?
398                PLAN_ACTIVATION_DONE :
399                PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING,
400                "");
401    // Verify that there is no need to wait for the connection. This will also
402    // evaluate the network.
403    OnNetworkManagerChanged(lib);
404    return;
405  }
406
407  if (lib->HasRecentCellularPlanPayment() &&
408      network->activation_state() == ACTIVATION_STATE_PARTIALLY_ACTIVATED) {
409    // Try to start with OTASP immediately if we have received payment recently.
410    state_ = PLAN_ACTIVATION_START_OTASP;
411  } else {
412    state_ =  PLAN_ACTIVATION_START;
413  }
414
415  EvaluateCellularNetwork(network);
416}
417
418void MobileActivator::RetryOTASP() {
419  DCHECK(state_ == PLAN_ACTIVATION_DELAY_OTASP);
420  StartOTASP();
421}
422
423void MobileActivator::StartOTASP() {
424  CellularNetwork* network = FindMatchingCellularNetwork(true);
425  ChangeState(network, PLAN_ACTIVATION_START_OTASP, std::string());
426  EvaluateCellularNetwork(network);
427}
428
429void MobileActivator::HandleOTASPTimeout() {
430  LOG(WARNING) << "OTASP seems to be taking too long.";
431  CellularNetwork* network = FindMatchingCellularNetwork(true);
432  // We're here because one of OTASP steps is taking too long to complete.
433  // Usually, this means something bad has happened below us.
434  if (state_ == PLAN_ACTIVATION_INITIATING_ACTIVATION) {
435    ++initial_OTASP_attempts_;
436    if (initial_OTASP_attempts_ <= kMaxOTASPTries) {
437      ChangeState(network,
438                  PLAN_ACTIVATION_RECONNECTING,
439                  GetErrorMessage(kErrorDefault));
440      return;
441    }
442  } else if (state_ == PLAN_ACTIVATION_TRYING_OTASP) {
443    ++trying_OTASP_attempts_;
444    if (trying_OTASP_attempts_ <= kMaxOTASPTries) {
445      ChangeState(network,
446                  PLAN_ACTIVATION_RECONNECTING,
447                  GetErrorMessage(kErrorDefault));
448      return;
449    }
450  } else if (state_ == PLAN_ACTIVATION_OTASP) {
451    ++final_OTASP_attempts_;
452    if (final_OTASP_attempts_ <= kMaxOTASPTries) {
453      // Give the portal time to propagate all those magic bits.
454      ChangeState(network,
455                  PLAN_ACTIVATION_DELAY_OTASP,
456                  GetErrorMessage(kErrorDefault));
457      return;
458    }
459  } else {
460    LOG(ERROR) << "OTASP timed out from a non-OTASP wait state?";
461  }
462  LOG(ERROR) << "OTASP failed too many times; aborting.";
463  ChangeState(network,
464              PLAN_ACTIVATION_ERROR,
465              GetErrorMessage(kErrorDefault));
466}
467
468void MobileActivator::ForceReconnect(CellularNetwork* network,
469                                     PlanActivationState next_state) {
470  DCHECK(network);
471  // Store away our next destination for when we complete.
472  post_reconnect_state_ = next_state;
473  UMA_HISTOGRAM_COUNTS("Cellular.ActivationRetry", 1);
474  // First, disconnect...
475  LOG(INFO) << "Disconnecting from " << network->service_path();
476  // Explicit service Disconnect()s disable autoconnect on the service until
477  // Connect() is called on the service again.  Hence this dance to explicitly
478  // call Connect().
479  GetNetworkLibrary()->DisconnectFromNetwork(network);
480  // Keep trying to connect until told otherwise.
481  continue_reconnect_timer_.Stop();
482  continue_reconnect_timer_.Start(
483      FROM_HERE,
484      base::TimeDelta::FromMilliseconds(kReconnectDelayMS),
485      this, &MobileActivator::ContinueConnecting);
486  // If we don't ever connect again, we're going to call this a failure.
487  reconnect_timeout_timer_.Stop();
488  reconnect_timeout_timer_.Start(
489      FROM_HERE,
490      base::TimeDelta::FromMilliseconds(kMaxReconnectTime),
491      this, &MobileActivator::ReconnectTimedOut);
492}
493
494void MobileActivator::ReconnectTimedOut() {
495  LOG(ERROR) << "Ending activation attempt after failing to reconnect.";
496  ChangeState(FindMatchingCellularNetwork(true),
497              PLAN_ACTIVATION_ERROR,
498              GetErrorMessage(kFailedConnectivity));
499}
500
501void MobileActivator::ContinueConnecting() {
502  CellularNetwork* network = FindMatchingCellularNetwork(true);
503  if (network && network->connected()) {
504    if (network->restricted_pool() &&
505        network->error() == ERROR_DNS_LOOKUP_FAILED) {
506      // It isn't an error to be in a restricted pool, but if DNS doesn't work,
507      // then we're not getting traffic through at all.  Just disconnect and
508      // try again.
509      GetNetworkLibrary()->DisconnectFromNetwork(network);
510      return;
511    }
512    // Stop this callback
513    continue_reconnect_timer_.Stop();
514    EvaluateCellularNetwork(network);
515  } else {
516    LOG(WARNING) << "Connect failed, will try again in a little bit.";
517    if (network) {
518      LOG(INFO) << "Connecting to: " << network->service_path();
519      ash::network_connect::ConnectToNetwork(
520          network->service_path(), NULL /* no parent window */);
521    }
522  }
523}
524
525void MobileActivator::EvaluateCellularNetwork(CellularNetwork* network) {
526  if (terminated_) {
527    LOG(ERROR) << "Tried to run MobileActivator state machine while "
528               << "terminated.";
529    return;
530  }
531
532  if (!network) {
533    LOG(WARNING) << "Cellular service lost";
534    return;
535  }
536
537  LOG(WARNING) << "Cellular:\n  service=" << network->GetStateString()
538      << "\n  ui=" << GetStateDescription(state_)
539      << "\n  activation=" << network->GetActivationStateString()
540      << "\n  error=" << network->GetErrorString()
541      << "\n  setvice_path=" << network->service_path()
542      << "\n  connected=" << network->connected();
543
544  // If the network is activated over non cellular network, the activator state
545  // does not depend on the network's own state.
546  if (network->activate_over_non_cellular_network())
547    return;
548
549  std::string error_description;
550  PlanActivationState new_state = PickNextState(network, &error_description);
551
552  ChangeState(network, new_state, error_description);
553}
554
555MobileActivator::PlanActivationState MobileActivator::PickNextState(
556    CellularNetwork* network, std::string* error_description) const {
557  PlanActivationState new_state = state_;
558  if (!network->connected())
559    new_state = PickNextOfflineState(network);
560  else
561    new_state = PickNextOnlineState(network);
562  if (new_state != PLAN_ACTIVATION_ERROR &&
563      GotActivationError(network, error_description)) {
564    // Check for this special case when we try to do activate partially
565    // activated device. If that attempt failed, try to disconnect to clear the
566    // state and reconnect again.
567    if ((network->activation_state() == ACTIVATION_STATE_PARTIALLY_ACTIVATED ||
568         network->activation_state() == ACTIVATION_STATE_ACTIVATING) &&
569        (network->error() == ERROR_NO_ERROR ||
570         network->error() == ERROR_OTASP_FAILED) &&
571        network->state() == STATE_ACTIVATION_FAILURE) {
572      LOG(WARNING) << "Activation failure detected "
573                   << network->service_path();
574      switch (state_) {
575        case PLAN_ACTIVATION_OTASP:
576          new_state = PLAN_ACTIVATION_DELAY_OTASP;
577          break;
578        case PLAN_ACTIVATION_INITIATING_ACTIVATION:
579        case PLAN_ACTIVATION_TRYING_OTASP:
580          new_state = PLAN_ACTIVATION_START;
581          break;
582        case PLAN_ACTIVATION_START:
583          // We are just starting, so this must be previous activation attempt
584          // failure.
585          new_state = PLAN_ACTIVATION_TRYING_OTASP;
586          break;
587        case PLAN_ACTIVATION_DELAY_OTASP:
588          new_state = state_;
589          break;
590        default:
591          new_state = PLAN_ACTIVATION_ERROR;
592          break;
593      }
594    } else {
595      LOG(WARNING) << "Unexpected activation failure for "
596                   << network->service_path();
597      new_state = PLAN_ACTIVATION_ERROR;
598    }
599  }
600
601  if (new_state == PLAN_ACTIVATION_ERROR && !error_description->length())
602    *error_description = GetErrorMessage(kErrorDefault);
603  return new_state;
604}
605
606MobileActivator::PlanActivationState MobileActivator::PickNextOfflineState(
607    CellularNetwork* network) const {
608  PlanActivationState new_state = state_;
609  switch (state_) {
610    case PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING:
611    case PLAN_ACTIVATION_SHOWING_PAYMENT:
612      if (!network->activate_over_non_cellular_network())
613        new_state = PLAN_ACTIVATION_RECONNECTING;
614      break;
615    case PLAN_ACTIVATION_START:
616      switch (network->activation_state()) {
617        case ACTIVATION_STATE_ACTIVATED:
618          if (network->restricted_pool())
619            new_state = PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING;
620          else
621            new_state = PLAN_ACTIVATION_DONE;
622          break;
623        case ACTIVATION_STATE_PARTIALLY_ACTIVATED:
624          new_state = PLAN_ACTIVATION_TRYING_OTASP;
625          break;
626        default:
627          new_state = PLAN_ACTIVATION_INITIATING_ACTIVATION;
628          break;
629      }
630      break;
631    default: {
632      LOG(INFO) << "Waiting for cellular service to connect.";
633      } break;
634  }
635  return new_state;
636}
637
638MobileActivator::PlanActivationState MobileActivator::PickNextOnlineState(
639    CellularNetwork* network) const {
640  PlanActivationState new_state = state_;
641  switch (state_) {
642    case PLAN_ACTIVATION_START:
643      switch (network->activation_state()) {
644        case ACTIVATION_STATE_ACTIVATED:
645          if (network->restricted_pool())
646            new_state = PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING;
647          else
648            new_state = PLAN_ACTIVATION_DONE;
649          break;
650        case ACTIVATION_STATE_PARTIALLY_ACTIVATED:
651          new_state = PLAN_ACTIVATION_TRYING_OTASP;
652          break;
653        default:
654          new_state = PLAN_ACTIVATION_INITIATING_ACTIVATION;
655          break;
656      }
657      break;
658    case PLAN_ACTIVATION_START_OTASP: {
659      switch (network->activation_state()) {
660        case ACTIVATION_STATE_PARTIALLY_ACTIVATED:
661          new_state = PLAN_ACTIVATION_OTASP;
662          break;
663        case ACTIVATION_STATE_ACTIVATED:
664          new_state = PLAN_ACTIVATION_RECONNECTING;
665          break;
666        default: {
667          LOG(WARNING) << "Unexpected activation state for device "
668                       << network->service_path();
669          break;
670        }
671      }
672      break;
673    }
674    case PLAN_ACTIVATION_DELAY_OTASP:
675      // Just ignore any changes until the OTASP retry timer kicks in.
676      break;
677    case PLAN_ACTIVATION_INITIATING_ACTIVATION: {
678      switch (network->activation_state()) {
679        case ACTIVATION_STATE_ACTIVATED:
680        case ACTIVATION_STATE_PARTIALLY_ACTIVATED:
681          new_state = PLAN_ACTIVATION_START;
682          break;
683        case ACTIVATION_STATE_NOT_ACTIVATED:
684        case ACTIVATION_STATE_ACTIVATING:
685          // Wait in this state until activation state changes.
686          break;
687        default:
688          LOG(WARNING) << "Unknown transition";
689          break;
690      }
691      break;
692    }
693    case PLAN_ACTIVATION_OTASP:
694    case PLAN_ACTIVATION_TRYING_OTASP:
695      if (network->activation_state() == ACTIVATION_STATE_NOT_ACTIVATED ||
696          network->activation_state() == ACTIVATION_STATE_ACTIVATING) {
697        LOG(INFO) << "Waiting for the OTASP to finish and the service to "
698                  << "come back online";
699      } else if (network->activation_state() == ACTIVATION_STATE_ACTIVATED) {
700        new_state = PLAN_ACTIVATION_DONE;
701      } else {
702        new_state = PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING;
703      }
704      break;
705    case PLAN_ACTIVATION_RECONNECTING_PAYMENT:
706      if (!network->restricted_pool() &&
707          network->activation_state() == ACTIVATION_STATE_ACTIVATED)
708        // We're not portalled, and we're already activated, so we're online!
709        new_state = PLAN_ACTIVATION_DONE;
710      else
711        new_state = PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING;
712      break;
713    // Initial state
714    case PLAN_ACTIVATION_PAGE_LOADING:
715      break;
716    // Just ignore all signals until the site confirms payment.
717    case PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING:
718    case PLAN_ACTIVATION_SHOWING_PAYMENT:
719    case PLAN_ACTIVATION_WAITING_FOR_CONNECTION:
720      break;
721    // Go where we decided earlier.
722    case PLAN_ACTIVATION_RECONNECTING:
723      new_state = post_reconnect_state_;
724      break;
725    // Activation completed/failed, ignore network changes.
726    case PLAN_ACTIVATION_DONE:
727    case PLAN_ACTIVATION_ERROR:
728      break;
729  }
730
731  return new_state;
732}
733
734// Debugging helper function, will take it out at the end.
735const char* MobileActivator::GetStateDescription(PlanActivationState state) {
736  switch (state) {
737    case PLAN_ACTIVATION_PAGE_LOADING:
738      return "PAGE_LOADING";
739    case PLAN_ACTIVATION_START:
740      return "ACTIVATION_START";
741    case PLAN_ACTIVATION_INITIATING_ACTIVATION:
742      return "INITIATING_ACTIVATION";
743    case PLAN_ACTIVATION_TRYING_OTASP:
744      return "TRYING_OTASP";
745    case PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING:
746      return "PAYMENT_PORTAL_LOADING";
747    case PLAN_ACTIVATION_SHOWING_PAYMENT:
748      return "SHOWING_PAYMENT";
749    case PLAN_ACTIVATION_RECONNECTING_PAYMENT:
750      return "RECONNECTING_PAYMENT";
751    case PLAN_ACTIVATION_DELAY_OTASP:
752      return "DELAY_OTASP";
753    case PLAN_ACTIVATION_START_OTASP:
754      return "START_OTASP";
755    case PLAN_ACTIVATION_OTASP:
756      return "OTASP";
757    case PLAN_ACTIVATION_DONE:
758      return "DONE";
759    case PLAN_ACTIVATION_ERROR:
760      return "ERROR";
761    case PLAN_ACTIVATION_RECONNECTING:
762      return "RECONNECTING";
763    case PLAN_ACTIVATION_WAITING_FOR_CONNECTION:
764      return "WAITING FOR CONNECTION";
765  }
766  return "UNKNOWN";
767}
768
769
770void MobileActivator::CompleteActivation(
771    CellularNetwork* network) {
772  // Remove observers, we are done with this page.
773  NetworkLibrary* lib = GetNetworkLibrary();
774  lib->RemoveNetworkManagerObserver(this);
775  lib->RemoveObserverForAllNetworks(this);
776  // Reactivate other types of connections if we have
777  // shut them down previously.
778  ReEnableCertRevocationChecking();
779}
780
781bool MobileActivator::RunningActivation() const {
782  return !(state_ == PLAN_ACTIVATION_DONE ||
783           state_ == PLAN_ACTIVATION_ERROR ||
784           state_ == PLAN_ACTIVATION_PAGE_LOADING);
785}
786
787void MobileActivator::ChangeState(CellularNetwork* network,
788                                  PlanActivationState new_state,
789                                  const std::string& error_description) {
790  static bool first_time = true;
791  LOG(INFO) << "Activation state flip old = "
792            << GetStateDescription(state_)
793            << ", new = " << GetStateDescription(new_state);
794  if (state_ == new_state && !first_time)
795    return;
796  first_time = false;
797  LOG(INFO) << "Transitioning...";
798
799  // Kill all the possible timers and callbacks we might have outstanding.
800  state_duration_timer_.Stop();
801  continue_reconnect_timer_.Stop();
802  reconnect_timeout_timer_.Stop();
803  const PlanActivationState old_state = state_;
804  state_ = new_state;
805
806  // Signal to observers layer that the state is changing.
807  FOR_EACH_OBSERVER(Observer, observers_,
808      OnActivationStateChanged(network, state_, error_description));
809
810  // Pick action that should happen on entering the new state.
811  switch (new_state) {
812    case PLAN_ACTIVATION_START:
813      break;
814    case PLAN_ACTIVATION_DELAY_OTASP: {
815      UMA_HISTOGRAM_COUNTS("Cellular.RetryOTASP", 1);
816      BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
817          base::Bind(&MobileActivator::RetryOTASP, AsWeakPtr()),
818          base::TimeDelta::FromMilliseconds(kOTASPRetryDelay));
819      break;
820    }
821    case PLAN_ACTIVATION_START_OTASP:
822      break;
823    case PLAN_ACTIVATION_INITIATING_ACTIVATION:
824    case PLAN_ACTIVATION_TRYING_OTASP:
825    case PLAN_ACTIVATION_OTASP:
826      DCHECK(network);
827      LOG(WARNING) << "Activating service " << network->service_path();
828      UMA_HISTOGRAM_COUNTS("Cellular.ActivationTry", 1);
829      if (!network->StartActivation()) {
830        UMA_HISTOGRAM_COUNTS("Cellular.ActivationFailure", 1);
831        LOG(ERROR) << "Failed to call Activate() on service in shill.";
832        if (new_state == PLAN_ACTIVATION_OTASP) {
833          ChangeState(network, PLAN_ACTIVATION_DELAY_OTASP, std::string());
834        } else {
835          ChangeState(network,
836                      PLAN_ACTIVATION_ERROR,
837                      GetErrorMessage(kFailedConnectivity));
838        }
839      } else {
840        StartOTASPTimer();
841      }
842      break;
843    case PLAN_ACTIVATION_PAGE_LOADING:
844      return;
845    case PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING:
846    case PLAN_ACTIVATION_SHOWING_PAYMENT:
847    case PLAN_ACTIVATION_RECONNECTING_PAYMENT:
848      // Fix for fix SSL for the walled gardens where cert chain verification
849      // might not work.
850      break;
851    case PLAN_ACTIVATION_WAITING_FOR_CONNECTION:
852      post_reconnect_state_ = old_state;
853      break;
854    case PLAN_ACTIVATION_RECONNECTING: {
855      PlanActivationState next_state = old_state;
856      // Pick where we want to return to after we reconnect.
857      switch (old_state) {
858        case PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING:
859        case PLAN_ACTIVATION_SHOWING_PAYMENT:
860          // We decide here what to do next based on the state of the modem.
861          next_state = PLAN_ACTIVATION_RECONNECTING_PAYMENT;
862          break;
863        case PLAN_ACTIVATION_INITIATING_ACTIVATION:
864        case PLAN_ACTIVATION_TRYING_OTASP:
865          next_state = PLAN_ACTIVATION_START;
866          break;
867        case PLAN_ACTIVATION_START_OTASP:
868        case PLAN_ACTIVATION_OTASP:
869          if (!network || !network->connected()) {
870            next_state = PLAN_ACTIVATION_START_OTASP;
871          } else {
872            // We're online, which means we've conspired with
873            // PickNextOnlineState to reconnect after activation (that's the
874            // only way we see this transition).  Thus, after we reconnect, we
875            // should be done.
876            next_state = PLAN_ACTIVATION_DONE;
877          }
878          break;
879        default:
880          LOG(ERROR) << "Transitioned to RECONNECTING from an unexpected "
881                     << "state.";
882          break;
883      }
884      ForceReconnect(network, next_state);
885      break;
886    }
887    case PLAN_ACTIVATION_DONE:
888      DCHECK(network);
889      CompleteActivation(network);
890      UMA_HISTOGRAM_COUNTS("Cellular.MobileSetupSucceeded", 1);
891      break;
892    case PLAN_ACTIVATION_ERROR:
893      CompleteActivation(NULL);
894      UMA_HISTOGRAM_COUNTS("Cellular.PlanFailed", 1);
895      break;
896    default:
897      break;
898  }
899}
900
901void MobileActivator::ReEnableCertRevocationChecking() {
902  PrefService* prefs = g_browser_process->local_state();
903  if (reenable_cert_check_) {
904    prefs->SetBoolean(prefs::kCertRevocationCheckingEnabled,
905                      true);
906    reenable_cert_check_ = false;
907  }
908}
909
910void MobileActivator::DisableCertRevocationChecking() {
911  // Disable SSL cert checks since we might be performing activation in the
912  // restricted pool.
913  // TODO(rkc): We want to do this only if on Cellular.
914  PrefService* prefs = g_browser_process->local_state();
915  if (!reenable_cert_check_ &&
916      prefs->GetBoolean(
917          prefs::kCertRevocationCheckingEnabled)) {
918    reenable_cert_check_ = true;
919    prefs->SetBoolean(prefs::kCertRevocationCheckingEnabled, false);
920  }
921}
922
923bool MobileActivator::GotActivationError(
924    CellularNetwork* network, std::string* error) const {
925  DCHECK(network);
926  bool got_error = false;
927  const char* error_code = kErrorDefault;
928
929  // This is the magic for detection of errors in during activation process.
930  if (network->state() == STATE_FAILURE &&
931      network->error() == ERROR_AAA_FAILED) {
932    if (network->activation_state() ==
933            ACTIVATION_STATE_PARTIALLY_ACTIVATED) {
934      error_code = kErrorBadConnectionPartial;
935    } else if (network->activation_state() == ACTIVATION_STATE_ACTIVATED) {
936      if (network->roaming_state() == ROAMING_STATE_HOME)
937        error_code = kErrorBadConnectionActivated;
938      else if (network->roaming_state() == ROAMING_STATE_ROAMING)
939        error_code = kErrorRoamingOnConnection;
940    }
941    got_error = true;
942  } else if (network->state() == STATE_ACTIVATION_FAILURE) {
943    if (network->error() == ERROR_NEED_EVDO) {
944      if (network->activation_state() == ACTIVATION_STATE_PARTIALLY_ACTIVATED)
945        error_code = kErrorNoEVDO;
946    } else if (network->error() == ERROR_NEED_HOME_NETWORK) {
947      if (network->activation_state() == ACTIVATION_STATE_NOT_ACTIVATED) {
948        error_code = kErrorRoamingActivation;
949      } else if (network->activation_state() ==
950                 ACTIVATION_STATE_PARTIALLY_ACTIVATED) {
951        error_code = kErrorRoamingPartiallyActivated;
952      }
953    }
954    got_error = true;
955  }
956
957  if (got_error)
958    *error = GetErrorMessage(error_code);
959
960  return got_error;
961}
962
963void MobileActivator::GetDeviceInfo(CellularNetwork* network,
964                                    DictionaryValue* value) {
965  DCHECK(network);
966  NetworkLibrary* cros = NetworkLibrary::Get();
967  if (!cros)
968    return;
969  value->SetString("carrier", network->name());
970  value->SetString("payment_url", network->payment_url());
971  if (network->using_post() && network->post_data().length())
972    value->SetString("post_data", network->post_data());
973
974  const NetworkDevice* device =
975      cros->FindNetworkDeviceByPath(network->device_path());
976  if (device) {
977    value->SetString("MEID", device->meid());
978    value->SetString("IMEI", device->imei());
979    value->SetString("MDN", device->mdn());
980  }
981}
982
983std::string MobileActivator::GetErrorMessage(const std::string& code) const {
984  return cellular_config_->GetErrorMessage(code);
985}
986
987NetworkLibrary* MobileActivator::GetNetworkLibrary() const {
988  return NetworkLibrary::Get();
989}
990
991}  // namespace chromeos
992