1 // Copyright 2018 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 
5 #include "chromeos/services/multidevice_setup/host_status_provider_impl.h"
6 
7 #include <algorithm>
8 
9 #include "base/memory/ptr_util.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "chromeos/components/multidevice/logging/logging.h"
12 #include "chromeos/services/multidevice_setup/eligible_host_devices_provider.h"
13 
14 namespace chromeos {
15 
16 namespace multidevice_setup {
17 
18 namespace {
19 
RecordMultiDeviceHostStatus(mojom::HostStatus host_status)20 static void RecordMultiDeviceHostStatus(mojom::HostStatus host_status) {
21   UMA_HISTOGRAM_ENUMERATION("MultiDevice.Setup.HostStatus", host_status);
22 }
23 
24 }  // namespace
25 
26 // static
27 HostStatusProviderImpl::Factory*
28     HostStatusProviderImpl::Factory::test_factory_ = nullptr;
29 
30 // static
Create(EligibleHostDevicesProvider * eligible_host_devices_provider,HostBackendDelegate * host_backend_delegate,HostVerifier * host_verifier,device_sync::DeviceSyncClient * device_sync_client)31 std::unique_ptr<HostStatusProvider> HostStatusProviderImpl::Factory::Create(
32     EligibleHostDevicesProvider* eligible_host_devices_provider,
33     HostBackendDelegate* host_backend_delegate,
34     HostVerifier* host_verifier,
35     device_sync::DeviceSyncClient* device_sync_client) {
36   if (test_factory_) {
37     return test_factory_->CreateInstance(eligible_host_devices_provider,
38                                          host_backend_delegate, host_verifier,
39                                          device_sync_client);
40   }
41 
42   return base::WrapUnique(new HostStatusProviderImpl(
43       eligible_host_devices_provider, host_backend_delegate, host_verifier,
44       device_sync_client));
45 }
46 
47 // static
SetFactoryForTesting(Factory * test_factory)48 void HostStatusProviderImpl::Factory::SetFactoryForTesting(
49     Factory* test_factory) {
50   test_factory_ = test_factory;
51 }
52 
53 HostStatusProviderImpl::Factory::~Factory() = default;
54 
HostStatusProviderImpl(EligibleHostDevicesProvider * eligible_host_devices_provider,HostBackendDelegate * host_backend_delegate,HostVerifier * host_verifier,device_sync::DeviceSyncClient * device_sync_client)55 HostStatusProviderImpl::HostStatusProviderImpl(
56     EligibleHostDevicesProvider* eligible_host_devices_provider,
57     HostBackendDelegate* host_backend_delegate,
58     HostVerifier* host_verifier,
59     device_sync::DeviceSyncClient* device_sync_client)
60     : eligible_host_devices_provider_(eligible_host_devices_provider),
61       host_backend_delegate_(host_backend_delegate),
62       host_verifier_(host_verifier),
63       device_sync_client_(device_sync_client),
64       current_status_and_device_(mojom::HostStatus::kNoEligibleHosts,
65                                  base::nullopt /* host_device */) {
66   host_backend_delegate_->AddObserver(this);
67   host_verifier_->AddObserver(this);
68   device_sync_client_->AddObserver(this);
69 
70   CheckForUpdatedStatusAndNotifyIfChanged(
71       /*force_notify_host_status_change=*/false);
72   RecordMultiDeviceHostStatus(current_status_and_device_.host_status());
73 }
74 
~HostStatusProviderImpl()75 HostStatusProviderImpl::~HostStatusProviderImpl() {
76   host_backend_delegate_->RemoveObserver(this);
77   host_verifier_->RemoveObserver(this);
78   device_sync_client_->RemoveObserver(this);
79 }
80 
81 HostStatusProvider::HostStatusWithDevice
GetHostWithStatus() const82 HostStatusProviderImpl::GetHostWithStatus() const {
83   return current_status_and_device_;
84 }
85 
OnHostChangedOnBackend()86 void HostStatusProviderImpl::OnHostChangedOnBackend() {
87   CheckForUpdatedStatusAndNotifyIfChanged(
88       /*force_notify_host_status_change=*/false);
89 }
90 
OnPendingHostRequestChange()91 void HostStatusProviderImpl::OnPendingHostRequestChange() {
92   CheckForUpdatedStatusAndNotifyIfChanged(
93       /*force_notify_host_status_change=*/false);
94 }
95 
OnHostVerified()96 void HostStatusProviderImpl::OnHostVerified() {
97   CheckForUpdatedStatusAndNotifyIfChanged(
98       /*force_notify_host_status_change=*/false);
99 }
100 
OnNewDevicesSynced()101 void HostStatusProviderImpl::OnNewDevicesSynced() {
102   CheckForUpdatedStatusAndNotifyIfChanged(
103       /*force_notify_host_status_change=*/true);
104 }
105 
CheckForUpdatedStatusAndNotifyIfChanged(bool force_notify_host_status_change)106 void HostStatusProviderImpl::CheckForUpdatedStatusAndNotifyIfChanged(
107     bool force_notify_host_status_change) {
108   HostStatusWithDevice current_status_and_device = GetCurrentStatus();
109   if (current_status_and_device == current_status_and_device_) {
110     if (force_notify_host_status_change) {
111       // If the RemoteDevice the host device references has changed, but not its
112       // contents, fire a host status change. Note that since the status doesn't
113       // actually change, neither logging nor metric collection should occur.
114       NotifyHostStatusChange(current_status_and_device_.host_status(),
115                              current_status_and_device_.host_device());
116     }
117     return;
118   }
119 
120   PA_LOG(VERBOSE) << "HostStatusProviderImpl::"
121                   << "CheckForUpdatedStatusAndNotifyIfChanged(): Host status "
122                   << "changed. New status: "
123                   << current_status_and_device.host_status() << ", Old status: "
124                   << current_status_and_device_.host_status()
125                   << ", Host device: "
126                   << (current_status_and_device.host_device()
127                           ? current_status_and_device.host_device()
128                                 ->GetInstanceIdDeviceIdForLogs()
129                           : "[no host]");
130 
131   current_status_and_device_ = current_status_and_device;
132   NotifyHostStatusChange(current_status_and_device_.host_status(),
133                          current_status_and_device_.host_device());
134   RecordMultiDeviceHostStatus(current_status_and_device_.host_status());
135 }
136 
137 HostStatusProvider::HostStatusWithDevice
GetCurrentStatus()138 HostStatusProviderImpl::GetCurrentStatus() {
139   if (host_verifier_->IsHostVerified()) {
140     return HostStatusWithDevice(
141         mojom::HostStatus::kHostVerified,
142         *host_backend_delegate_->GetMultiDeviceHostFromBackend());
143   }
144 
145   if (host_backend_delegate_->GetMultiDeviceHostFromBackend() &&
146       !host_backend_delegate_->HasPendingHostRequest()) {
147     return HostStatusWithDevice(
148         mojom::HostStatus::kHostSetButNotYetVerified,
149         *host_backend_delegate_->GetMultiDeviceHostFromBackend());
150   }
151 
152   if (host_backend_delegate_->HasPendingHostRequest() &&
153       host_backend_delegate_->GetPendingHostRequest()) {
154     return HostStatusWithDevice(
155         mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
156         *host_backend_delegate_->GetPendingHostRequest());
157   }
158 
159   if (!eligible_host_devices_provider_->GetEligibleHostDevices().empty()) {
160     return HostStatusWithDevice(
161         mojom::HostStatus::kEligibleHostExistsButNoHostSet,
162         base::nullopt /* host_device */);
163   }
164 
165   return HostStatusWithDevice(mojom::HostStatus::kNoEligibleHosts,
166                               base::nullopt /* host_device */);
167 }
168 
169 }  // namespace multidevice_setup
170 
171 }  // namespace chromeos
172