tether_controller_impl.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. // Copyright 2020 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. #include "ash/components/phonehub/tether_controller_impl.h"
  5. #include "ash/components/multidevice/logging/logging.h"
  6. #include "ash/components/phonehub/phone_status_model.h"
  7. #include "ash/components/phonehub/user_action_recorder.h"
  8. #include "ash/components/phonehub/util/histogram_util.h"
  9. #include "chromeos/services/network_config/in_process_instance.h"
  10. namespace ash {
  11. namespace phonehub {
  12. namespace {
  13. using ::chromeos::network_config::mojom::ConnectionStateType;
  14. using ::chromeos::network_config::mojom::DeviceStatePropertiesPtr;
  15. using ::chromeos::network_config::mojom::FilterType;
  16. using ::chromeos::network_config::mojom::NetworkType;
  17. using ::chromeos::network_config::mojom::StartConnectResult;
  18. using multidevice_setup::MultiDeviceSetupClient;
  19. using multidevice_setup::mojom::Feature;
  20. using multidevice_setup::mojom::FeatureState;
  21. // TODO(https://crbug.com/1164001): remove after migrating to namespace ash.
  22. namespace network_config = ::chromeos::network_config;
  23. } // namespace
  24. TetherControllerImpl::TetherNetworkConnector::TetherNetworkConnector() {
  25. chromeos::network_config::BindToInProcessInstance(
  26. cros_network_config_.BindNewPipeAndPassReceiver());
  27. }
  28. TetherControllerImpl::TetherNetworkConnector::~TetherNetworkConnector() =
  29. default;
  30. void TetherControllerImpl::TetherNetworkConnector::StartConnect(
  31. const std::string& guid,
  32. StartConnectCallback callback) {
  33. cros_network_config_->StartConnect(guid, std::move(callback));
  34. }
  35. void TetherControllerImpl::TetherNetworkConnector::StartDisconnect(
  36. const std::string& guid,
  37. StartDisconnectCallback callback) {
  38. cros_network_config_->StartDisconnect(guid, std::move(callback));
  39. }
  40. void TetherControllerImpl::TetherNetworkConnector::GetNetworkStateList(
  41. network_config::mojom::NetworkFilterPtr filter,
  42. GetNetworkStateListCallback callback) {
  43. cros_network_config_->GetNetworkStateList(std::move(filter),
  44. std::move(callback));
  45. }
  46. TetherControllerImpl::TetherControllerImpl(
  47. PhoneModel* phone_model,
  48. UserActionRecorder* user_action_recorder,
  49. MultiDeviceSetupClient* multidevice_setup_client)
  50. : TetherControllerImpl(
  51. phone_model,
  52. user_action_recorder,
  53. multidevice_setup_client,
  54. std::make_unique<TetherControllerImpl::TetherNetworkConnector>()) {}
  55. TetherControllerImpl::TetherControllerImpl(
  56. PhoneModel* phone_model,
  57. UserActionRecorder* user_action_recorder,
  58. MultiDeviceSetupClient* multidevice_setup_client,
  59. std::unique_ptr<TetherControllerImpl::TetherNetworkConnector> connector)
  60. : phone_model_(phone_model),
  61. user_action_recorder_(user_action_recorder),
  62. multidevice_setup_client_(multidevice_setup_client),
  63. connector_(std::move(connector)) {
  64. // Receive updates when devices (e.g., Tether, Ethernet, Wi-Fi) go on/offline
  65. // This class only cares about Tether devices.
  66. chromeos::network_config::BindToInProcessInstance(
  67. cros_network_config_.BindNewPipeAndPassReceiver());
  68. cros_network_config_->AddObserver(receiver_.BindNewPipeAndPassRemote());
  69. phone_model_->AddObserver(this);
  70. multidevice_setup_client_->AddObserver(this);
  71. // Compute current status.
  72. status_ = ComputeStatus();
  73. // Load the current tether network if it exists.
  74. FetchVisibleTetherNetwork();
  75. }
  76. TetherControllerImpl::~TetherControllerImpl() {
  77. phone_model_->RemoveObserver(this);
  78. multidevice_setup_client_->RemoveObserver(this);
  79. }
  80. TetherController::Status TetherControllerImpl::GetStatus() const {
  81. PA_LOG(VERBOSE) << __func__ << ": status = " << status_;
  82. return status_;
  83. }
  84. void TetherControllerImpl::ScanForAvailableConnection() {
  85. if (status_ != Status::kConnectionUnavailable) {
  86. PA_LOG(WARNING) << "Received request to scan for available connection, but "
  87. << "a scan cannot be performed because the current status "
  88. << "is " << status_;
  89. return;
  90. }
  91. PA_LOG(INFO) << "Scanning for available connection.";
  92. cros_network_config_->RequestNetworkScan(NetworkType::kTether);
  93. }
  94. void TetherControllerImpl::AttemptConnection() {
  95. if (status_ != Status::kConnectionUnavailable &&
  96. status_ != Status::kConnectionAvailable) {
  97. PA_LOG(WARNING) << "Received request to attempt a connection, but a "
  98. << "connection cannot be attempted because the current "
  99. << "status is " << status_;
  100. return;
  101. }
  102. PA_LOG(INFO) << "Attempting connection; current status is " << status_;
  103. user_action_recorder_->RecordTetherConnectionAttempt();
  104. util::LogTetherConnectionResult(
  105. util::TetherConnectionResult::kAttemptConnection);
  106. is_attempting_connection_ = true;
  107. FeatureState feature_state =
  108. multidevice_setup_client_->GetFeatureState(Feature::kInstantTethering);
  109. if (feature_state == FeatureState::kEnabledByUser) {
  110. PerformConnectionAttempt();
  111. return;
  112. }
  113. // The Tethering feature was disabled and must be enabled first, before a
  114. // connection attempt can be made.
  115. DCHECK(feature_state == FeatureState::kDisabledByUser);
  116. AttemptTurningOnTethering();
  117. }
  118. void TetherControllerImpl::AttemptTurningOnTethering() {
  119. SetConnectDisconnectStatus(
  120. ConnectDisconnectStatus::kTurningOnInstantTethering);
  121. multidevice_setup_client_->SetFeatureEnabledState(
  122. Feature::kInstantTethering,
  123. /*enabled=*/true,
  124. /*auth_token=*/absl::nullopt,
  125. base::BindOnce(&TetherControllerImpl::OnSetFeatureEnabled,
  126. weak_ptr_factory_.GetWeakPtr()));
  127. }
  128. void TetherControllerImpl::OnSetFeatureEnabled(bool success) {
  129. if (connect_disconnect_status_ !=
  130. ConnectDisconnectStatus::kTurningOnInstantTethering) {
  131. return;
  132. }
  133. if (success) {
  134. PerformConnectionAttempt();
  135. return;
  136. }
  137. PA_LOG(WARNING) << "Failed to enable InstantTethering";
  138. SetConnectDisconnectStatus(ConnectDisconnectStatus::kIdle);
  139. }
  140. void TetherControllerImpl::OnFeatureStatesChanged(
  141. const MultiDeviceSetupClient::FeatureStatesMap& feature_states_map) {
  142. FeatureState feature_state =
  143. multidevice_setup_client_->GetFeatureState(Feature::kInstantTethering);
  144. // The |connect_disconnect_status_| should always be
  145. // ConnectDisconnectStatus::kIdle if the |feature_state| is anything other
  146. // than |FeatureState::kEnabledByUser|. A |feature_status| other than
  147. // |FeatureState::kEnabledByUser| would indicate that Instant Tethering became
  148. // disabled or disallowed.
  149. if (feature_state != FeatureState::kEnabledByUser) {
  150. SetConnectDisconnectStatus(ConnectDisconnectStatus::kIdle);
  151. } else if (connect_disconnect_status_ !=
  152. ConnectDisconnectStatus::kTurningOnInstantTethering) {
  153. UpdateStatus();
  154. }
  155. }
  156. void TetherControllerImpl::PerformConnectionAttempt() {
  157. if (!tether_network_.is_null()) {
  158. StartConnect();
  159. return;
  160. }
  161. SetConnectDisconnectStatus(
  162. ConnectDisconnectStatus::kScanningForEligiblePhone);
  163. cros_network_config_->RequestNetworkScan(NetworkType::kTether);
  164. }
  165. void TetherControllerImpl::StartConnect() {
  166. DCHECK(!tether_network_.is_null());
  167. SetConnectDisconnectStatus(
  168. ConnectDisconnectStatus::kConnectingToEligiblePhone);
  169. connector_->StartConnect(
  170. tether_network_->guid,
  171. base::BindOnce(&TetherControllerImpl::OnStartConnectCompleted,
  172. weak_ptr_factory_.GetWeakPtr()));
  173. }
  174. void TetherControllerImpl::OnStartConnectCompleted(StartConnectResult result,
  175. const std::string& message) {
  176. if (result != StartConnectResult::kSuccess) {
  177. PA_LOG(WARNING) << "Start connect failed with result " << result
  178. << " and message " << message;
  179. }
  180. if (connect_disconnect_status_ !=
  181. ConnectDisconnectStatus::kConnectingToEligiblePhone) {
  182. return;
  183. }
  184. // Note that OnVisibleTetherNetworkFetched() may not have called
  185. // SetConnectDisconnectStatus() with kIdle at this point, so this should go
  186. // ahead and do it.
  187. SetConnectDisconnectStatus(ConnectDisconnectStatus::kIdle);
  188. }
  189. void TetherControllerImpl::Disconnect() {
  190. if (status_ != Status::kConnecting && status_ != Status::kConnected) {
  191. PA_LOG(WARNING) << "Received request to disconnect, but no connection or "
  192. << "connection attempt is in progress. Current status is "
  193. << status_;
  194. return;
  195. }
  196. // If |status_| is Status::kConnecting, a tether network may not be available
  197. // yet e.g this class may be in the process of enabling Instant Tethering.
  198. if (tether_network_.is_null()) {
  199. SetConnectDisconnectStatus(ConnectDisconnectStatus::kIdle);
  200. return;
  201. }
  202. PA_LOG(INFO) << "Attempting disconnection; current status is " << status_;
  203. SetConnectDisconnectStatus(ConnectDisconnectStatus::kDisconnecting);
  204. connector_->StartDisconnect(
  205. tether_network_->guid,
  206. base::BindOnce(&TetherControllerImpl::OnDisconnectCompleted,
  207. weak_ptr_factory_.GetWeakPtr()));
  208. }
  209. void TetherControllerImpl::OnModelChanged() {
  210. UpdateStatus();
  211. }
  212. void TetherControllerImpl::OnDisconnectCompleted(bool success) {
  213. if (connect_disconnect_status_ != ConnectDisconnectStatus::kDisconnecting)
  214. return;
  215. SetConnectDisconnectStatus(ConnectDisconnectStatus::kIdle);
  216. // Fetch the tether network and its updated connection state, if it exists.
  217. // By the time OnDisconnectCompleted() is called, the connection state is
  218. // properly updated to ConnectionStateType::kDisconnected, so a fetch may be
  219. // necessary to promptly update |tether_network_|, as neither
  220. // OnActiveNetworksChanged() nor OnNetworkStateListChanged() may be called
  221. // shortly afterwards with the latest network information.
  222. FetchVisibleTetherNetwork();
  223. if (!success)
  224. PA_LOG(WARNING) << "Failed to disconnect tether network";
  225. }
  226. void TetherControllerImpl::OnActiveNetworksChanged(
  227. std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks) {
  228. // Active networks either changed externally (e.g via OS Settings or a new
  229. // actve network added), or as a result of a call to AttemptConnection() or
  230. // Disconnect(). This is needed for the case of
  231. // ConnectionStateType::kConnecting in ComputeStatus().
  232. //
  233. // Note: When OnActiveNetworksChanged() is called shortly after starting a
  234. // disconnect to a ConnectionStateType::kConnecting |tether_network_|, the
  235. // |tether_network_|'s ConnectionStateType may still remain in the
  236. // ConnectionStateType::kConnecting state. This may happen if on the phone,
  237. // hotspot is off but bluetooth tethering is on, and a connection attempt is
  238. // made, but the user does not acknowledge the notification to connect on
  239. // their phone, and subsequently decides to disconnect while
  240. // |tether_network_|'s ConnectionStateType is still
  241. // ConnectionStateType::kConnecting.
  242. FetchVisibleTetherNetwork();
  243. }
  244. void TetherControllerImpl::OnNetworkStateListChanged() {
  245. // Any network change whether caused externally or within this class should
  246. // be reflected to the state of this class (e.g user makes changes to Tether
  247. // network in OS Settings).
  248. FetchVisibleTetherNetwork();
  249. }
  250. void TetherControllerImpl::OnDeviceStateListChanged() {
  251. if (connect_disconnect_status_ !=
  252. ConnectDisconnectStatus::kScanningForEligiblePhone) {
  253. return;
  254. }
  255. cros_network_config_->GetDeviceStateList(
  256. base::BindOnce(&TetherControllerImpl::OnGetDeviceStateList,
  257. weak_ptr_factory_.GetWeakPtr()));
  258. }
  259. void TetherControllerImpl::OnGetDeviceStateList(
  260. std::vector<DeviceStatePropertiesPtr> devices) {
  261. if (connect_disconnect_status_ !=
  262. ConnectDisconnectStatus::kScanningForEligiblePhone) {
  263. return;
  264. }
  265. // There should only be one Tether device in the list.
  266. bool is_tether_device_scanning = false;
  267. for (const auto& device : devices) {
  268. NetworkType type = device->type;
  269. if (type != NetworkType::kTether)
  270. continue;
  271. is_tether_device_scanning = device->scanning;
  272. break;
  273. }
  274. if (!is_tether_device_scanning) {
  275. NotifyAttemptConnectionScanFailed();
  276. SetConnectDisconnectStatus(ConnectDisconnectStatus::kIdle);
  277. }
  278. }
  279. void TetherControllerImpl::FetchVisibleTetherNetwork() {
  280. // Return the connected, connecting, or connectable Tether network.
  281. connector_->GetNetworkStateList(
  282. network_config::mojom::NetworkFilter::New(FilterType::kVisible,
  283. NetworkType::kTether,
  284. /*limit=*/0),
  285. base::BindOnce(&TetherControllerImpl::OnVisibleTetherNetworkFetched,
  286. weak_ptr_factory_.GetWeakPtr()));
  287. }
  288. void TetherControllerImpl::OnVisibleTetherNetworkFetched(
  289. std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks) {
  290. network_config::mojom::NetworkStatePropertiesPtr previous_tether_network =
  291. std::move(tether_network_);
  292. if (!networks.empty()) {
  293. // The number of tether networks is expected to be at most 1, though some
  294. // tests do use multiple networks.
  295. tether_network_ = std::move(networks[0]);
  296. } else {
  297. tether_network_ = nullptr;
  298. }
  299. // No observeable changes to the tether network specifically. This fetch
  300. // was initiated by a change in a non Tether network type.
  301. if (tether_network_.Equals(previous_tether_network))
  302. return;
  303. // If AttemptConnection() was called when Instant Tethering was disabled.
  304. // The feature must be enabled before scanning can occur.
  305. if (connect_disconnect_status_ ==
  306. ConnectDisconnectStatus::kTurningOnInstantTethering) {
  307. UpdateStatus();
  308. return;
  309. }
  310. // If AttemptConnection() was called when there was no available tether
  311. // connection.
  312. if (connect_disconnect_status_ ==
  313. ConnectDisconnectStatus::kScanningForEligiblePhone &&
  314. !tether_network_.is_null()) {
  315. StartConnect();
  316. return;
  317. }
  318. // If there is no attempt connection in progress, or an attempt connection
  319. // caused OnVisibleTetherNetworkFetched() to be fired. This case also occurs
  320. // in the event that Tethering settings are changed externally from this class
  321. // (e.g user connects via Settings).
  322. SetConnectDisconnectStatus(ConnectDisconnectStatus::kIdle);
  323. }
  324. void TetherControllerImpl::SetConnectDisconnectStatus(
  325. ConnectDisconnectStatus connect_disconnect_status) {
  326. if (connect_disconnect_status_ != connect_disconnect_status)
  327. weak_ptr_factory_.InvalidateWeakPtrs();
  328. connect_disconnect_status_ = connect_disconnect_status;
  329. UpdateStatus();
  330. }
  331. void TetherControllerImpl::UpdateStatus() {
  332. Status status = ComputeStatus();
  333. if (status_ == status)
  334. return;
  335. PA_LOG(INFO) << "TetherController status update: " << status_ << " => "
  336. << status;
  337. status_ = status;
  338. if (is_attempting_connection_ && status_ == Status::kConnected)
  339. util::LogTetherConnectionResult(util::TetherConnectionResult::kSuccess);
  340. if (status_ != Status::kConnecting)
  341. is_attempting_connection_ = false;
  342. NotifyStatusChanged();
  343. }
  344. TetherController::Status TetherControllerImpl::ComputeStatus() const {
  345. FeatureState feature_state =
  346. multidevice_setup_client_->GetFeatureState(Feature::kInstantTethering);
  347. if (feature_state != FeatureState::kDisabledByUser &&
  348. feature_state != FeatureState::kEnabledByUser) {
  349. // Tethering may be for instance, prohibited by policy or not supported
  350. // by the phone or Chromebook.
  351. return Status::kIneligibleForFeature;
  352. }
  353. if (phone_model_->phone_status_model().has_value()) {
  354. // If the phone status exists, and it indicates that the phone
  355. // does not have reception, the status becomes no kNoReception.
  356. bool does_sim_exist_with_reception =
  357. phone_model_->phone_status_model()->mobile_status() ==
  358. PhoneStatusModel::MobileStatus::kSimWithReception;
  359. if (!does_sim_exist_with_reception)
  360. return Status::kNoReception;
  361. }
  362. if (connect_disconnect_status_ ==
  363. ConnectDisconnectStatus::kTurningOnInstantTethering ||
  364. connect_disconnect_status_ ==
  365. ConnectDisconnectStatus::kScanningForEligiblePhone ||
  366. connect_disconnect_status_ ==
  367. ConnectDisconnectStatus::kConnectingToEligiblePhone) {
  368. return Status::kConnecting;
  369. }
  370. if (feature_state == FeatureState::kDisabledByUser)
  371. return Status::kConnectionUnavailable;
  372. if (tether_network_.is_null())
  373. return Status::kConnectionUnavailable;
  374. ConnectionStateType connection_state = tether_network_->connection_state;
  375. switch (connection_state) {
  376. case ConnectionStateType::kOnline:
  377. [[fallthrough]];
  378. case ConnectionStateType::kConnected:
  379. [[fallthrough]];
  380. case ConnectionStateType::kPortal:
  381. return Status::kConnected;
  382. case ConnectionStateType::kConnecting:
  383. return Status::kConnecting;
  384. case ConnectionStateType::kNotConnected:
  385. return Status::kConnectionAvailable;
  386. }
  387. return Status::kConnectionUnavailable;
  388. }
  389. } // namespace phonehub
  390. } // namespace ash