123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/services/cellular_setup/ota_activator_impl.h"
- #include <sstream>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/logging.h"
- #include "base/memory/ptr_util.h"
- #include "base/metrics/histogram_functions.h"
- #include "chromeos/ash/components/dbus/shill/shill_device_client.h"
- #include "chromeos/ash/components/network/cellular_utils.h"
- #include "chromeos/ash/components/network/device_state.h"
- #include "chromeos/ash/components/network/network_activation_handler.h"
- #include "chromeos/ash/components/network/network_connection_handler.h"
- #include "chromeos/ash/components/network/network_event_log.h"
- #include "chromeos/ash/components/network/network_state.h"
- #include "chromeos/ash/components/network/network_state_handler.h"
- #include "chromeos/ash/components/network/network_util.h"
- #include "dbus/object_path.h"
- #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
- #include "url/gurl.h"
- namespace ash::cellular_setup {
- namespace {
- OtaActivatorImpl::Factory* g_test_factory = nullptr;
- void OnModemResetError(const std::string& error_name,
- const std::string& error_message) {
- NET_LOG(ERROR) << "ShillDeviceClient::Reset() failed. " << error_name << ": "
- << error_message << ".";
- }
- } // namespace
- // static
- std::unique_ptr<OtaActivator> OtaActivatorImpl::Factory::Create(
- mojo::PendingRemote<mojom::ActivationDelegate> activation_delegate,
- base::OnceClosure on_finished_callback,
- NetworkStateHandler* network_state_handler,
- NetworkConnectionHandler* network_connection_handler,
- NetworkActivationHandler* network_activation_handler,
- scoped_refptr<base::TaskRunner> task_runner) {
- if (g_test_factory) {
- return g_test_factory->CreateInstance(
- std::move(activation_delegate), std::move(on_finished_callback),
- network_state_handler, network_connection_handler,
- network_activation_handler, std::move(task_runner));
- }
- return base::WrapUnique(new OtaActivatorImpl(
- std::move(activation_delegate), std::move(on_finished_callback),
- network_state_handler, network_connection_handler,
- network_activation_handler, std::move(task_runner)));
- }
- // static
- void OtaActivatorImpl::Factory::SetFactoryForTesting(Factory* test_factory) {
- g_test_factory = test_factory;
- }
- OtaActivatorImpl::Factory::~Factory() = default;
- // static
- const base::TimeDelta OtaActivatorImpl::kConnectRetryDelay = base::Seconds(3);
- // static
- const size_t OtaActivatorImpl::kMaxConnectRetryAttempt = 3;
- OtaActivatorImpl::OtaActivatorImpl(
- mojo::PendingRemote<mojom::ActivationDelegate> activation_delegate,
- base::OnceClosure on_finished_callback,
- NetworkStateHandler* network_state_handler,
- NetworkConnectionHandler* network_connection_handler,
- NetworkActivationHandler* network_activation_handler,
- scoped_refptr<base::TaskRunner> task_runner)
- : OtaActivator(std::move(on_finished_callback)),
- activation_delegate_(std::move(activation_delegate)),
- network_state_handler_(network_state_handler),
- network_connection_handler_(network_connection_handler),
- network_activation_handler_(network_activation_handler) {
- task_runner->PostTask(FROM_HERE,
- base::BindOnce(&OtaActivatorImpl::StartActivation,
- weak_ptr_factory_.GetWeakPtr()));
- }
- OtaActivatorImpl::~OtaActivatorImpl() {
- // If this object is being deleted but it never finished the flow, consider
- // this a failure.
- if (state_ != State::kFinished)
- FinishActivationAttempt(mojom::ActivationResult::kFailedToActivate);
- }
- void OtaActivatorImpl::OnCarrierPortalStatusChange(
- mojom::CarrierPortalStatus status) {
- if (last_carrier_portal_status_) {
- NET_LOG(USER) << "OtaActivatorImpl: Carrier portal status updated. "
- << *last_carrier_portal_status_ << " => " << status;
- } else {
- NET_LOG(USER) << "OtaActivatorImpl: Carrier portal status updated. "
- << "Status: " << status;
- }
- last_carrier_portal_status_ = status;
- AttemptNextActivationStep();
- }
- void OtaActivatorImpl::NetworkListChanged() {
- AttemptNextActivationStep();
- }
- void OtaActivatorImpl::DeviceListChanged() {
- AttemptNextActivationStep();
- }
- void OtaActivatorImpl::NetworkPropertiesUpdated(const NetworkState* network) {
- AttemptNextActivationStep();
- }
- void OtaActivatorImpl::DevicePropertiesUpdated(const DeviceState* device) {
- AttemptNextActivationStep();
- }
- void OtaActivatorImpl::OnShuttingDown() {
- // |network_state_handler_| is shutting down before activation was able to
- // complete.
- FinishActivationAttempt(mojom::ActivationResult::kFailedToActivate);
- }
- const DeviceState* OtaActivatorImpl::GetCellularDeviceState() const {
- return network_state_handler_->GetDeviceStateByType(
- NetworkTypePattern::Cellular());
- }
- const NetworkState* OtaActivatorImpl::GetCellularNetworkState() const {
- NetworkStateHandler::NetworkStateList network_list;
- network_state_handler_->GetVisibleNetworkListByType(
- NetworkTypePattern::Cellular(), &network_list);
- for (const NetworkState* network_state : network_list) {
- if (network_state->iccid() == iccid_) {
- return network_state;
- }
- }
- return nullptr;
- }
- void OtaActivatorImpl::StartActivation() {
- network_state_handler_->AddObserver(this, FROM_HERE);
- // If |activation_delegate_| becomes disconnected, the activation request is
- // considered canceled.
- activation_delegate_.set_disconnect_handler(base::BindOnce(
- &OtaActivatorImpl::FinishActivationAttempt, base::Unretained(this),
- mojom::ActivationResult::kFailedToActivate));
- ChangeStateAndAttemptNextStep(State::kWaitingForValidSimToBecomePresent);
- }
- void OtaActivatorImpl::ChangeStateAndAttemptNextStep(State state) {
- DCHECK_NE(state, state_);
- NET_LOG(DEBUG) << "OtaActivatorImpl: " << state_ << " => " << state << ".";
- state_ = state;
- AttemptNextActivationStep();
- }
- void OtaActivatorImpl::AttemptNextActivationStep() {
- switch (state_) {
- case State::kNotYetStarted:
- // The flow either has not yet started; nothing to do.
- break;
- case State::kWaitingForValidSimToBecomePresent:
- AttemptToDiscoverSim();
- break;
- case State::kWaitingForCellularConnection:
- AttemptConnectionToCellularNetwork();
- break;
- case State::kWaitingForCellularPayment:
- AttemptToSendMetadataToDelegate();
- break;
- case State::kWaitingForActivation:
- AttemptToCompleteActivation();
- break;
- case State::kFinished:
- InvokeOnFinishedCallback();
- break;
- }
- }
- void OtaActivatorImpl::FinishActivationAttempt(
- mojom::ActivationResult activation_result) {
- DCHECK(network_state_handler_);
- network_state_handler_->RemoveObserver(this, FROM_HERE);
- network_state_handler_ = nullptr;
- NET_LOG(EVENT) << "Finished attempt with result " << activation_result << ".";
- base::UmaHistogramEnumeration("Network.Cellular.PSim.OtaActivationResult",
- activation_result);
- if (activation_delegate_)
- activation_delegate_->OnActivationFinished(activation_result);
- ChangeStateAndAttemptNextStep(State::kFinished);
- }
- void OtaActivatorImpl::AttemptToDiscoverSim() {
- DCHECK(state_ == State::kWaitingForValidSimToBecomePresent);
- const DeviceState* cellular_device = GetCellularDeviceState();
- // If the Cellular device is not present, either this machine does not support
- // cellular connections or the modem on the device is in the process of
- // restarting.
- if (!cellular_device)
- return;
- // Find status of first available pSIM slot.
- // Note: We currently only support setting up devices with single physical
- // SIM slots. This excludes other configurations such as multiple physical
- // SIM slots and SIM cards in external dongles.
- bool has_psim_slots = false;
- for (const CellularSIMSlotInfo& sim_slot_info :
- GetSimSlotInfosWithUpdatedEid(cellular_device)) {
- if (sim_slot_info.eid.empty()) {
- has_psim_slots = true;
- iccid_ = sim_slot_info.iccid;
- break;
- }
- }
- if (!has_psim_slots) {
- NET_LOG(ERROR) << "No PSim slots found";
- FinishActivationAttempt(mojom::ActivationResult::kFailedToActivate);
- return;
- }
- // If no SIM card is present, it may be due to the fact that some devices do
- // not have hardware support for determining whether a SIM has been inserted.
- // Restart the modem to see if the SIM is detected when the modem powers back
- // on.
- if (iccid_.empty()) {
- NET_LOG(DEBUG) << "No SIM detected; restarting modem.";
- ShillDeviceClient::Get()->Reset(
- dbus::ObjectPath(cellular_device->path()),
- base::BindOnce(&OtaActivatorImpl::AttemptNextActivationStep,
- weak_ptr_factory_.GetWeakPtr()),
- base::BindOnce(&OnModemResetError));
- return;
- }
- ChangeStateAndAttemptNextStep(State::kWaitingForCellularConnection);
- }
- void OtaActivatorImpl::AttemptConnectionToCellularNetwork() {
- DCHECK(state_ == State::kWaitingForCellularConnection);
- const NetworkState* cellular_network = GetCellularNetworkState();
- // There is no cellular network to be connected; return early and wait for
- // NetworkListChanged() to be called if/when one becomes available.
- if (!cellular_network)
- return;
- // The network is disconnected; trigger a connection and wait for
- // NetworkPropertiesUpdated() to be called when the network connects.
- if (!cellular_network->IsConnectingOrConnected()) {
- if (connect_retry_timer_.IsRunning()) {
- return;
- }
- network_connection_handler_->ConnectToNetwork(
- cellular_network->path(), base::DoNothing(),
- base::BindOnce(&OtaActivatorImpl::OnNetworkConnectionError,
- weak_ptr_factory_.GetWeakPtr()),
- false /* check_error_state */, ConnectCallbackMode::ON_COMPLETED);
- return;
- }
- // The network is connecting; return early and wait for
- // NetworkPropertiesUpdated() to be called if/when the network connects.
- if (cellular_network->IsConnectingState())
- return;
- connect_retry_timer_.Stop();
- // If the network is already activated, there is no need to complete the rest
- // of the flow.
- if (cellular_network->activation_state() ==
- shill::kActivationStateActivated) {
- FinishActivationAttempt(mojom::ActivationResult::kAlreadyActivated);
- return;
- }
- const DeviceState* cellular_device = GetCellularDeviceState();
- // The device must have the properties required for the actication flow;
- // namely, the operator name and IMEI. Return and wait to see if
- // DevicePropertiesUpdated() is invoked with valid properties.
- if (cellular_device->operator_name().empty() ||
- cellular_device->imei().empty()) {
- NET_LOG(DEBUG) << "Insufficient activation data: "
- << "Operator name: " << cellular_device->operator_name()
- << ", IMEI: " << cellular_device->imei();
- return;
- }
- // The network must have payment information; at minimum, a payment URL is
- // required in order to contact the carrier payment portal. Return and wait to
- // see if NetworkPropertiesUpdated() is invoked with valid properties.
- if (cellular_network->payment_url().empty()) {
- NET_LOG(DEBUG) << "Insufficient activation data: "
- << "Payment URL: " << cellular_network->payment_url() << ", "
- << "Post Data: " << cellular_network->payment_post_data();
- return;
- }
- ChangeStateAndAttemptNextStep(State::kWaitingForCellularPayment);
- }
- void OtaActivatorImpl::AttemptToSendMetadataToDelegate() {
- DCHECK(state_ == State::kWaitingForCellularPayment);
- // Metadata should only be sent to the delegate once.
- if (!has_sent_metadata_) {
- has_sent_metadata_ = true;
- const DeviceState* cellular_device = GetCellularDeviceState();
- const NetworkState* cellular_network = GetCellularNetworkState();
- NET_LOG(DEBUG) << "Sending CellularMetadata. "
- << "Payment URL: " << cellular_network->payment_url() << ", "
- << "Post data: " << cellular_network->payment_post_data()
- << ", Carrier: " << cellular_device->operator_name() << ", "
- << "MEID: " << cellular_device->meid() << ", "
- << "IMEI: " << cellular_device->imei() << ", "
- << "MDN: " << cellular_device->mdn();
- activation_delegate_->OnActivationStarted(mojom::CellularMetadata::New(
- GURL(cellular_network->payment_url()),
- cellular_network->payment_post_data(), cellular_device->operator_name(),
- cellular_device->meid(), cellular_device->imei(),
- cellular_device->mdn()));
- }
- // The user must successfully pay via the carrier portal before continuing.
- // The carrier portal may also load but fail to notify the UI of payment
- // success. The UI will send kPortalLoadedButErrorOccurredDuringPayment
- // in this case. Optimistically complete activation in case the user did
- // complete payment.
- if (last_carrier_portal_status_ !=
- mojom::CarrierPortalStatus::kPortalLoadedAndUserCompletedPayment &&
- last_carrier_portal_status_ !=
- mojom::CarrierPortalStatus::
- kPortalLoadedButErrorOccurredDuringPayment) {
- return;
- }
- ChangeStateAndAttemptNextStep(State::kWaitingForActivation);
- }
- void OtaActivatorImpl::AttemptToCompleteActivation() {
- DCHECK(state_ == State::kWaitingForActivation);
- // CompleteActivation() should only be called once.
- if (has_called_complete_activation_)
- return;
- has_called_complete_activation_ = true;
- network_activation_handler_->CompleteActivation(
- GetCellularNetworkState()->path(),
- base::BindOnce(&OtaActivatorImpl::FinishActivationAttempt,
- weak_ptr_factory_.GetWeakPtr(),
- mojom::ActivationResult::kSuccessfullyStartedActivation),
- base::BindOnce(&OtaActivatorImpl::OnCompleteActivationError,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void OtaActivatorImpl::OnCompleteActivationError(
- const std::string& error_name) {
- NET_LOG(ERROR) << "CompleteActivation() failed. Error name: " << error_name;
- FinishActivationAttempt(mojom::ActivationResult::kFailedToActivate);
- }
- void OtaActivatorImpl::OnNetworkConnectionError(const std::string& error_name) {
- if (connect_retry_attempts_ >= kMaxConnectRetryAttempt) {
- NET_LOG(ERROR) << "Reached max connection retry attempts.";
- FinishActivationAttempt(mojom::ActivationResult::kFailedToActivate);
- return;
- }
- base::TimeDelta retry_delay =
- kConnectRetryDelay * (1 << connect_retry_attempts_);
- connect_retry_attempts_++;
- NET_LOG(DEBUG) << "Network connect for activation failed. Error="
- << error_name << ". Retrying in " << retry_delay;
- connect_retry_timer_.Start(
- FROM_HERE, retry_delay,
- base::BindOnce(&OtaActivatorImpl::AttemptNextActivationStep,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void OtaActivatorImpl::FlushForTesting() {
- if (activation_delegate_)
- activation_delegate_.FlushForTesting();
- }
- std::ostream& operator<<(std::ostream& stream,
- const OtaActivatorImpl::State& state) {
- switch (state) {
- case OtaActivatorImpl::State::kNotYetStarted:
- stream << "[Not yet started]";
- break;
- case OtaActivatorImpl::State::kWaitingForValidSimToBecomePresent:
- stream << "[Waiting for SIM to become present]";
- break;
- case OtaActivatorImpl::State::kWaitingForCellularConnection:
- stream << "[Waiting for connected cellular network]";
- break;
- case OtaActivatorImpl::State::kWaitingForCellularPayment:
- stream << "[Waiting cellular payment payment to complete]";
- break;
- case OtaActivatorImpl::State::kWaitingForActivation:
- stream << "[Waiting for Shill activation to complete]";
- break;
- case OtaActivatorImpl::State::kFinished:
- stream << "[Finished]";
- break;
- }
- return stream;
- }
- } // namespace ash::cellular_setup
|