chromium/chrome/browser/ash/tether/tether_service.h

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_TETHER_TETHER_SERVICE_H_
#define CHROME_BROWSER_ASH_TETHER_TETHER_SERVICE_H_

#include <memory>

#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/network_state_handler_observer.h"
#include "chromeos/ash/components/tether/tether_component.h"
#include "chromeos/ash/components/tether/tether_host_fetcher.h"
#include "chromeos/ash/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "components/keyed_service/core/keyed_service.h"
#include "device/bluetooth/bluetooth_adapter.h"

class Profile;

namespace session_manager {
class SessionManager;
}  // namespace session_manager

namespace user_prefs {
class PrefRegistrySyncable;
}  // namespace user_prefs

namespace ash {

namespace secure_channel {
class SecureChannelClient;
}  // namespace secure_channel

namespace tether {

class GmsCoreNotificationsStateTracker;
class GmsCoreNotificationsStateTrackerImpl;
class NotificationPresenter;

// Service providing access to the Instant Tethering component. Provides an
// interface to start up the component as well as to retrieve metadata about
// ongoing Tether connections.
//
// This service starts up when the user logs in (or recovers from a crash) and
// is shut down when the user logs out.
class TetherService
    : public KeyedService,
      public chromeos::PowerManagerClient::Observer,
      public TetherHostFetcher::Observer,
      public device::BluetoothAdapter::Observer,
      public NetworkStateHandlerObserver,
      public TetherComponent::Observer,
      public device_sync::DeviceSyncClient::Observer,
      public multidevice_setup::MultiDeviceSetupClient::Observer {
 public:
  TetherService(
      Profile* profile,
      chromeos::PowerManagerClient* power_manager_client,
      device_sync::DeviceSyncClient* device_sync_client,
      secure_channel::SecureChannelClient* secure_channel_client,
      multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
      session_manager::SessionManager* session_manager);
  TetherService(const TetherService&) = delete;
  TetherService& operator=(const TetherService&) = delete;
  ~TetherService() override;

  // Gets TetherService instance.
  static TetherService* Get(Profile* profile);

  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);

  // Attempt to start the Tether module. Only succeeds if all conditions to
  // reach NetworkStateHandler::TechnologyState::ENABLED are reached.
  // Should only be called once a user is logged in.
  virtual void StartTetherIfPossible();

  virtual GmsCoreNotificationsStateTracker*
  GetGmsCoreNotificationsStateTracker();

 protected:
  // KeyedService:
  void Shutdown() override;

  // chromeos::PowerManagerClient::Observer:
  void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
  void SuspendDone(base::TimeDelta sleep_duration) override;

  // TetherHostFetcher::Observer
  void OnTetherHostUpdated() override;

  // device::BluetoothAdapter::Observer:
  void AdapterPoweredChanged(device::BluetoothAdapter* adapter,
                             bool powered) override;

  // NetworkStateHandlerObserver:
  void DeviceListChanged() override;
  void DevicePropertiesUpdated(const DeviceState* device) override;

  // Helper method called from NetworkStateHandlerObserver methods.
  void UpdateEnabledState();

  // TetherComponent::Observer:
  void OnShutdownComplete() override;

  // ash::device_sync::DeviceSyncClient::Observer:
  void OnReady() override;

  // ash::multidevice_setup::MultiDeviceSetupClient::Observer:
  void OnFeatureStatesChanged(
      const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
          feature_states_map) override;

  // Stop the Tether module if it is currently enabled; if it was not enabled,
  // this function is a no-op.
  virtual void StopTetherIfNecessary();

  // Whether Tether hosts are available.
  virtual bool HasSyncedTetherHosts() const;

  virtual void UpdateTetherTechnologyState();
  NetworkStateHandler::TechnologyState GetTetherTechnologyState();

  NetworkStateHandler* network_state_handler() {
    return network_state_handler_;
  }

