feature_status_provider_impl.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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/feature_status_provider_impl.h"
  5. #include <utility>
  6. #include "ash/components/multidevice/logging/logging.h"
  7. #include "ash/components/multidevice/remote_device_ref.h"
  8. #include "ash/components/multidevice/software_feature.h"
  9. #include "ash/components/multidevice/software_feature_state.h"
  10. #include "base/bind.h"
  11. #include "base/metrics/histogram_macros.h"
  12. #include "base/task/task_traits.h"
  13. #include "base/task/thread_pool.h"
  14. #include "device/bluetooth/bluetooth_adapter_factory.h"
  15. namespace ash {
  16. namespace phonehub {
  17. namespace {
  18. using multidevice::RemoteDeviceRef;
  19. using multidevice::RemoteDeviceRefList;
  20. using multidevice::SoftwareFeature;
  21. using multidevice::SoftwareFeatureState;
  22. using multidevice_setup::mojom::Feature;
  23. using multidevice_setup::mojom::FeatureState;
  24. using multidevice_setup::mojom::HostStatus;
  25. bool IsEligiblePhoneHubHost(const RemoteDeviceRef& device) {
  26. // Device must be capable of being a multi-device host.
  27. if (device.GetSoftwareFeatureState(SoftwareFeature::kBetterTogetherHost) ==
  28. SoftwareFeatureState::kNotSupported) {
  29. return false;
  30. }
  31. if (device.GetSoftwareFeatureState(SoftwareFeature::kPhoneHubHost) ==
  32. SoftwareFeatureState::kNotSupported) {
  33. return false;
  34. }
  35. // Device must have a synced Bluetooth public address, which is used to
  36. // bootstrap Phone Hub connections.
  37. return !device.bluetooth_public_address().empty();
  38. }
  39. bool IsEligibleForFeature(
  40. const absl::optional<multidevice::RemoteDeviceRef>& local_device,
  41. multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice host_status,
  42. const RemoteDeviceRefList& remote_devices,
  43. FeatureState feature_state) {
  44. // If the feature is prohibited by policy, we don't initialize Phone Hub
  45. // classes at all. But, there is an edge case where a user session starts up
  46. // normally, then an administrator prohibits the policy during the user
  47. // session. If this occurs, we consider the session ineligible for using Phone
  48. // Hub.
  49. if (feature_state == FeatureState::kProhibitedByPolicy)
  50. return false;
  51. // If the local device has not yet been enrolled, no phone can serve as its
  52. // Phone Hub host.
  53. if (!local_device)
  54. return false;
  55. // If the local device does not support being a Phone Hub client, no phone can
  56. // serve as its host.
  57. if (local_device->GetSoftwareFeatureState(SoftwareFeature::kPhoneHubClient) ==
  58. SoftwareFeatureState::kNotSupported) {
  59. return false;
  60. }
  61. // If the local device does not have an enrolled Bluetooth address, no phone
  62. // can serve as its host.
  63. if (local_device->bluetooth_public_address().empty())
  64. return false;
  65. // If the host device is not an eligible host, do not initialize Phone Hub.
  66. if (host_status.first == HostStatus::kNoEligibleHosts)
  67. return false;
  68. // If there is a host device available, check if the device is eligible for
  69. // Phonehub.
  70. if (host_status.second.has_value())
  71. return IsEligiblePhoneHubHost(*(host_status.second));
  72. // Otherwise, check if there is any available remote device that is
  73. // eligible for Phonehub.
  74. for (const RemoteDeviceRef& device : remote_devices) {
  75. if (IsEligiblePhoneHubHost(device))
  76. return true;
  77. }
  78. // If none of the devices return true above, there are no phones capable of
  79. // Phone Hub connections on the account.
  80. return false;
  81. }
  82. bool IsPhonePendingSetup(HostStatus host_status, FeatureState feature_state) {
  83. // The user has completed the opt-in flow, but we have not yet notified the
  84. // back-end of this selection. One common cause of this state is when the user
  85. // completes setup while offline.
  86. if (host_status ==
  87. HostStatus::kHostSetLocallyButWaitingForBackendConfirmation) {
  88. return true;
  89. }
  90. // The device has been set up with the back-end, but the phone has not yet
  91. // enabled itself.
  92. if (host_status == HostStatus::kHostSetButNotYetVerified)
  93. return true;
  94. // The phone has enabled itself for the multi-device suite but has not yet
  95. // enabled itself for Phone Hub. Note that kNotSupportedByPhone is a bit of a
  96. // misnomer here; this value means that the phone has advertised support for
  97. // the feature but has not yet enabled it.
  98. return host_status == HostStatus::kHostVerified &&
  99. feature_state == FeatureState::kNotSupportedByPhone;
  100. }
  101. bool IsFeatureDisabledByUser(FeatureState feature_state) {
  102. return feature_state == FeatureState::kDisabledByUser ||
  103. feature_state == FeatureState::kUnavailableSuiteDisabled ||
  104. feature_state == FeatureState::kUnavailableTopLevelFeatureDisabled;
  105. }
  106. } // namespace
  107. FeatureStatusProviderImpl::FeatureStatusProviderImpl(
  108. device_sync::DeviceSyncClient* device_sync_client,
  109. multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
  110. secure_channel::ConnectionManager* connection_manager,
  111. session_manager::SessionManager* session_manager,
  112. PowerManagerClient* power_manager_client)
  113. : device_sync_client_(device_sync_client),
  114. multidevice_setup_client_(multidevice_setup_client),
  115. connection_manager_(connection_manager),
  116. session_manager_(session_manager),
  117. power_manager_client_(power_manager_client) {
  118. DCHECK(session_manager_);
  119. DCHECK(power_manager_client_);
  120. device_sync_client_->AddObserver(this);
  121. multidevice_setup_client_->AddObserver(this);
  122. connection_manager_->AddObserver(this);
  123. session_manager_->AddObserver(this);
  124. power_manager_client_->AddObserver(this);
  125. device::BluetoothAdapterFactory::Get()->GetAdapter(
  126. base::BindOnce(&FeatureStatusProviderImpl::OnBluetoothAdapterReceived,
  127. weak_ptr_factory_.GetWeakPtr()));
  128. status_ = ComputeStatus();
  129. }
  130. FeatureStatusProviderImpl::~FeatureStatusProviderImpl() {
  131. device_sync_client_->RemoveObserver(this);
  132. multidevice_setup_client_->RemoveObserver(this);
  133. connection_manager_->RemoveObserver(this);
  134. if (bluetooth_adapter_)
  135. bluetooth_adapter_->RemoveObserver(this);
  136. session_manager_->RemoveObserver(this);
  137. power_manager_client_->RemoveObserver(this);
  138. }
  139. FeatureStatus FeatureStatusProviderImpl::GetStatus() const {
  140. PA_LOG(VERBOSE) << __func__ << ": status = " << *status_;
  141. return *status_;
  142. }
  143. void FeatureStatusProviderImpl::OnReady() {
  144. UpdateStatus();
  145. }
  146. void FeatureStatusProviderImpl::OnNewDevicesSynced() {
  147. UpdateStatus();
  148. }
  149. void FeatureStatusProviderImpl::OnHostStatusChanged(
  150. const multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice&
  151. host_device_with_status) {
  152. UpdateStatus();
  153. }
  154. void FeatureStatusProviderImpl::OnFeatureStatesChanged(
  155. const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
  156. feature_states_map) {
  157. UpdateStatus();
  158. }
  159. void FeatureStatusProviderImpl::AdapterPresentChanged(
  160. device::BluetoothAdapter* adapter,
  161. bool present) {
  162. UpdateStatus();
  163. }
  164. void FeatureStatusProviderImpl::AdapterPoweredChanged(
  165. device::BluetoothAdapter* adapter,
  166. bool powered) {
  167. UpdateStatus();
  168. }
  169. void FeatureStatusProviderImpl::OnBluetoothAdapterReceived(
  170. scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) {
  171. bluetooth_adapter_ = std::move(bluetooth_adapter);
  172. bluetooth_adapter_->AddObserver(this);
  173. // If |status_| has not yet been set, this call occurred synchronously in the
  174. // constructor, so status_ has not yet been initialized.
  175. if (status_.has_value())
  176. UpdateStatus();
  177. }
  178. void FeatureStatusProviderImpl::OnConnectionStatusChanged() {
  179. UpdateStatus();
  180. }
  181. void FeatureStatusProviderImpl::OnSessionStateChanged() {
  182. UpdateStatus();
  183. }
  184. void FeatureStatusProviderImpl::UpdateStatus() {
  185. DCHECK(status_.has_value());
  186. FeatureStatus computed_status = ComputeStatus();
  187. if (computed_status == *status_)
  188. return;
  189. PA_LOG(INFO) << "Phone Hub feature status: " << *status_ << " => "
  190. << computed_status;
  191. *status_ = computed_status;
  192. NotifyStatusChanged();
  193. UMA_HISTOGRAM_ENUMERATION("PhoneHub.Adoption.FeatureStatusChangesSinceLogin",
  194. GetStatus());
  195. }
  196. FeatureStatus FeatureStatusProviderImpl::ComputeStatus() {
  197. FeatureState feature_state =
  198. multidevice_setup_client_->GetFeatureState(Feature::kPhoneHub);
  199. HostStatus host_status = multidevice_setup_client_->GetHostStatus().first;
  200. // Note: If |device_sync_client_| is not yet ready, it has not initialized
  201. // itself with device metadata, so we assume that we are ineligible for the
  202. // feature until proven otherwise.
  203. if (!device_sync_client_->is_ready() ||
  204. !IsEligibleForFeature(device_sync_client_->GetLocalDeviceMetadata(),
  205. multidevice_setup_client_->GetHostStatus(),
  206. device_sync_client_->GetSyncedDevices(),
  207. feature_state)) {
  208. return FeatureStatus::kNotEligibleForFeature;
  209. }
  210. if (session_manager_->IsScreenLocked() || is_suspended_)
  211. return FeatureStatus::kLockOrSuspended;
  212. if (host_status == HostStatus::kEligibleHostExistsButNoHostSet)
  213. return FeatureStatus::kEligiblePhoneButNotSetUp;
  214. if (IsPhonePendingSetup(host_status, feature_state))
  215. return FeatureStatus::kPhoneSelectedAndPendingSetup;
  216. if (IsFeatureDisabledByUser(feature_state))
  217. return FeatureStatus::kDisabled;
  218. if (!IsBluetoothOn())
  219. return FeatureStatus::kUnavailableBluetoothOff;
  220. switch (connection_manager_->GetStatus()) {
  221. case secure_channel::ConnectionManager::Status::kDisconnected:
  222. return FeatureStatus::kEnabledButDisconnected;
  223. case secure_channel::ConnectionManager::Status::kConnecting:
  224. return FeatureStatus::kEnabledAndConnecting;
  225. case secure_channel::ConnectionManager::Status::kConnected:
  226. return FeatureStatus::kEnabledAndConnected;
  227. }
  228. return FeatureStatus::kEnabledButDisconnected;
  229. }
  230. bool FeatureStatusProviderImpl::IsBluetoothOn() const {
  231. if (!bluetooth_adapter_)
  232. return false;
  233. return bluetooth_adapter_->IsPresent() && bluetooth_adapter_->IsPowered();
  234. }
  235. void FeatureStatusProviderImpl::SuspendImminent(
  236. power_manager::SuspendImminent::Reason reason) {
  237. PA_LOG(INFO) << "Device is suspending";
  238. is_suspended_ = true;
  239. UpdateStatus();
  240. }
  241. void FeatureStatusProviderImpl::SuspendDone(base::TimeDelta sleep_duration) {
  242. PA_LOG(INFO) << "Device has stopped suspending";
  243. is_suspended_ = false;
  244. UpdateStatus();
  245. }
  246. } // namespace phonehub
  247. } // namespace ash