| 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. | |
| 99 base::TimeDelta time_since_last_zero_rssi; | |
| 100 if (last_zero_rssi_timestamp_) | |
| 101 time_since_last_zero_rssi = clock_->NowTicks() - *last_zero_rssi_timestamp_; | |
| 102 else | |
| 103 time_since_last_zero_rssi = base::TimeDelta::FromDays(100); | |
| 104 | |
| 105 std::string remote_device_model = metrics::kUnknownDeviceModel; | 77 std::string remote_device_model = metrics::kUnknownDeviceModel; |
| 106 if (remote_device_.name != remote_device_.bluetooth_address) | 78 cryptauth::RemoteDevice remote_device = connection_->remote_device(); |
| 107 remote_device_model = remote_device_.name; | 79 if (!remote_device.name.empty()) |
| 80 remote_device_model = remote_device.name; |
| 108 | 81 |
| 109 metrics::RecordAuthProximityRollingRssi(round(rssi_rolling_average)); | 82 metrics::RecordAuthProximityRollingRssi(round(rssi_rolling_average)); |
| 110 metrics::RecordAuthProximityTransmitPowerDelta(last_transmit_power_delta); | |
| 111 metrics::RecordAuthProximityTimeSinceLastZeroRssi(time_since_last_zero_rssi); | |
| 112 metrics::RecordAuthProximityRemoteDeviceModelHash(remote_device_model); | 83 metrics::RecordAuthProximityRemoteDeviceModelHash(remote_device_model); |
| 113 } | 84 } |
| 114 | 85 |
| 115 void ProximityMonitorImpl::AddObserver(ProximityMonitorObserver* observer) { | 86 void ProximityMonitorImpl::AddObserver(ProximityMonitorObserver* observer) { |
| 116 observers_.AddObserver(observer); | 87 observers_.AddObserver(observer); |
| 117 } | 88 } |
| 118 | 89 |
| 119 void ProximityMonitorImpl::RemoveObserver(ProximityMonitorObserver* observer) { | 90 void ProximityMonitorImpl::RemoveObserver(ProximityMonitorObserver* observer) { |
| 120 observers_.RemoveObserver(observer); | 91 observers_.RemoveObserver(observer); |
| 121 } | 92 } |
| 122 | 93 |
| 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( | 94 void ProximityMonitorImpl::OnAdapterInitialized( |
| 142 scoped_refptr<device::BluetoothAdapter> adapter) { | 95 scoped_refptr<device::BluetoothAdapter> adapter) { |
| 143 bluetooth_adapter_ = adapter; | 96 bluetooth_adapter_ = adapter; |
| 144 UpdatePollingState(); | 97 UpdatePollingState(); |
| 145 } | 98 } |
| 146 | 99 |
| 147 void ProximityMonitorImpl::UpdatePollingState() { | 100 void ProximityMonitorImpl::UpdatePollingState() { |
| 148 if (ShouldPoll()) { | 101 if (ShouldPoll()) { |
| 149 // If there is a polling iteration already scheduled, wait for it. | 102 // If there is a polling iteration already scheduled, wait for it. |
| 150 if (polling_weak_ptr_factory_.HasWeakPtrs()) | 103 if (polling_weak_ptr_factory_.HasWeakPtrs()) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 163 remote_device_is_in_proximity_ = false; | 116 remote_device_is_in_proximity_ = false; |
| 164 } | 117 } |
| 165 } | 118 } |
| 166 | 119 |
| 167 void ProximityMonitorImpl::PerformScheduledUpdatePollingState() { | 120 void ProximityMonitorImpl::PerformScheduledUpdatePollingState() { |
| 168 polling_weak_ptr_factory_.InvalidateWeakPtrs(); | 121 polling_weak_ptr_factory_.InvalidateWeakPtrs(); |
| 169 UpdatePollingState(); | 122 UpdatePollingState(); |
| 170 } | 123 } |
| 171 | 124 |
| 172 bool ProximityMonitorImpl::ShouldPoll() const { | 125 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_; | 126 return is_active_ && bluetooth_adapter_; |
| 175 } | 127 } |
| 176 | 128 |
| 177 void ProximityMonitorImpl::Poll() { | 129 void ProximityMonitorImpl::Poll() { |
| 178 DCHECK(ShouldPoll()); | 130 DCHECK(ShouldPoll()); |
| 179 | 131 |
| 180 BluetoothDevice* device = | 132 std::string address = connection_->GetDeviceAddress(); |
| 181 bluetooth_adapter_->GetDevice(remote_device_.bluetooth_address); | 133 BluetoothDevice* device = bluetooth_adapter_->GetDevice(address); |
| 182 | 134 |
| 183 if (!device) { | 135 if (!device) { |
| 184 PA_LOG(ERROR) << "Unknown Bluetooth device with address " | 136 PA_LOG(ERROR) << "Unknown Bluetooth device with address " << address; |
| 185 << remote_device_.bluetooth_address; | |
| 186 ClearProximityState(); | 137 ClearProximityState(); |
| 187 return; | 138 return; |
| 188 } | 139 } |
| 189 if (!device->IsConnected()) { | 140 if (!device->IsConnected()) { |
| 190 PA_LOG(ERROR) << "Bluetooth device with address " | 141 PA_LOG(ERROR) << "Bluetooth device with address " << address |
| 191 << remote_device_.bluetooth_address << " is not connected."; | 142 << " is not connected."; |
| 192 ClearProximityState(); | 143 ClearProximityState(); |
| 193 return; | 144 return; |
| 194 } | 145 } |
| 195 | 146 |
| 196 device->GetConnectionInfo(base::Bind(&ProximityMonitorImpl::OnConnectionInfo, | 147 device->GetConnectionInfo(base::Bind(&ProximityMonitorImpl::OnConnectionInfo, |
| 197 weak_ptr_factory_.GetWeakPtr())); | 148 weak_ptr_factory_.GetWeakPtr())); |
| 198 } | 149 } |
| 199 | 150 |
| 200 void ProximityMonitorImpl::OnConnectionInfo( | 151 void ProximityMonitorImpl::OnConnectionInfo( |
| 201 const BluetoothDevice::ConnectionInfo& connection_info) { | 152 const BluetoothDevice::ConnectionInfo& connection_info) { |
| 202 if (!is_active_) { | 153 if (!is_active_) { |
| 203 PA_LOG(INFO) << "[Proximity] Got connection info after stopping"; | 154 PA_LOG(INFO) << "[Proximity] Got connection info after stopping"; |
| 204 return; | 155 return; |
| 205 } | 156 } |
| 206 | 157 |
| 207 if (connection_info.rssi != BluetoothDevice::kUnknownPower && | 158 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); | 159 AddSample(connection_info); |
| 211 } else { | 160 } else { |
| 212 PA_LOG(WARNING) << "[Proximity] Unkown values received from API: " | 161 PA_LOG(WARNING) << "[Proximity] Unkown values received from API: " |
| 213 << connection_info.rssi << " " | 162 << connection_info.rssi; |
| 214 << connection_info.transmit_power << " " | |
| 215 << connection_info.max_transmit_power; | |
| 216 rssi_rolling_average_.reset(); | 163 rssi_rolling_average_.reset(); |
| 217 last_transmit_power_reading_.reset(); | |
| 218 CheckForProximityStateChange(); | 164 CheckForProximityStateChange(); |
| 219 } | 165 } |
| 220 } | 166 } |
| 221 | 167 |
| 222 void ProximityMonitorImpl::ClearProximityState() { | 168 void ProximityMonitorImpl::ClearProximityState() { |
| 223 if (is_active_ && remote_device_is_in_proximity_) { | 169 if (is_active_ && remote_device_is_in_proximity_) { |
| 224 for (auto& observer : observers_) | 170 for (auto& observer : observers_) |
| 225 observer.OnProximityStateChanged(); | 171 observer.OnProximityStateChanged(); |
| 226 } | 172 } |
| 227 | 173 |
| 228 remote_device_is_in_proximity_ = false; | 174 remote_device_is_in_proximity_ = false; |
| 229 rssi_rolling_average_.reset(); | 175 rssi_rolling_average_.reset(); |
| 230 last_transmit_power_reading_.reset(); | |
| 231 last_zero_rssi_timestamp_.reset(); | |
| 232 } | 176 } |
| 233 | 177 |
| 234 void ProximityMonitorImpl::AddSample( | 178 void ProximityMonitorImpl::AddSample( |
| 235 const BluetoothDevice::ConnectionInfo& connection_info) { | 179 const BluetoothDevice::ConnectionInfo& connection_info) { |
| 236 double weight = kRssiSampleWeight; | 180 double weight = kRssiSampleWeight; |
| 237 if (!rssi_rolling_average_) { | 181 if (!rssi_rolling_average_) { |
| 238 rssi_rolling_average_.reset(new double(connection_info.rssi)); | 182 rssi_rolling_average_.reset(new double(connection_info.rssi)); |
| 239 } else { | 183 } else { |
| 240 *rssi_rolling_average_ = | 184 *rssi_rolling_average_ = |
| 241 weight * connection_info.rssi + (1 - weight) * (*rssi_rolling_average_); | 185 weight * connection_info.rssi + (1 - weight) * (*rssi_rolling_average_); |
| 242 } | 186 } |
| 243 last_transmit_power_reading_.reset(new TransmitPowerReading( | |
| 244 connection_info.transmit_power, connection_info.max_transmit_power)); | |
| 245 | |
| 246 // It's rare but possible for the RSSI to be positive briefly. | |
| 247 if (connection_info.rssi >= 0) | |
| 248 last_zero_rssi_timestamp_.reset(new base::TimeTicks(clock_->NowTicks())); | |
| 249 | 187 |
| 250 CheckForProximityStateChange(); | 188 CheckForProximityStateChange(); |
| 251 } | 189 } |
| 252 | 190 |
| 253 void ProximityMonitorImpl::CheckForProximityStateChange() { | 191 void ProximityMonitorImpl::CheckForProximityStateChange() { |
| 254 if (strategy_ == Strategy::NONE) | 192 bool is_now_in_proximity = |
| 255 return; | 193 rssi_rolling_average_ && *rssi_rolling_average_ > kRssiThreshold; |
| 256 | 194 |
| 257 bool is_now_in_proximity = false; | 195 if (rssi_rolling_average_) { |
| 258 switch (strategy_) { | 196 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 } | 197 } |
| 271 | 198 |
| 272 if (remote_device_is_in_proximity_ != is_now_in_proximity) { | 199 if (remote_device_is_in_proximity_ != is_now_in_proximity) { |
| 273 PA_LOG(INFO) << "[Proximity] Updated proximity state: " | 200 PA_LOG(INFO) << "[Proximity] Updated proximity state: " |
| 274 << (is_now_in_proximity ? "proximate" : "distant"); | 201 << (is_now_in_proximity ? "proximate" : "distant"); |
| 275 remote_device_is_in_proximity_ = is_now_in_proximity; | 202 remote_device_is_in_proximity_ = is_now_in_proximity; |
| 276 for (auto& observer : observers_) | 203 for (auto& observer : observers_) |
| 277 observer.OnProximityStateChanged(); | 204 observer.OnProximityStateChanged(); |
| 278 } | 205 } |
| 279 } | 206 } |
| 280 | 207 |
| 281 } // namespace proximity_auth | 208 } // namespace proximity_auth |
| OLD | NEW |