 private:
  friend class TetherServiceTest;
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestSuspend);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestDeviceSyncClientNotReady);
  FRIEND_TEST_ALL_PREFIXES(
      TetherServiceTest,
      TestMultiDeviceSetupClientInitiallyHasNoVerifiedHost);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest,
                           TestMultiDeviceSetupClientLosesVerifiedHost);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest,
                           TestBetterTogetherSuiteInitiallyDisabled);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest,
                           TestBetterTogetherSuiteBecomesDisabled);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest,
                           TestGet_PrimaryUser_FeatureFlagEnabled);
  FRIEND_TEST_ALL_PREFIXES(
      TetherServiceTest,
      TestGet_PrimaryUser_FeatureFlagEnabled_MultiDeviceApiFlagEnabled);
  FRIEND_TEST_ALL_PREFIXES(
      TetherServiceTest,
      TestGet_PrimaryUser_FeatureFlagEnabled_MultiDeviceApiAndMultiDeviceSetupFlagsEnabled);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestNoTetherHosts);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestProhibitedByPolicy);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestIsBluetoothPowered);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestCellularIsUnavailable);
  FRIEND_TEST_ALL_PREFIXES(
      TetherServiceTest,
      TestCellularIsAvailable_InstantHotspotRebrandDisabled);
  FRIEND_TEST_ALL_PREFIXES(
      TetherServiceTest,
      TestCellularIsAvailable_InstantHotspotRebrandEnabled);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestDisabled);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestEnabled);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest,
                           TestUserPrefChangesViaFeatureStateChange);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest,
                           TestUserPrefChangesViaTechnologyStateChange);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestBluetoothNotification);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestBluetoothNotPresent);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestMetricsFalsePositives);
  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestWifiNotPresent);

  // Reflects InstantTethering_FeatureState enum in enums.xml. Do not rearrange.
  enum TetherFeatureState {
    // Note: Value 0 was previously OTHER_OR_UNKNOWN, but this was a vague
    // description.
    SHUT_DOWN = 0,
    // Note: Value 1 was previously BLE_ADVERTISING_NOT_SUPPORTED, but this
    // value is obsolete and should no longer be used.
    // Note: Value 2 was previously SCREEN_LOCKED, but this value is obsolete
    // and should no longer be used.
    NO_AVAILABLE_HOSTS = 3,
    CELLULAR_DISABLED = 4,
    PROHIBITED = 5,
    BLUETOOTH_DISABLED = 6,
    USER_PREFERENCE_DISABLED = 7,
    ENABLED = 8,
    BLE_NOT_PRESENT = 9,
    WIFI_NOT_PRESENT = 10,
    SUSPENDED = 11,
    BETTER_TOGETHER_SUITE_DISABLED = 12,
    TETHER_FEATURE_STATE_MAX
  };

  // For debug logs.
  static std::string TetherFeatureStateToString(
      const TetherFeatureState& state);

  void GetBluetoothAdapter();
  void OnBluetoothAdapterFetched(
      scoped_refptr<device::BluetoothAdapter> adapter);

  bool IsBluetoothPresent() const;
  bool IsBluetoothPowered() const;

  bool IsWifiPresent() const;

  bool IsCellularAvailableButNotEnabled() const;

  // Whether Tether is allowed to be used. If the controlling preference
  // is set (from policy), this returns the preference value. Otherwise, it is
  // permitted if the flag is enabled.
  bool IsAllowedByPolicy() const;

  // Whether Tether is enabled.
  bool IsEnabledByPreference() const;

  TetherFeatureState GetTetherFeatureState();

  // Record to UMA Tether's current feature state.
  void RecordTetherFeatureState();

  // Attempt to record the current Tether FeatureState.
  void RecordTetherFeatureStateIfPossible();

  // Handles potential false positive metric states which may occur normally
  // during startup. In the normal case (i.e., when Tether is enabled), the
  // state transitions from OTHER_OR_UNKNOWN -> BLE_NOT_PRESENT ->
  // NO_AVAILABLE_HOSTS -> ENABLED, but we do not wish to log metrics for the
  // intermediate states (BLE_NOT_PRESENT or NO_AVAILABLE_HOSTS), since these
  // are ephemeral. Returns whether a false positive case was handled.
  bool HandleFeatureStateMetricIfUninitialized();

  void LogUserPreferenceChanged(bool is_now_enabled);

  void SetTestDoubles(
      std::unique_ptr<NotificationPresenter> notification_presenter,
      std::unique_ptr<base::OneShotTimer> timer);

  // Whether the service has been shut down.
  bool shut_down_ = false;

  // Whether the device and service have been suspended (e.g. the laptop lid
  // was closed).
  bool suspended_ = false;

  bool is_adapter_being_fetched_ = false;

  multidevice_setup::mojom::HostStatus host_status_ =
      multidevice_setup::mojom::HostStatus::kNoEligibleHosts;

  // The first report of TetherFeatureState::BLE_NOT_PRESENT is usually
  // incorrect and hence is a false positive. This property tracks if the first
  // report has been hit yet.
  bool ble_not_present_false_positive_encountered_ = false;

  // The first report of TetherFeatureState::NO_AVAILABLE_HOSTS may be incorrect
  // and hence a false positive. This property tracks if the first report has
  // been hit yet.
  bool no_available_hosts_false_positive_encountered_ = false;

  // The TetherFeatureState obtained the last time that
  // GetTetherTechnologyState() was called. Used only for logging purposes.
  TetherFeatureState previous_feature_state_ =
      TetherFeatureState::TETHER_FEATURE_STATE_MAX;

  raw_ptr<Profile> profile_;
  raw_ptr<chromeos::PowerManagerClient> power_manager_client_;
  raw_ptr<device_sync::DeviceSyncClient, DanglingUntriaged> device_sync_client_;
  raw_ptr<secure_channel::SecureChannelClient> secure_channel_client_;
  raw_ptr<multidevice_setup::MultiDeviceSetupClient, DanglingUntriaged>
      multidevice_setup_client_;
  raw_ptr<NetworkStateHandler> network_state_handler_;
  base::ScopedObservation<NetworkStateHandler, NetworkStateHandlerObserver>
      network_state_handler_observer_{this};
  raw_ptr<session_manager::SessionManager> session_manager_;
  std::unique_ptr<NotificationPresenter> notification_presenter_;
  std::unique_ptr<GmsCoreNotificationsStateTrackerImpl>
      gms_core_notifications_state_tracker_;
  std::unique_ptr<TetherHostFetcher> tether_host_fetcher_;
  std::unique_ptr<TetherComponent> tether_component_;

  scoped_refptr<device::BluetoothAdapter> adapter_;
  std::unique_ptr<base::OneShotTimer> timer_;

  base::WeakPtrFactory<TetherService> weak_ptr_factory_{this};
};

}  // namespace tether
}  // namespace ash

#endif  // CHROME_BROWSER_ASH_TETHER_TETHER_SERVICE_H_