OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/proximity_auth/proximity_monitor_impl.h" | 5 #include "components/proximity_auth/proximity_monitor_impl.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/location.h" | 11 #include "base/location.h" |
12 #include "base/threading/thread_task_runner_handle.h" | 12 #include "base/threading/thread_task_runner_handle.h" |
13 #include "base/time/tick_clock.h" | 13 #include "base/time/tick_clock.h" |
14 #include "base/time/time.h" | 14 #include "base/time/time.h" |
15 #include "components/proximity_auth/logging/logging.h" | 15 #include "components/proximity_auth/logging/logging.h" |
16 #include "components/proximity_auth/metrics.h" | 16 #include "components/proximity_auth/metrics.h" |
17 #include "components/proximity_auth/proximity_monitor_observer.h" | 17 #include "components/proximity_auth/proximity_monitor_observer.h" |
18 #include "device/bluetooth/bluetooth_adapter.h" | 18 #include "device/bluetooth/bluetooth_adapter.h" |
19 #include "device/bluetooth/bluetooth_adapter_factory.h" | 19 #include "device/bluetooth/bluetooth_adapter_factory.h" |
20 | 20 |
21 using device::BluetoothDevice; | 21 using device::BluetoothDevice; |
22 | 22 |
23 namespace proximity_auth { | 23 namespace proximity_auth { |
24 | 24 |
25 // The time to wait, in milliseconds, between proximity polling iterations. | 25 // The time to wait, in milliseconds, between proximity polling iterations. |
26 const int kPollingTimeoutMs = 250; | 26 const int kPollingTimeoutMs = 250; |
27 | 27 |
28 // The RSSI threshold below which we consider the remote device to not be in | 28 // The RSSI threshold below which we consider the remote device to not be in |
29 // proximity. | 29 // proximity. |
30 const int kRssiThreshold = -5; | 30 const int kRssiThreshold = -45; |
31 | 31 |
32 // The weight of the most recent RSSI sample. | 32 // The weight of the most recent RSSI sample. |
33 const double kRssiSampleWeight = 0.3; | 33 const double kRssiSampleWeight = 0.3; |
34 | 34 |
35 ProximityMonitorImpl::ProximityMonitorImpl( | 35 ProximityMonitorImpl::ProximityMonitorImpl( |
36 const cryptauth::RemoteDevice& remote_device, | 36 cryptauth::Connection* connection, |
37 std::unique_ptr<base::TickClock> clock) | 37 std::unique_ptr<base::TickClock> clock) |
38 : remote_device_(remote_device), | 38 : connection_(connection), |
39 strategy_(Strategy::NONE), | |
40 remote_device_is_in_proximity_(false), | 39 remote_device_is_in_proximity_(false), |
41 is_active_(false), | 40 is_active_(false), |
42 clock_(std::move(clock)), | 41 clock_(std::move(clock)), |
43 polling_weak_ptr_factory_(this), | 42 polling_weak_ptr_factory_(this), |
44 weak_ptr_factory_(this) { | 43 weak_ptr_factory_(this) { |
45 if (device::BluetoothAdapterFactory::IsBluetoothSupported()) { | 44 if (device::BluetoothAdapterFactory::IsBluetoothSupported()) { |
46 device::BluetoothAdapterFactory::GetAdapter( | 45 device::BluetoothAdapterFactory::GetAdapter( |
47 base::Bind(&ProximityMonitorImpl::OnAdapterInitialized, | 46 base::Bind(&ProximityMonitorImpl::OnAdapterInitialized, |
48 weak_ptr_factory_.GetWeakPtr())); | 47 weak_ptr_factory_.GetWeakPtr())); |
49 } else { | 48 } else { |
50 PA_LOG(ERROR) << "[Proximity] Proximity monitoring unavailable: " | 49 PA_LOG(ERROR) << "[Proximity] Proximity monitoring unavailable: " |
51 << "Bluetooth is unsupported on this platform."; | 50 << "Bluetooth is unsupported on this platform."; |
52 } | 51 } |
53 | |
54 // TODO(isherman): Test prefs to set the strategy. Need to read from "Local | |
55 // State" prefs on the sign-in screen, and per-user prefs on the lock screen. | |
56 // TODO(isherman): Unlike in the JS app, destroy and recreate the proximity | |
57 // monitor when the connection state changes. | |
58 } | 52 } |
59 | 53 |
60 ProximityMonitorImpl::~ProximityMonitorImpl() { | 54 ProximityMonitorImpl::~ProximityMonitorImpl() { |
61 } | 55 } |
62 | 56 |
63 void ProximityMonitorImpl::Start() { | 57 void ProximityMonitorImpl::Start() { |
64 is_active_ = true; | 58 is_active_ = true; |
65 UpdatePollingState(); | 59 UpdatePollingState(); |
66 } | 60 } |
67 | 61 |
68 void ProximityMonitorImpl::Stop() { | 62 void ProximityMonitorImpl::Stop() { |
69 is_active_ = false; | 63 is_active_ = false; |
70 ClearProximityState(); | 64 ClearProximityState(); |
71 UpdatePollingState(); | 65 UpdatePollingState(); |
72 } | 66 } |
73 | 67 |
74 ProximityMonitor::Strategy ProximityMonitorImpl::GetStrategy() const { | |
75 return strategy_; | |
76 } | |
77 | |
78 bool ProximityMonitorImpl::IsUnlockAllowed() const { | 68 bool ProximityMonitorImpl::IsUnlockAllowed() const { |
79 return strategy_ == Strategy::NONE || remote_device_is_in_proximity_; | 69 return remote_device_is_in_proximity_; |
80 } | |
81 | |
82 bool ProximityMonitorImpl::IsInRssiRange() const { | |
83 return (strategy_ != Strategy::NONE && rssi_rolling_average_ && | |
84 *rssi_rolling_average_ > kRssiThreshold); | |
85 } | 70 } |
86 | 71 |
87 void ProximityMonitorImpl::RecordProximityMetricsOnAuthSuccess() { | 72 void ProximityMonitorImpl::RecordProximityMetricsOnAuthSuccess() { |
88 double rssi_rolling_average = rssi_rolling_average_ | 73 double rssi_rolling_average = rssi_rolling_average_ |
89 ? *rssi_rolling_average_ | 74 ? *rssi_rolling_average_ |
90 : metrics::kUnknownProximityValue; | 75 : metrics::kUnknownProximityValue; |
91 | 76 |
92 int last_transmit_power_delta = | |
93 last_transmit_power_reading_ | |
94 ? (last_transmit_power_reading_->transmit_power - | |
95 last_transmit_power_reading_->max_transmit_power) | |
96 : metrics::kUnknownProximityValue; | |
97 | |
98 // If no zero RSSI value has been read, then record an overflow. | 77 // If no zero RSSI value has been read, then record an overflow. |
99 base::TimeDelta time_since_last_zero_rssi; | 78 base::TimeDelta time_since_last_zero_rssi; |
100 if (last_zero_rssi_timestamp_) | 79 if (last_zero_rssi_timestamp_) |
101 time_since_last_zero_rssi = clock_->NowTicks() - *last_zero_rssi_timestamp_; | 80 time_since_last_zero_rssi = clock_->NowTicks() - *last_zero_rssi_timestamp_; |
102 else | 81 else |
103 time_since_last_zero_rssi = base::TimeDelta::FromDays(100); | 82 time_since_last_zero_rssi = base::TimeDelta::FromDays(100); |
104 | 83 |
105 std::string remote_device_model = metrics::kUnknownDeviceModel; | 84 std::string remote_device_model = metrics::kUnknownDeviceModel; |
106 if (remote_device_.name != remote_device_.bluetooth_address) | 85 cryptauth::RemoteDevice remote_device = connection_->remote_device(); |
107 remote_device_model = remote_device_.name; | 86 if (!remote_device.name.empty()) |
| 87 remote_device_model = remote_device.name; |
108 | 88 |
109 metrics::RecordAuthProximityRollingRssi(round(rssi_rolling_average)); | 89 metrics::RecordAuthProximityRollingRssi(round(rssi_rolling_average)); |
110 metrics::RecordAuthProximityTransmitPowerDelta(last_transmit_power_delta); | |
111 metrics::RecordAuthProximityTimeSinceLastZeroRssi(time_since_last_zero_rssi); | 90 metrics::RecordAuthProximityTimeSinceLastZeroRssi(time_since_last_zero_rssi); |
112 metrics::RecordAuthProximityRemoteDeviceModelHash(remote_device_model); | 91 metrics::RecordAuthProximityRemoteDeviceModelHash(remote_device_model); |
113 } | 92 } |
114 | 93 |
115 void ProximityMonitorImpl::AddObserver(ProximityMonitorObserver* observer) { | 94 void ProximityMonitorImpl::AddObserver(ProximityMonitorObserver* observer) { |
116 observers_.AddObserver(observer); | 95 observers_.AddObserver(observer); |
117 } | 96 } |
118 | 97 |
119 void ProximityMonitorImpl::RemoveObserver(ProximityMonitorObserver* observer) { | 98 void ProximityMonitorImpl::RemoveObserver(ProximityMonitorObserver* observer) { |
120 observers_.RemoveObserver(observer); | 99 observers_.RemoveObserver(observer); |
121 } | 100 } |
122 | 101 |
123 void ProximityMonitorImpl::SetStrategy(Strategy strategy) { | |
124 if (strategy_ == strategy) | |
125 return; | |
126 strategy_ = strategy; | |
127 CheckForProximityStateChange(); | |
128 UpdatePollingState(); | |
129 } | |
130 | |
131 ProximityMonitorImpl::TransmitPowerReading::TransmitPowerReading( | |
132 int transmit_power, | |
133 int max_transmit_power) | |
134 : transmit_power(transmit_power), max_transmit_power(max_transmit_power) { | |
135 } | |
136 | |
137 bool ProximityMonitorImpl::TransmitPowerReading::IsInProximity() const { | |
138 return transmit_power < max_transmit_power; | |
139 } | |
140 | |
141 void ProximityMonitorImpl::OnAdapterInitialized( | 102 void ProximityMonitorImpl::OnAdapterInitialized( |
142 scoped_refptr<device::BluetoothAdapter> adapter) { | 103 scoped_refptr<device::BluetoothAdapter> adapter) { |
143 bluetooth_adapter_ = adapter; | 104 bluetooth_adapter_ = adapter; |
144 UpdatePollingState(); | 105 UpdatePollingState(); |
145 } | 106 } |
146 | 107 |
147 void ProximityMonitorImpl::UpdatePollingState() { | 108 void ProximityMonitorImpl::UpdatePollingState() { |
148 if (ShouldPoll()) { | 109 if (ShouldPoll()) { |
149 // If there is a polling iteration already scheduled, wait for it. | 110 // If there is a polling iteration already scheduled, wait for it. |
150 if (polling_weak_ptr_factory_.HasWeakPtrs()) | 111 if (polling_weak_ptr_factory_.HasWeakPtrs()) |
(...skipping 12 matching lines...) Expand all Loading... |
163 remote_device_is_in_proximity_ = false; | 124 remote_device_is_in_proximity_ = false; |
164 } | 125 } |
165 } | 126 } |
166 | 127 |
167 void ProximityMonitorImpl::PerformScheduledUpdatePollingState() { | 128 void ProximityMonitorImpl::PerformScheduledUpdatePollingState() { |
168 polling_weak_ptr_factory_.InvalidateWeakPtrs(); | 129 polling_weak_ptr_factory_.InvalidateWeakPtrs(); |
169 UpdatePollingState(); | 130 UpdatePollingState(); |
170 } | 131 } |
171 | 132 |
172 bool ProximityMonitorImpl::ShouldPoll() const { | 133 bool ProximityMonitorImpl::ShouldPoll() const { |
173 // Note: We poll even if the strategy is NONE so we can record measurements. | |
174 return is_active_ && bluetooth_adapter_; | 134 return is_active_ && bluetooth_adapter_; |
175 } | 135 } |
176 | 136 |
177 void ProximityMonitorImpl::Poll() { | 137 void ProximityMonitorImpl::Poll() { |
178 DCHECK(ShouldPoll()); | 138 DCHECK(ShouldPoll()); |
179 | 139 |
180 BluetoothDevice* device = | 140 std::string address = connection_->GetDeviceAddress(); |
181 bluetooth_adapter_->GetDevice(remote_device_.bluetooth_address); | 141 BluetoothDevice* device = bluetooth_adapter_->GetDevice(address); |
182 | 142 |
183 if (!device) { | 143 if (!device) { |
184 PA_LOG(ERROR) << "Unknown Bluetooth device with address " | 144 PA_LOG(ERROR) << "Unknown Bluetooth device with address " << address; |
185 << remote_device_.bluetooth_address; | |
186 ClearProximityState(); | 145 ClearProximityState(); |
187 return; | 146 return; |
188 } | 147 } |
189 if (!device->IsConnected()) { | 148 if (!device->IsConnected()) { |
190 PA_LOG(ERROR) << "Bluetooth device with address " | 149 PA_LOG(ERROR) << "Bluetooth device with address " << address |
191 << remote_device_.bluetooth_address << " is not connected."; | 150 << " is not connected."; |
192 ClearProximityState(); | 151 ClearProximityState(); |
193 return; | 152 return; |
194 } | 153 } |
195 | 154 |
196 device->GetConnectionInfo(base::Bind(&ProximityMonitorImpl::OnConnectionInfo, | 155 device->GetConnectionInfo(base::Bind(&ProximityMonitorImpl::OnConnectionInfo, |
197 weak_ptr_factory_.GetWeakPtr())); | 156 weak_ptr_factory_.GetWeakPtr())); |
198 } | 157 } |
199 | 158 |
200 void ProximityMonitorImpl::OnConnectionInfo( | 159 void ProximityMonitorImpl::OnConnectionInfo( |
201 const BluetoothDevice::ConnectionInfo& connection_info) { | 160 const BluetoothDevice::ConnectionInfo& connection_info) { |
202 if (!is_active_) { | 161 if (!is_active_) { |
203 PA_LOG(INFO) << "[Proximity] Got connection info after stopping"; | 162 PA_LOG(INFO) << "[Proximity] Got connection info after stopping"; |
204 return; | 163 return; |
205 } | 164 } |
206 | 165 |
207 if (connection_info.rssi != BluetoothDevice::kUnknownPower && | 166 if (connection_info.rssi != BluetoothDevice::kUnknownPower) { |
208 connection_info.transmit_power != BluetoothDevice::kUnknownPower && | |
209 connection_info.max_transmit_power != BluetoothDevice::kUnknownPower) { | |
210 AddSample(connection_info); | 167 AddSample(connection_info); |
211 } else { | 168 } else { |
212 PA_LOG(WARNING) << "[Proximity] Unkown values received from API: " | 169 PA_LOG(WARNING) << "[Proximity] Unkown values received from API: " |
213 << connection_info.rssi << " " | 170 << connection_info.rssi; |
214 << connection_info.transmit_power << " " | |
215 << connection_info.max_transmit_power; | |
216 rssi_rolling_average_.reset(); | 171 rssi_rolling_average_.reset(); |
217 last_transmit_power_reading_.reset(); | |
218 CheckForProximityStateChange(); | 172 CheckForProximityStateChange(); |
219 } | 173 } |
220 } | 174 } |
221 | 175 |
222 void ProximityMonitorImpl::ClearProximityState() { | 176 void ProximityMonitorImpl::ClearProximityState() { |
223 if (is_active_ && remote_device_is_in_proximity_) { | 177 if (is_active_ && remote_device_is_in_proximity_) { |
224 for (auto& observer : observers_) | 178 for (auto& observer : observers_) |
225 observer.OnProximityStateChanged(); | 179 observer.OnProximityStateChanged(); |
226 } | 180 } |
227 | 181 |
228 remote_device_is_in_proximity_ = false; | 182 remote_device_is_in_proximity_ = false; |
229 rssi_rolling_average_.reset(); | 183 rssi_rolling_average_.reset(); |
230 last_transmit_power_reading_.reset(); | |
231 last_zero_rssi_timestamp_.reset(); | 184 last_zero_rssi_timestamp_.reset(); |
232 } | 185 } |
233 | 186 |
234 void ProximityMonitorImpl::AddSample( | 187 void ProximityMonitorImpl::AddSample( |
235 const BluetoothDevice::ConnectionInfo& connection_info) { | 188 const BluetoothDevice::ConnectionInfo& connection_info) { |
236 double weight = kRssiSampleWeight; | 189 double weight = kRssiSampleWeight; |
237 if (!rssi_rolling_average_) { | 190 if (!rssi_rolling_average_) { |
238 rssi_rolling_average_.reset(new double(connection_info.rssi)); | 191 rssi_rolling_average_.reset(new double(connection_info.rssi)); |
239 } else { | 192 } else { |
240 *rssi_rolling_average_ = | 193 *rssi_rolling_average_ = |
241 weight * connection_info.rssi + (1 - weight) * (*rssi_rolling_average_); | 194 weight * connection_info.rssi + (1 - weight) * (*rssi_rolling_average_); |
242 } | 195 } |
243 last_transmit_power_reading_.reset(new TransmitPowerReading( | |
244 connection_info.transmit_power, connection_info.max_transmit_power)); | |
245 | 196 |
246 // It's rare but possible for the RSSI to be positive briefly. | 197 // It's rare but possible for the RSSI to be positive briefly. |
247 if (connection_info.rssi >= 0) | 198 if (connection_info.rssi >= 0) |
248 last_zero_rssi_timestamp_.reset(new base::TimeTicks(clock_->NowTicks())); | 199 last_zero_rssi_timestamp_.reset(new base::TimeTicks(clock_->NowTicks())); |
249 | 200 |
250 CheckForProximityStateChange(); | 201 CheckForProximityStateChange(); |
251 } | 202 } |
252 | 203 |
253 void ProximityMonitorImpl::CheckForProximityStateChange() { | 204 void ProximityMonitorImpl::CheckForProximityStateChange() { |
254 if (strategy_ == Strategy::NONE) | 205 bool is_now_in_proximity = |
255 return; | 206 rssi_rolling_average_ && *rssi_rolling_average_ > kRssiThreshold; |
256 | 207 |
257 bool is_now_in_proximity = false; | 208 if (rssi_rolling_average_) { |
258 switch (strategy_) { | 209 LOG(WARNING) << "RSSI: " << *rssi_rolling_average_; |
259 case Strategy::NONE: | |
260 return; | |
261 | |
262 case Strategy::CHECK_RSSI: | |
263 is_now_in_proximity = IsInRssiRange(); | |
264 break; | |
265 | |
266 case Strategy::CHECK_TRANSMIT_POWER: | |
267 is_now_in_proximity = (last_transmit_power_reading_ && | |
268 last_transmit_power_reading_->IsInProximity()); | |
269 break; | |
270 } | 210 } |
271 | 211 |
272 if (remote_device_is_in_proximity_ != is_now_in_proximity) { | 212 if (remote_device_is_in_proximity_ != is_now_in_proximity) { |
273 PA_LOG(INFO) << "[Proximity] Updated proximity state: " | 213 PA_LOG(INFO) << "[Proximity] Updated proximity state: " |
274 << (is_now_in_proximity ? "proximate" : "distant"); | 214 << (is_now_in_proximity ? "proximate" : "distant"); |
275 remote_device_is_in_proximity_ = is_now_in_proximity; | 215 remote_device_is_in_proximity_ = is_now_in_proximity; |
276 for (auto& observer : observers_) | 216 for (auto& observer : observers_) |
277 observer.OnProximityStateChanged(); | 217 observer.OnProximityStateChanged(); |
278 } | 218 } |
279 } | 219 } |
280 | 220 |
281 } // namespace proximity_auth | 221 } // namespace proximity_auth |
OLD | NEW |