| // Copyright 2017 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 "chromeos/components/tether/crash_recovery_manager_impl.h" |
| |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/weak_ptr.h" |
| #include "chromeos/components/multidevice/logging/logging.h" |
| #include "chromeos/components/multidevice/remote_device_ref.h" |
| #include "chromeos/components/tether/host_scan_cache.h" |
| #include "chromeos/network/network_state.h" |
| #include "chromeos/network/network_state_handler.h" |
| |
| namespace chromeos { |
| |
| namespace tether { |
| |
| // static |
| CrashRecoveryManagerImpl::Factory* |
| CrashRecoveryManagerImpl::Factory::factory_instance_ = nullptr; |
| |
| // static |
| std::unique_ptr<CrashRecoveryManager> |
| CrashRecoveryManagerImpl::Factory::NewInstance( |
| NetworkStateHandler* network_state_handler, |
| ActiveHost* active_host, |
| HostScanCache* host_scan_cache) { |
| if (!factory_instance_) |
| factory_instance_ = new Factory(); |
| |
| return factory_instance_->BuildInstance(network_state_handler, active_host, |
| host_scan_cache); |
| } |
| |
| // static |
| void CrashRecoveryManagerImpl::Factory::SetInstanceForTesting( |
| Factory* factory) { |
| factory_instance_ = factory; |
| } |
| |
| CrashRecoveryManagerImpl::Factory::~Factory() = default; |
| |
| std::unique_ptr<CrashRecoveryManager> |
| CrashRecoveryManagerImpl::Factory::BuildInstance( |
| NetworkStateHandler* network_state_handler, |
| ActiveHost* active_host, |
| HostScanCache* host_scan_cache) { |
| return base::WrapUnique(new CrashRecoveryManagerImpl( |
| network_state_handler, active_host, host_scan_cache)); |
| } |
| |
| CrashRecoveryManagerImpl::CrashRecoveryManagerImpl( |
| NetworkStateHandler* network_state_handler, |
| ActiveHost* active_host, |
| HostScanCache* host_scan_cache) |
| : network_state_handler_(network_state_handler), |
| active_host_(active_host), |
| host_scan_cache_(host_scan_cache), |
| weak_ptr_factory_(this) {} |
| |
| CrashRecoveryManagerImpl::~CrashRecoveryManagerImpl() = default; |
| |
| void CrashRecoveryManagerImpl::RestorePreCrashStateIfNecessary( |
| const base::Closure& on_restoration_finished) { |
| ActiveHost::ActiveHostStatus status = active_host_->GetActiveHostStatus(); |
| std::string active_host_device_id = active_host_->GetActiveHostDeviceId(); |
| std::string wifi_network_guid = active_host_->GetWifiNetworkGuid(); |
| std::string tether_network_guid = active_host_->GetTetherNetworkGuid(); |
| |
| if (status == ActiveHost::ActiveHostStatus::DISCONNECTED) { |
| // There was no active Tether session, so either the last TetherComponent |
| // shutdown occurred normally (i.e., without a crash), or it occurred due |
| // to a crash and there was no active host at the time of the crash. |
| on_restoration_finished.Run(); |
| return; |
| } |
| |
| if (status == ActiveHost::ActiveHostStatus::CONNECTING) { |
| // If a connection attempt was in progress when the crash occurred, abandon |
| // the connection attempt. Set ActiveHost back to DISCONNECTED; the user can |
| // attempt another connection if desired. |
| // TODO(khorimoto): Explore whether we should attempt to restore an |
| // in-progress connection attempt. This is a low-priority edge case which is |
| // difficult to solve. |
| PA_LOG(WARNING) << "Browser crashed while Tether connection attempt was in " |
| << "progress. Abandoning connection attempt."; |
| active_host_->SetActiveHostDisconnected(); |
| on_restoration_finished.Run(); |
| return; |
| } |
| |
| RestoreConnectedState(on_restoration_finished, active_host_device_id, |
| tether_network_guid, wifi_network_guid); |
| } |
| |
| void CrashRecoveryManagerImpl::RestoreConnectedState( |
| const base::Closure& on_restoration_finished, |
| const std::string& active_host_device_id, |
| const std::string& tether_network_guid, |
| const std::string& wifi_network_guid) { |
| // Since the host was connected, both a Wi-Fi and Tether network GUID are |
| // expected to be present. |
| DCHECK(!wifi_network_guid.empty() && !tether_network_guid.empty()); |
| |
| if (!host_scan_cache_->ExistsInCache(tether_network_guid)) { |
| // If a crash occurred, HostScanCache is expected to have restored its state |
| // via its persistent scan results. If that did not happen correctly, there |
| // is no way to restore the active host, so abandon the connection. Note |
| // that this is not an expected error condition. |
| PA_LOG(ERROR) |
| << "Browser crashed while a Tether connection was active, " |
| << "but the scan result for the active host was lost. Setting " |
| << "the active host to DISCONNECTED."; |
| active_host_->SetActiveHostDisconnected(); |
| on_restoration_finished.Run(); |
| return; |
| } |
| |
| // Since the associated scan result exists in the cache, it is expected to be |
| // present in the network stack. |
| const NetworkState* tether_state = |
| network_state_handler_->GetNetworkStateFromGuid(tether_network_guid); |
| DCHECK(tether_state); |
| |
| const NetworkState* wifi_state = |
| network_state_handler_->GetNetworkStateFromGuid(wifi_network_guid); |
| if (!wifi_state || !wifi_state->IsConnectedState()) { |
| // If the Wi-Fi network corresponding to the Tether hotspot is not present |
| // or is no longer connected, then this device is no longer truly connected |
| // to the active host. |
| PA_LOG(ERROR) << "Browser crashed while a Tether connection was active, " |
| << "but underlying Wi-Fi network corresponding to the Tether " |
| << "connection is no longer present. Setting the active host " |
| << "to DISCONNECTED."; |
| active_host_->SetActiveHostDisconnected(); |
| on_restoration_finished.Run(); |
| return; |
| } |
| |
| // Because the NetworkState object representing the Wi-Fi network was lost |
| // during the crash, the association between it and the Tether NetworkState |
| // has been broken. Restore it now. |
| DCHECK(wifi_state->tether_guid().empty()); |
| network_state_handler_->AssociateTetherNetworkStateWithWifiNetwork( |
| tether_network_guid, wifi_network_guid); |
| |
| active_host_->GetActiveHost( |
| base::Bind(&CrashRecoveryManagerImpl::OnActiveHostFetched, |
| weak_ptr_factory_.GetWeakPtr(), on_restoration_finished)); |
| } |
| |
| void CrashRecoveryManagerImpl::OnActiveHostFetched( |
| const base::Closure& on_restoration_finished, |
| ActiveHost::ActiveHostStatus active_host_status, |
| base::Optional<multidevice::RemoteDeviceRef> active_host, |
| const std::string& tether_network_guid, |
| const std::string& wifi_network_guid) { |
| DCHECK(ActiveHost::ActiveHostStatus::CONNECTED == active_host_status); |
| DCHECK(active_host_); |
| |
| // Even though the active host has not actually changed, fire an active host |
| // changed update so that classes listening on ActiveHost (e.g., |
| // ActiveHostNetworkStateUpdater and KeepAliveScheduler) will be notified |
| // that there is an active connection. |
| // |
| // Note: SendActiveHostChangedUpdate() is a protected function of ActiveHost |
| // which is only invoked here because CrashRecoveryManagerImpl is a friend |
| // class. |
| // It is invoked directly here because we are sending out a "fake" change |
| // event which has equal old and new values. |
| active_host_->SendActiveHostChangedUpdate( |
| ActiveHost::ActiveHostStatus::CONNECTED /* old_status */, |
| active_host ? active_host->GetDeviceId() |
| : std::string() /* old_active_host_id */, |
| tether_network_guid /* old_tether_network_guid */, |
| wifi_network_guid /* old_wifi_network_guid */, |
| ActiveHost::ActiveHostStatus::CONNECTED /* new_status */, |
| active_host /* new_active_host */, |
| tether_network_guid /* new_tether_network_guid */, |
| wifi_network_guid /* new_wifi_network_guid */); |
| on_restoration_finished.Run(); |
| } |
| |
| } // namespace tether |
| |
| } // namespace chromeos |