1 // Copyright 2020 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 package org.chromium.chrome.browser.webauth.authenticator;
6 
7 import android.bluetooth.BluetoothAdapter;
8 import android.bluetooth.le.AdvertiseCallback;
9 import android.bluetooth.le.AdvertiseData;
10 import android.bluetooth.le.AdvertiseSettings;
11 import android.bluetooth.le.BluetoothLeAdvertiser;
12 import android.os.ParcelUuid;
13 
14 import org.chromium.base.Log;
15 import org.chromium.base.annotations.CalledByNative;
16 
17 import java.io.Closeable;
18 import java.nio.ByteBuffer;
19 import java.util.UUID;
20 
21 class BLEAdvert implements Closeable {
22     private static final String TAG = "CableBLEAdvert";
23     // This UUID is allocated to Google.
24     private static final String CABLE_UUID = "0000fde2-0000-1000-8000-00805f9b34fb";
25 
26     private AdvertiseCallback mCallback;
27 
BLEAdvert(byte[] payload)28     BLEAdvert(byte[] payload) {
29         assert payload.length == 20;
30 
31         BluetoothLeAdvertiser advertiser =
32                 BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
33         mCallback = new AdvertiseCallback() {
34             @Override
35             public void onStartFailure(int errorCode) {
36                 Log.i(TAG, "advertising failure " + errorCode);
37             }
38 
39             @Override
40             public void onStartSuccess(AdvertiseSettings settingsInEffect) {
41                 Log.i(TAG, "advertising success");
42             }
43         };
44 
45         AdvertiseSettings settings =
46                 (new AdvertiseSettings.Builder())
47                         .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
48                         .setConnectable(false)
49                         .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
50                         .build();
51         ParcelUuid fidoUuid = new ParcelUuid(UUID.fromString(CABLE_UUID));
52 
53         // The first 16 bytes of the payload are encoded into a 16-byte UUID.
54         ByteBuffer bb = ByteBuffer.wrap(payload);
55         long high = bb.getLong();
56         long low = bb.getLong();
57         final UUID uuid16 = new UUID(high, low);
58 
59         // The final four bytes of the payload are turned into a 4-byte UUID.
60         // Depending on the value of those four bytes, this might happen to be a
61         // 2-byte UUID, but the desktop handles that.
62         high = (long) bb.getInt();
63         high <<= 32;
64         // This is the fixed suffix for short UUIDs in Bluetooth.
65         high |= 0x1000;
66         low = 0x800000805f9b34fbL;
67         final UUID uuid4 = new UUID(high, low);
68 
69         AdvertiseData data = (new AdvertiseData.Builder())
70                                      .addServiceUuid(fidoUuid)
71                                      .addServiceUuid(new ParcelUuid(uuid16))
72                                      .addServiceUuid(new ParcelUuid(uuid4))
73                                      .setIncludeDeviceName(false)
74                                      .setIncludeTxPowerLevel(false)
75                                      .build();
76 
77         advertiser.startAdvertising(settings, data, mCallback);
78     }
79 
80     @Override
81     @CalledByNative
close()82     public void close() {
83         if (mCallback == null) {
84             return;
85         }
86 
87         BluetoothLeAdvertiser advertiser =
88                 BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
89         Log.i(TAG, "stopping advertising");
90         advertiser.stopAdvertising(mCallback);
91         mCallback = null;
92     }
93 }
94