123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- // Copyright 2020 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/components/phonehub/feature_status_provider_impl.h"
- #include <utility>
- #include "ash/components/multidevice/logging/logging.h"
- #include "ash/components/multidevice/remote_device_ref.h"
- #include "ash/components/multidevice/software_feature.h"
- #include "ash/components/multidevice/software_feature_state.h"
- #include "base/bind.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/task/task_traits.h"
- #include "base/task/thread_pool.h"
- #include "device/bluetooth/bluetooth_adapter_factory.h"
- namespace ash {
- namespace phonehub {
- namespace {
- using multidevice::RemoteDeviceRef;
- using multidevice::RemoteDeviceRefList;
- using multidevice::SoftwareFeature;
- using multidevice::SoftwareFeatureState;
- using multidevice_setup::mojom::Feature;
- using multidevice_setup::mojom::FeatureState;
- using multidevice_setup::mojom::HostStatus;
- bool IsEligiblePhoneHubHost(const RemoteDeviceRef& device) {
- // Device must be capable of being a multi-device host.
- if (device.GetSoftwareFeatureState(SoftwareFeature::kBetterTogetherHost) ==
- SoftwareFeatureState::kNotSupported) {
- return false;
- }
- if (device.GetSoftwareFeatureState(SoftwareFeature::kPhoneHubHost) ==
- SoftwareFeatureState::kNotSupported) {
- return false;
- }
- // Device must have a synced Bluetooth public address, which is used to
- // bootstrap Phone Hub connections.
- return !device.bluetooth_public_address().empty();
- }
- bool IsEligibleForFeature(
- const absl::optional<multidevice::RemoteDeviceRef>& local_device,
- multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice host_status,
- const RemoteDeviceRefList& remote_devices,
- FeatureState feature_state) {
- // If the feature is prohibited by policy, we don't initialize Phone Hub
- // classes at all. But, there is an edge case where a user session starts up
- // normally, then an administrator prohibits the policy during the user
- // session. If this occurs, we consider the session ineligible for using Phone
- // Hub.
- if (feature_state == FeatureState::kProhibitedByPolicy)
- return false;
- // If the local device has not yet been enrolled, no phone can serve as its
- // Phone Hub host.
- if (!local_device)
- return false;
- // If the local device does not support being a Phone Hub client, no phone can
- // serve as its host.
- if (local_device->GetSoftwareFeatureState(SoftwareFeature::kPhoneHubClient) ==
- SoftwareFeatureState::kNotSupported) {
- return false;
- }
- // If the local device does not have an enrolled Bluetooth address, no phone
- // can serve as its host.
- if (local_device->bluetooth_public_address().empty())
- return false;
- // If the host device is not an eligible host, do not initialize Phone Hub.
- if (host_status.first == HostStatus::kNoEligibleHosts)
- return false;
- // If there is a host device available, check if the device is eligible for
- // Phonehub.
- if (host_status.second.has_value())
- return IsEligiblePhoneHubHost(*(host_status.second));
- // Otherwise, check if there is any available remote device that is
- // eligible for Phonehub.
- for (const RemoteDeviceRef& device : remote_devices) {
- if (IsEligiblePhoneHubHost(device))
- return true;
- }
- // If none of the devices return true above, there are no phones capable of
- // Phone Hub connections on the account.
- return false;
- }
- bool IsPhonePendingSetup(HostStatus host_status, FeatureState feature_state) {
- // The user has completed the opt-in flow, but we have not yet notified the
- // back-end of this selection. One common cause of this state is when the user
- // completes setup while offline.
- if (host_status ==
- HostStatus::kHostSetLocallyButWaitingForBackendConfirmation) {
- return true;
- }
- // The device has been set up with the back-end, but the phone has not yet
- // enabled itself.
- if (host_status == HostStatus::kHostSetButNotYetVerified)
- return true;
- // The phone has enabled itself for the multi-device suite but has not yet
- // enabled itself for Phone Hub. Note that kNotSupportedByPhone is a bit of a
- // misnomer here; this value means that the phone has advertised support for
- // the feature but has not yet enabled it.
- return host_status == HostStatus::kHostVerified &&
- feature_state == FeatureState::kNotSupportedByPhone;
- }
- bool IsFeatureDisabledByUser(FeatureState feature_state) {
- return feature_state == FeatureState::kDisabledByUser ||
- feature_state == FeatureState::kUnavailableSuiteDisabled ||
- feature_state == FeatureState::kUnavailableTopLevelFeatureDisabled;
- }
- } // namespace
- FeatureStatusProviderImpl::FeatureStatusProviderImpl(
- device_sync::DeviceSyncClient* device_sync_client,
- multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
- secure_channel::ConnectionManager* connection_manager,
- session_manager::SessionManager* session_manager,
- PowerManagerClient* power_manager_client)
- : device_sync_client_(device_sync_client),
- multidevice_setup_client_(multidevice_setup_client),
- connection_manager_(connection_manager),
- session_manager_(session_manager),
- power_manager_client_(power_manager_client) {
- DCHECK(session_manager_);
- DCHECK(power_manager_client_);
- device_sync_client_->AddObserver(this);
- multidevice_setup_client_->AddObserver(this);
- connection_manager_->AddObserver(this);
- session_manager_->AddObserver(this);
- power_manager_client_->AddObserver(this);
- device::BluetoothAdapterFactory::Get()->GetAdapter(
- base::BindOnce(&FeatureStatusProviderImpl::OnBluetoothAdapterReceived,
- weak_ptr_factory_.GetWeakPtr()));
- status_ = ComputeStatus();
- }
- FeatureStatusProviderImpl::~FeatureStatusProviderImpl() {
- device_sync_client_->RemoveObserver(this);
- multidevice_setup_client_->RemoveObserver(this);
- connection_manager_->RemoveObserver(this);
- if (bluetooth_adapter_)
- bluetooth_adapter_->RemoveObserver(this);
- session_manager_->RemoveObserver(this);
- power_manager_client_->RemoveObserver(this);
- }
- FeatureStatus FeatureStatusProviderImpl::GetStatus() const {
- PA_LOG(VERBOSE) << __func__ << ": status = " << *status_;
- return *status_;
- }
- void FeatureStatusProviderImpl::OnReady() {
- UpdateStatus();
- }
- void FeatureStatusProviderImpl::OnNewDevicesSynced() {
- UpdateStatus();
- }
- void FeatureStatusProviderImpl::OnHostStatusChanged(
- const multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice&
- host_device_with_status) {
- UpdateStatus();
- }
- void FeatureStatusProviderImpl::OnFeatureStatesChanged(
- const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
- feature_states_map) {
- UpdateStatus();
- }
- void FeatureStatusProviderImpl::AdapterPresentChanged(
- device::BluetoothAdapter* adapter,
- bool present) {
- UpdateStatus();
- }
- void FeatureStatusProviderImpl::AdapterPoweredChanged(
- device::BluetoothAdapter* adapter,
- bool powered) {
- UpdateStatus();
- }
- void FeatureStatusProviderImpl::OnBluetoothAdapterReceived(
- scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) {
- bluetooth_adapter_ = std::move(bluetooth_adapter);
- bluetooth_adapter_->AddObserver(this);
- // If |status_| has not yet been set, this call occurred synchronously in the
- // constructor, so status_ has not yet been initialized.
- if (status_.has_value())
- UpdateStatus();
- }
- void FeatureStatusProviderImpl::OnConnectionStatusChanged() {
- UpdateStatus();
- }
- void FeatureStatusProviderImpl::OnSessionStateChanged() {
- UpdateStatus();
- }
- void FeatureStatusProviderImpl::UpdateStatus() {
- DCHECK(status_.has_value());
- FeatureStatus computed_status = ComputeStatus();
- if (computed_status == *status_)
- return;
- PA_LOG(INFO) << "Phone Hub feature status: " << *status_ << " => "
- << computed_status;
- *status_ = computed_status;
- NotifyStatusChanged();
- UMA_HISTOGRAM_ENUMERATION("PhoneHub.Adoption.FeatureStatusChangesSinceLogin",
- GetStatus());
- }
- FeatureStatus FeatureStatusProviderImpl::ComputeStatus() {
- FeatureState feature_state =
- multidevice_setup_client_->GetFeatureState(Feature::kPhoneHub);
- HostStatus host_status = multidevice_setup_client_->GetHostStatus().first;
- // Note: If |device_sync_client_| is not yet ready, it has not initialized
- // itself with device metadata, so we assume that we are ineligible for the
- // feature until proven otherwise.
- if (!device_sync_client_->is_ready() ||
- !IsEligibleForFeature(device_sync_client_->GetLocalDeviceMetadata(),
- multidevice_setup_client_->GetHostStatus(),
- device_sync_client_->GetSyncedDevices(),
- feature_state)) {
- return FeatureStatus::kNotEligibleForFeature;
- }
- if (session_manager_->IsScreenLocked() || is_suspended_)
- return FeatureStatus::kLockOrSuspended;
- if (host_status == HostStatus::kEligibleHostExistsButNoHostSet)
- return FeatureStatus::kEligiblePhoneButNotSetUp;
- if (IsPhonePendingSetup(host_status, feature_state))
- return FeatureStatus::kPhoneSelectedAndPendingSetup;
- if (IsFeatureDisabledByUser(feature_state))
- return FeatureStatus::kDisabled;
- if (!IsBluetoothOn())
- return FeatureStatus::kUnavailableBluetoothOff;
- switch (connection_manager_->GetStatus()) {
- case secure_channel::ConnectionManager::Status::kDisconnected:
- return FeatureStatus::kEnabledButDisconnected;
- case secure_channel::ConnectionManager::Status::kConnecting:
- return FeatureStatus::kEnabledAndConnecting;
- case secure_channel::ConnectionManager::Status::kConnected:
- return FeatureStatus::kEnabledAndConnected;
- }
- return FeatureStatus::kEnabledButDisconnected;
- }
- bool FeatureStatusProviderImpl::IsBluetoothOn() const {
- if (!bluetooth_adapter_)
- return false;
- return bluetooth_adapter_->IsPresent() && bluetooth_adapter_->IsPowered();
- }
- void FeatureStatusProviderImpl::SuspendImminent(
- power_manager::SuspendImminent::Reason reason) {
- PA_LOG(INFO) << "Device is suspending";
- is_suspended_ = true;
- UpdateStatus();
- }
- void FeatureStatusProviderImpl::SuspendDone(base::TimeDelta sleep_duration) {
- PA_LOG(INFO) << "Device has stopped suspending";
- is_suspended_ = false;
- UpdateStatus();
- }
- } // namespace phonehub
- } // namespace ash
|