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
3 // found in the LICENSE file.
5 #include "components/proximity_auth/ble/proximity_auth_ble_system.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "components/proximity_auth/ble/bluetooth_low_energy_connection.h"
12 #include "components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h"
13 #include "components/proximity_auth/ble/fake_wire_message.h"
14 #include "components/proximity_auth/connection.h"
15 #include "components/proximity_auth/cryptauth/base64url.h"
16 #include "components/proximity_auth/cryptauth/cryptauth_client.h"
17 #include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h"
18 #include "components/proximity_auth/logging/logging.h"
19 #include "components/proximity_auth/proximity_auth_client.h"
20 #include "components/proximity_auth/remote_device.h"
21 #include "device/bluetooth/bluetooth_device.h"
22 #include "device/bluetooth/bluetooth_gatt_connection.h"
24 namespace proximity_auth
{
28 // The UUID of the Bluetooth Low Energy service.
29 const char kSmartLockServiceUUID
[] = "b3b7e28e-a000-3e17-bd86-6e97b9e28c11";
31 // The UUID of the characteristic used to send data to the peripheral.
32 const char kToPeripheralCharUUID
[] = "977c6674-1239-4e72-993b-502369b8bb5a";
34 // The UUID of the characteristic used to receive data from the peripheral.
35 const char kFromPeripheralCharUUID
[] = "f4b904a2-a030-43b3-98a8-221c536c03cb";
37 // Polling interval in seconds.
38 const int kPollingIntervalSeconds
= 5;
40 // String received when the remote device's screen is unlocked.
41 const char kScreenUnlocked
[] = "Screen Unlocked";
43 // String send to poll the remote device screen state.
44 const char kPollScreenState
[] = "PollScreenState";
46 // BluetoothLowEnergyConnection parameter, number of attempts to send a write
47 // request before failing.
48 const int kMaxNumberOfTries
= 2;
52 ProximityAuthBleSystem::ScreenlockBridgeAdapter::ScreenlockBridgeAdapter(
53 ScreenlockBridge
* screenlock_bridge
)
54 : screenlock_bridge_(screenlock_bridge
) {
57 ProximityAuthBleSystem::ScreenlockBridgeAdapter::ScreenlockBridgeAdapter() {
60 ProximityAuthBleSystem::ScreenlockBridgeAdapter::~ScreenlockBridgeAdapter() {
63 void ProximityAuthBleSystem::ScreenlockBridgeAdapter::AddObserver(
64 ScreenlockBridge::Observer
* observer
) {
65 screenlock_bridge_
->AddObserver(observer
);
68 void ProximityAuthBleSystem::ScreenlockBridgeAdapter::RemoveObserver(
69 ScreenlockBridge::Observer
* observer
) {
70 screenlock_bridge_
->RemoveObserver(observer
);
73 void ProximityAuthBleSystem::ScreenlockBridgeAdapter::Unlock(
74 ProximityAuthClient
* client
) {
75 screenlock_bridge_
->Unlock(client
->GetAuthenticatedUsername());
78 ProximityAuthBleSystem::ProximityAuthBleSystem(
79 ScreenlockBridge
* screenlock_bridge
,
80 ProximityAuthClient
* proximity_auth_client
,
81 scoped_ptr
<CryptAuthClientFactory
> cryptauth_client_factory
)
82 : screenlock_bridge_(new ProximityAuthBleSystem::ScreenlockBridgeAdapter(
84 proximity_auth_client_(proximity_auth_client
),
85 cryptauth_client_factory_(cryptauth_client_factory
.Pass()),
86 is_polling_screen_state_(false),
87 weak_ptr_factory_(this) {
88 PA_LOG(INFO
) << "Starting Proximity Auth over Bluetooth Low Energy.";
89 screenlock_bridge_
->AddObserver(this);
92 ProximityAuthBleSystem::ProximityAuthBleSystem(
93 scoped_ptr
<ScreenlockBridgeAdapter
> screenlock_bridge
,
94 ProximityAuthClient
* proximity_auth_client
)
95 : screenlock_bridge_(screenlock_bridge
.Pass()),
96 proximity_auth_client_(proximity_auth_client
),
97 is_polling_screen_state_(false),
98 weak_ptr_factory_(this) {
99 PA_LOG(INFO
) << "Starting Proximity Auth over Bluetooth Low Energy.";
100 screenlock_bridge_
->AddObserver(this);
103 ProximityAuthBleSystem::~ProximityAuthBleSystem() {
104 PA_LOG(INFO
) << "Stopping Proximity over Bluetooth Low Energy.";
105 screenlock_bridge_
->RemoveObserver(this);
107 connection_
->RemoveObserver(this);
110 void ProximityAuthBleSystem::OnGetMyDevices(
111 const cryptauth::GetMyDevicesResponse
& response
) {
112 PA_LOG(INFO
) << "Found " << response
.devices_size()
113 << " devices on CryptAuth.";
114 unlock_keys_
.clear();
115 for (const auto& device
: response
.devices()) {
116 // Cache BLE devices (|bluetooth_address().empty() == true|) that are
117 // keys (|unlock_key() == 1|).
118 if (device
.unlock_key() && device
.bluetooth_address().empty()) {
119 std::string base64_public_key
;
120 Base64UrlEncode(device
.public_key(), &base64_public_key
);
121 unlock_keys_
[base64_public_key
] = device
.friendly_device_name();
123 PA_LOG(INFO
) << "friendly_name = " << device
.friendly_device_name();
124 PA_LOG(INFO
) << "public_key = " << base64_public_key
;
127 PA_LOG(INFO
) << "Found " << unlock_keys_
.size() << " unlock keys.";
130 void ProximityAuthBleSystem::OnGetMyDevicesError(const std::string
& error
) {
131 PA_LOG(INFO
) << "GetMyDevices failed: " << error
;
134 // This should be called exclusively after the user has logged in. For instance,
135 // calling |GetUnlockKeys| from the constructor cause |GetMyDevices| to always
137 void ProximityAuthBleSystem::GetUnlockKeys() {
138 if (cryptauth_client_factory_
) {
139 cryptauth_client_
= cryptauth_client_factory_
->CreateInstance();
140 cryptauth::GetMyDevicesRequest request
;
141 cryptauth_client_
->GetMyDevices(
142 request
, base::Bind(&ProximityAuthBleSystem::OnGetMyDevices
,
143 weak_ptr_factory_
.GetWeakPtr()),
144 base::Bind(&ProximityAuthBleSystem::OnGetMyDevicesError
,
145 weak_ptr_factory_
.GetWeakPtr()));
149 void ProximityAuthBleSystem::OnScreenDidLock(
150 ScreenlockBridge::LockHandler::ScreenType screen_type
) {
151 PA_LOG(INFO
) << "OnScreenDidLock: " << screen_type
;
152 switch (screen_type
) {
153 case ScreenlockBridge::LockHandler::SIGNIN_SCREEN
:
154 connection_finder_
.reset();
156 case ScreenlockBridge::LockHandler::LOCK_SCREEN
:
157 DCHECK(!connection_finder_
);
158 connection_finder_
.reset(CreateConnectionFinder());
159 connection_finder_
->Find(
160 base::Bind(&ProximityAuthBleSystem::OnConnectionFound
,
161 weak_ptr_factory_
.GetWeakPtr()));
163 case ScreenlockBridge::LockHandler::OTHER_SCREEN
:
164 connection_finder_
.reset();
169 ConnectionFinder
* ProximityAuthBleSystem::CreateConnectionFinder() {
170 return new BluetoothLowEnergyConnectionFinder(
171 kSmartLockServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
175 void ProximityAuthBleSystem::OnScreenDidUnlock(
176 ScreenlockBridge::LockHandler::ScreenType screen_type
) {
177 PA_LOG(INFO
) << "OnScreenDidUnlock: " << screen_type
;
179 // Fetch the unlock keys when the user signs in.
180 // TODO(sacomoto): refetch the keys periodically, in case a new device was
182 if (screen_type
== ScreenlockBridge::LockHandler::SIGNIN_SCREEN
)
186 // Note: it's important to remove the observer before calling
187 // |Disconnect()|, otherwise |OnConnectedStatusChanged()| will be called
188 // from |connection_| and a new instance for |connection_finder_| will be
190 connection_
->RemoveObserver(this);
191 connection_
->Disconnect();
195 connection_finder_
.reset();
198 void ProximityAuthBleSystem::OnFocusedUserChanged(const std::string
& user_id
) {
199 PA_LOG(INFO
) << "OnFocusedUserChanged: " << user_id
;
202 void ProximityAuthBleSystem::OnMessageReceived(const Connection
& connection
,
203 const WireMessage
& message
) {
204 // TODO(sacomoto): change this when WireMessage is fully implemented.
205 PA_LOG(INFO
) << "Message received: " << message
.payload();
207 // Unlock the screen when the remote device sends an unlock signal.
209 // Note that this magically unlocks Chrome (no user interaction is needed).
210 // This user experience for this operation will be greately improved once
211 // the Proximity Auth Unlock Manager migration to C++ is done.
212 if (message
.payload() == kScreenUnlocked
) {
213 PA_LOG(INFO
) << "Device unlocked. Unlock.";
214 screenlock_bridge_
->Unlock(proximity_auth_client_
);
218 void ProximityAuthBleSystem::OnConnectionFound(
219 scoped_ptr
<Connection
> connection
) {
220 PA_LOG(INFO
) << "Connection found.";
222 connection_
= connection
.Pass();
224 connection_
->AddObserver(this);
226 if (!is_polling_screen_state_
) {
227 is_polling_screen_state_
= true;
228 StartPollingScreenState();
233 void ProximityAuthBleSystem::OnConnectionStatusChanged(
234 Connection
* connection
,
235 Connection::Status old_status
,
236 Connection::Status new_status
) {
237 if (old_status
== Connection::CONNECTED
&&
238 new_status
== Connection::DISCONNECTED
) {
239 StopPollingScreenState();
241 connection_
->RemoveObserver(this);
244 connection_finder_
.reset(CreateConnectionFinder());
245 connection_finder_
->Find(
246 base::Bind(&ProximityAuthBleSystem::OnConnectionFound
,
247 weak_ptr_factory_
.GetWeakPtr()));
251 void ProximityAuthBleSystem::StartPollingScreenState() {
252 if (is_polling_screen_state_
) {
253 if (!connection_
|| !connection_
->IsConnected()) {
254 PA_LOG(INFO
) << "Polling stopped.";
255 is_polling_screen_state_
= false;
259 // Sends message requesting screen state.
260 connection_
->SendMessage(
261 make_scoped_ptr(new FakeWireMessage(kPollScreenState
)));
263 // Schedules the next message in |kPollingIntervalSeconds| ms.
264 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
265 FROM_HERE
, base::Bind(&ProximityAuthBleSystem::StartPollingScreenState
,
266 weak_ptr_factory_
.GetWeakPtr()),
267 base::TimeDelta::FromSeconds(kPollingIntervalSeconds
));
271 void ProximityAuthBleSystem::StopPollingScreenState() {
272 is_polling_screen_state_
= false;
275 } // namespace proximity_auth