• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.nearby.fastpair.provider;
18 
19 import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
20 import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
21 import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
22 import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
23 import static android.bluetooth.BluetoothAdapter.STATE_OFF;
24 import static android.bluetooth.BluetoothAdapter.STATE_ON;
25 import static android.bluetooth.BluetoothDevice.ERROR;
26 import static android.bluetooth.BluetoothGattCharacteristic.PERMISSION_READ;
27 import static android.bluetooth.BluetoothGattCharacteristic.PERMISSION_WRITE;
28 import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_INDICATE;
29 import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_NOTIFY;
30 import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_READ;
31 import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_WRITE;
32 import static android.nearby.fastpair.provider.bluetooth.BluetoothManager.wrap;
33 import static android.nearby.fastpair.provider.bluetooth.RfcommServer.State.CONNECTED;
34 
35 import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.AES_BLOCK_LENGTH;
36 import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.encrypt;
37 import static com.android.server.nearby.common.bluetooth.fastpair.Bytes.toBytes;
38 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.A2DP_SINK_SERVICE_UUID;
39 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.TransportDiscoveryService.BLUETOOTH_SIG_ORGANIZATION_ID;
40 import static com.android.server.nearby.common.bluetooth.fastpair.EllipticCurveDiffieHellmanExchange.PUBLIC_KEY_LENGTH;
41 import static com.android.server.nearby.common.bluetooth.fastpair.MessageStreamHmacEncoder.SECTION_NONCE_LENGTH;
42 
43 import static com.google.common.io.BaseEncoding.base16;
44 import static com.google.common.primitives.Bytes.concat;
45 
46 import android.bluetooth.BluetoothAdapter;
47 import android.bluetooth.BluetoothClass.Device.Major;
48 import android.bluetooth.BluetoothDevice;
49 import android.bluetooth.BluetoothGatt;
50 import android.bluetooth.BluetoothGattCharacteristic;
51 import android.bluetooth.BluetoothGattDescriptor;
52 import android.bluetooth.BluetoothManager;
53 import android.bluetooth.BluetoothProfile;
54 import android.bluetooth.le.AdvertiseSettings;
55 import android.content.BroadcastReceiver;
56 import android.content.Context;
57 import android.content.Intent;
58 import android.content.IntentFilter;
59 import android.nearby.fastpair.provider.EventStreamProtocol.AcknowledgementEventCode;
60 import android.nearby.fastpair.provider.EventStreamProtocol.DeviceActionEventCode;
61 import android.nearby.fastpair.provider.EventStreamProtocol.DeviceCapabilitySyncEventCode;
62 import android.nearby.fastpair.provider.EventStreamProtocol.DeviceConfigurationEventCode;
63 import android.nearby.fastpair.provider.EventStreamProtocol.DeviceEventCode;
64 import android.nearby.fastpair.provider.EventStreamProtocol.EventGroup;
65 import android.nearby.fastpair.provider.bluetooth.BluetoothGattServerConfig;
66 import android.nearby.fastpair.provider.bluetooth.BluetoothGattServerConfig.ServiceConfig;
67 import android.nearby.fastpair.provider.bluetooth.BluetoothGattServerConnection;
68 import android.nearby.fastpair.provider.bluetooth.BluetoothGattServerConnection.Notifier;
69 import android.nearby.fastpair.provider.bluetooth.BluetoothGattServerHelper;
70 import android.nearby.fastpair.provider.bluetooth.BluetoothGattServlet;
71 import android.nearby.fastpair.provider.bluetooth.RfcommServer;
72 import android.nearby.fastpair.provider.crypto.Crypto;
73 import android.nearby.fastpair.provider.crypto.E2eeCalculator;
74 import android.nearby.fastpair.provider.utils.Logger;
75 import android.os.Build.VERSION;
76 import android.os.Build.VERSION_CODES;
77 import android.os.Handler;
78 import android.os.Looper;
79 import android.os.SystemClock;
80 import android.provider.Settings;
81 import android.text.TextUtils;
82 
83 import androidx.annotation.Nullable;
84 import androidx.annotation.VisibleForTesting;
85 import androidx.core.util.Consumer;
86 
87 import com.android.server.nearby.common.bloomfilter.BloomFilter;
88 import com.android.server.nearby.common.bloomfilter.FastPairBloomFilterHasher;
89 import com.android.server.nearby.common.bluetooth.BluetoothException;
90 import com.android.server.nearby.common.bluetooth.BluetoothGattException;
91 import com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption;
92 import com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress;
93 import com.android.server.nearby.common.bluetooth.fastpair.Bytes.Value;
94 import com.android.server.nearby.common.bluetooth.fastpair.Constants;
95 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService;
96 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AccountKeyCharacteristic;
97 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.BeaconActionsCharacteristic;
98 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType;
99 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.FirmwareVersionCharacteristic;
100 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic;
101 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.NameCharacteristic;
102 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.PasskeyCharacteristic;
103 import com.android.server.nearby.common.bluetooth.fastpair.Constants.TransportDiscoveryService;
104 import com.android.server.nearby.common.bluetooth.fastpair.Constants.TransportDiscoveryService.BrHandoverDataCharacteristic;
105 import com.android.server.nearby.common.bluetooth.fastpair.Constants.TransportDiscoveryService.ControlPointCharacteristic;
106 import com.android.server.nearby.common.bluetooth.fastpair.EllipticCurveDiffieHellmanExchange;
107 import com.android.server.nearby.common.bluetooth.fastpair.Ltv;
108 import com.android.server.nearby.common.bluetooth.fastpair.MessageStreamHmacEncoder;
109 import com.android.server.nearby.common.bluetooth.fastpair.NamingEncoder;
110 
111 import com.google.common.base.Ascii;
112 import com.google.common.primitives.Bytes;
113 import com.google.protobuf.ByteString;
114 
115 import java.lang.reflect.Method;
116 import java.nio.ByteOrder;
117 import java.nio.charset.StandardCharsets;
118 import java.security.GeneralSecurityException;
119 import java.security.MessageDigest;
120 import java.security.NoSuchAlgorithmException;
121 import java.security.SecureRandom;
122 import java.text.SimpleDateFormat;
123 import java.util.ArrayList;
124 import java.util.Arrays;
125 import java.util.Calendar;
126 import java.util.Collections;
127 import java.util.HashMap;
128 import java.util.HashSet;
129 import java.util.List;
130 import java.util.Locale;
131 import java.util.Map;
132 import java.util.Random;
133 import java.util.Set;
134 import java.util.concurrent.CountDownLatch;
135 import java.util.concurrent.Executors;
136 import java.util.concurrent.ScheduledExecutorService;
137 import java.util.concurrent.ScheduledFuture;
138 import java.util.concurrent.TimeUnit;
139 import java.util.concurrent.TimeoutException;
140 
141 /**
142  * Simulates a Fast Pair device (e.g. a headset).
143  *
144  * <p>Note: There are two deviations from the spec:
145  *
146  * <ul>
147  *   <li>Instead of using the public address when in pairing mode (discoverable), it always uses the
148  *       random private address (RPA), because that's how stock Android works. To work around this,
149  *       it implements the BR/EDR Handover profile (which is no longer part of the Fast Pair spec)
150  *       when simulating a keyless device (i.e. Fast Pair 1.0), which allows the phone to ask for
151  *       the public address. When there is an anti-spoofing key, i.e. Fast Pair 2.0, the public
152  *       address is delivered via the Key-based Pairing handshake. b/79374759 tracks fixing this.
153  *   <li>The simulator always identifies its device capabilities as Keyboard/Display, even when
154  *       simulating a keyless (Fast Pair 1.0) device that should identify as NoInput/NoOutput.
155  *       b/79377125 tracks fixing this.
156  * </ul>
157  *
158  * @see {http://go/fast-pair-2-spec}
159  */
160 public class FastPairSimulator {
161     public static final String TAG = "FastPairSimulator";
162     private final Logger mLogger;
163 
164     private static final int BECOME_DISCOVERABLE_TIMEOUT_SEC = 3;
165 
166     private static final int SCAN_MODE_REFRESH_SEC = 30;
167 
168     /**
169      * Headphones. Generated by
170      * http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html
171      */
172     private static final Value CLASS_OF_DEVICE =
173             new Value(base16().decode("200418"), ByteOrder.BIG_ENDIAN);
174 
175     private static final byte[] SUPPORTED_SERVICES_LTV = new Ltv(
176             TransportDiscoveryService.SERVICE_UUIDS_16_BIT_LIST_TYPE,
177             toBytes(ByteOrder.LITTLE_ENDIAN, A2DP_SINK_SERVICE_UUID)
178     ).getBytes();
179     private static final byte[] TDS_CONTROL_POINT_RESPONSE_PARAMETER =
180             Bytes.concat(new byte[]{BLUETOOTH_SIG_ORGANIZATION_ID}, SUPPORTED_SERVICES_LTV);
181 
182     private static final String SIMULATOR_FAKE_BLE_ADDRESS = "11:22:33:44:55:66";
183 
184     private static final long ADVERTISING_REFRESH_DELAY_1_MIN = TimeUnit.MINUTES.toMillis(1);
185 
186     /**
187      * The size of account key filter in bytes is (1.2*n + 3), n represents the size of account key,
188      * see https://developers.google.com/nearby/fast-pair/spec#advertising_when_not_discoverable.
189      * However we'd like to advertise something else, so we could only afford 8 account keys.
190      *
191      * <ul>
192      *   <li>BLE flags: 3 bytes
193      *   <li>TxPower: 3 bytes
194      *   <li>FastPair: max 25 bytes
195      *       <ul>
196      *         <li>FastPair service data: 4 bytes
197      *         <li>Flags: 1 byte
198      *         <li>Account key filter: max 14 bytes (1 byte: length + type, 13 bytes: max 8 account
199      *             keys)
200      *         <li>Salt: 2 bytes
201      *         <li>Battery: 4 bytes
202      *       </ul>
203      * </ul>
204      */
205     private String mDeviceFirmwareVersion = "1.1.0";
206 
207     private byte[] mSessionNonce;
208 
209     private boolean mUseLogFullEvent = true;
210 
211     private enum ResultCode {
212         SUCCESS((byte) 0x00),
213         OP_CODE_NOT_SUPPORTED((byte) 0x01),
214         INVALID_PARAMETER((byte) 0x02),
215         UNSUPPORTED_ORGANIZATION_ID((byte) 0x03),
216         OPERATION_FAILED((byte) 0x04);
217 
218         private final byte mByteValue;
219 
ResultCode(byte byteValue)220         ResultCode(byte byteValue) {
221             this.mByteValue = byteValue;
222         }
223     }
224 
225     private enum TransportState {
226         OFF((byte) 0x00),
227         ON((byte) 0x01),
228         TEMPORARILY_UNAVAILABLE((byte) 0x10);
229 
230         private final byte mByteValue;
231 
TransportState(byte byteValue)232         TransportState(byte byteValue) {
233             this.mByteValue = byteValue;
234         }
235     }
236 
237     private final Context mContext;
238     private final Options mOptions;
239     private final Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
240     // No thread pool: Only used in test app (outside gmscore) and in javatests/.../gmscore/.
241     private final ScheduledExecutorService mExecutor =
242             Executors.newSingleThreadScheduledExecutor(); // exempt
243     private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
244     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
245         @Override
246         public void onReceive(Context context, Intent intent) {
247             if (mShouldFailPairing) {
248                 mLogger.log("Pairing disabled by test app switch");
249                 return;
250             }
251             if (mIsDestroyed) {
252                 // Sometimes this receiver does not successfully unregister in destroy()
253                 // which causes events to occur after the simulator is stopped, so ignore
254                 // those events.
255                 mLogger.log("Intent received after simulator destroyed, ignoring");
256                 return;
257             }
258             BluetoothDevice device = intent.getParcelableExtra(
259                     BluetoothDevice.EXTRA_DEVICE);
260             switch (intent.getAction()) {
261                 case BluetoothAdapter.ACTION_SCAN_MODE_CHANGED:
262                     if (isDiscoverable()) {
263                         mIsDiscoverableLatch.countDown();
264                     }
265                     break;
266                 case BluetoothDevice.ACTION_PAIRING_REQUEST:
267                     int variant = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
268                             ERROR);
269                     int key = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, ERROR);
270                     mLogger.log(
271                             "Pairing request, variant=%d, key=%s", variant,
272                             key == ERROR ? "(none)" : key);
273 
274                     // Prevent Bluetooth Settings from getting the pairing request.
275                     abortBroadcast();
276 
277                     mPairingDevice = device;
278                     if (mSecret == null) {
279                         // We haven't done the handshake over GATT to agree on the shared
280                         // secret. For now, just accept anyway (so we can still simulate
281                         // old 1.0 model IDs).
282                         mLogger.log("No handshake, auto-accepting anyway.");
283                         setPasskeyConfirmation(true);
284                     } else if (variant
285                             == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
286                         // Store the passkey. And check it, since there's a race (see
287                         // method for why). Usually this check is a no-op and we'll get
288                         // the passkey later over GATT.
289                         mLocalPasskey = key;
290                         checkPasskey();
291                     } else if (variant == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
292                         if (mPasskeyEventCallback != null) {
293                             mPasskeyEventCallback.onPasskeyRequested(
294                                     FastPairSimulator.this::enterPassKey);
295                         } else {
296                             mLogger.log("passkeyEventCallback is not set!");
297                             enterPassKey(key);
298                         }
299                     } else if (variant == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
300                         setPasskeyConfirmation(true);
301 
302                     } else if (variant == BluetoothDevice.PAIRING_VARIANT_PIN) {
303                         if (mPasskeyEventCallback != null) {
304                             mPasskeyEventCallback.onPasskeyRequested(
305                                     (int pin) -> {
306                                         byte[] newPin = convertPinToBytes(
307                                                 String.format(Locale.ENGLISH, "%d", pin));
308                                         mPairingDevice.setPin(newPin);
309                                     });
310                         }
311                     } else {
312                         // Reject the pairing request if it's not using the Numeric
313                         // Comparison (aka Passkey Confirmation) method.
314                         setPasskeyConfirmation(false);
315                     }
316                     break;
317                 case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
318                     int bondState =
319                             intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
320                                     BluetoothDevice.BOND_NONE);
321                     mLogger.log("Bond state to %s changed to %d", device, bondState);
322                     switch (bondState) {
323                         case BluetoothDevice.BOND_BONDING:
324                             // If we've started bonding, we shouldn't be advertising.
325                             mAdvertiser.stopAdvertising();
326                             // Not discoverable anymore, but still connectable.
327                             setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
328                             break;
329                         case BluetoothDevice.BOND_BONDED:
330                             // Once bonded, advertise the account keys.
331                             mAdvertiser.startAdvertising(accountKeysServiceData());
332                             setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
333 
334                             // If it is subsequent pair, we need to add paired device here.
335                             if (mIsSubsequentPair
336                                     && mSecret != null
337                                     && mSecret.length == AES_BLOCK_LENGTH) {
338                                 addAccountKey(mSecret, mPairingDevice);
339                             }
340                             break;
341                         case BluetoothDevice.BOND_NONE:
342                             // If the bonding process fails, we should be advertising again.
343                             mAdvertiser.startAdvertising(getServiceData());
344                             break;
345                         default:
346                             break;
347                     }
348                     break;
349                 case BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED:
350                     mLogger.log(
351                             "Connection state to %s changed to %d",
352                             device,
353                             intent.getIntExtra(
354                                     BluetoothAdapter.EXTRA_CONNECTION_STATE,
355                                     BluetoothAdapter.STATE_DISCONNECTED));
356                     break;
357                 case BluetoothAdapter.ACTION_STATE_CHANGED:
358                     int state = intent.getIntExtra(EXTRA_STATE, -1);
359                     mLogger.log("Bluetooth adapter state=%s", state);
360                     switch (state) {
361                         case STATE_ON:
362                             startRfcommServer();
363                             break;
364                         case STATE_OFF:
365                             stopRfcommServer();
366                             break;
367                         default: // fall out
368                     }
369                     break;
370                 default:
371                     mLogger.log(new IllegalArgumentException(intent.toString()),
372                             "Received unexpected intent");
373                     break;
374             }
375         }
376     };
377 
378     @Nullable
convertPinToBytes(@ullable String pin)379     private byte[] convertPinToBytes(@Nullable String pin) {
380         if (TextUtils.isEmpty(pin)) {
381             return null;
382         }
383         byte[] pinBytes;
384         pinBytes = pin.getBytes(StandardCharsets.UTF_8);
385         if (pinBytes.length <= 0 || pinBytes.length > 16) {
386             return null;
387         }
388         return pinBytes;
389     }
390 
391     private final NotifiableGattServlet mPasskeyServlet =
392             new NotifiableGattServlet() {
393                 @Override
394                 // Simulating deprecated API {@code PasskeyCharacteristic.ID} for testing.
395                 @SuppressWarnings("deprecation")
396                 public BluetoothGattCharacteristic getBaseCharacteristic() {
397                     return new BluetoothGattCharacteristic(
398                             PasskeyCharacteristic.CUSTOM_128_BIT_UUID,
399                             PROPERTY_WRITE | PROPERTY_INDICATE,
400                             PERMISSION_WRITE);
401                 }
402 
403                 @Override
404                 public void write(
405                         BluetoothGattServerConnection connection, int offset, byte[] value) {
406                     mLogger.log("Got value from passkey servlet: %s", base16().encode(value));
407                     if (mSecret == null) {
408                         mLogger.log("Ignoring write to passkey characteristic, no pairing secret.");
409                         return;
410                     }
411 
412                     try {
413                         mRemotePasskey = PasskeyCharacteristic.decrypt(
414                                 PasskeyCharacteristic.Type.SEEKER, mSecret, value);
415                         if (mPasskeyEventCallback != null) {
416                             mPasskeyEventCallback.onRemotePasskeyReceived(mRemotePasskey);
417                         }
418                         checkPasskey();
419                     } catch (GeneralSecurityException e) {
420                         mLogger.log(
421                                 "Decrypting passkey value %s failed using key %s",
422                                 base16().encode(value), base16().encode(mSecret));
423                     }
424                 }
425             };
426 
427     private final NotifiableGattServlet mDeviceNameServlet =
428             new NotifiableGattServlet() {
429                 @Override
430                 // Simulating deprecated API {@code NameCharacteristic.ID} for testing.
431                 @SuppressWarnings("deprecation")
432                 BluetoothGattCharacteristic getBaseCharacteristic() {
433                     return new BluetoothGattCharacteristic(
434                             NameCharacteristic.CUSTOM_128_BIT_UUID,
435                             PROPERTY_WRITE | PROPERTY_INDICATE,
436                             PERMISSION_WRITE);
437                 }
438 
439                 @Override
440                 public void write(
441                         BluetoothGattServerConnection connection, int offset, byte[] value) {
442                     mLogger.log("Got value from device naming servlet: %s", base16().encode(value));
443                     if (mSecret == null) {
444                         mLogger.log("Ignoring write to name characteristic, no pairing secret.");
445                         return;
446                     }
447                     // Parse the device name from seeker to write name into provider.
448                     mLogger.log("Got name byte array size = %d", value.length);
449                     try {
450                         String decryptedDeviceName =
451                                 NamingEncoder.decodeNamingPacket(mSecret, value);
452                         if (decryptedDeviceName != null) {
453                             setDeviceName(decryptedDeviceName.getBytes(StandardCharsets.UTF_8));
454                             mLogger.log("write device name = %s", decryptedDeviceName);
455                         }
456                     } catch (GeneralSecurityException e) {
457                         mLogger.log(e, "Failed to decrypt device name.");
458                     }
459                     // For testing to make sure we get the new provider name from simulator.
460                     if (mWriteNameCountDown != null) {
461                         mLogger.log("finish count down latch to write device name.");
462                         mWriteNameCountDown.countDown();
463                     }
464                 }
465             };
466 
467     private Value mBluetoothAddress;
468     private final FastPairAdvertiser mAdvertiser;
469     private final Map<String, BluetoothGattServerHelper> mBluetoothGattServerHelpers =
470             new HashMap<>();
471     private CountDownLatch mIsDiscoverableLatch = new CountDownLatch(1);
472     private ScheduledFuture<?> mRevertDiscoverableFuture;
473     private boolean mShouldFailPairing = false;
474     private boolean mIsDestroyed = false;
475     private boolean mIsAdvertising;
476     @Nullable
477     private String mBleAddress;
478     private BluetoothDevice mPairingDevice;
479     private int mLocalPasskey;
480     private int mRemotePasskey;
481     @Nullable
482     private byte[] mSecret;
483     @Nullable
484     private byte[] mAccountKey; // The latest account key added.
485     // The first account key added. Eddystone treats that account as the owner of the device.
486     @Nullable
487     private byte[] mOwnerAccountKey;
488     @Nullable
489     private PasskeyConfirmationCallback mPasskeyConfirmationCallback;
490     @Nullable
491     private DeviceNameCallback mDeviceNameCallback;
492     @Nullable
493     private PasskeyEventCallback mPasskeyEventCallback;
494     private final List<BatteryValue> mBatteryValues;
495     private boolean mSuppressBatteryNotification = false;
496     private boolean mSuppressSubsequentPairingNotification = false;
497     HandshakeRequest mHandshakeRequest;
498     @Nullable
499     private CountDownLatch mWriteNameCountDown;
500     private final RfcommServer mRfcommServer = new RfcommServer();
501     private boolean mSupportDynamicBufferSize = false;
502     private NotifiableGattServlet mBeaconActionsServlet;
503     private final FastPairSimulatorDatabase mFastPairSimulatorDatabase;
504     private boolean mIsSubsequentPair = false;
505 
506     /** Sets the flag for failing paring for debug purpose. */
setShouldFailPairing(boolean shouldFailPairing)507     public void setShouldFailPairing(boolean shouldFailPairing) {
508         this.mShouldFailPairing = shouldFailPairing;
509     }
510 
511     /** Gets the flag for failing paring for debug purpose. */
getShouldFailPairing()512     public boolean getShouldFailPairing() {
513         return mShouldFailPairing;
514     }
515 
516     /** Clear the battery values, then no battery information is packed when advertising. */
clearBatteryValues()517     public void clearBatteryValues() {
518         mBatteryValues.clear();
519     }
520 
521     /** Sets the battery items which will be included in the advertisement packet. */
setBatteryValues(BatteryValue... batteryValues)522     public void setBatteryValues(BatteryValue... batteryValues) {
523         this.mBatteryValues.clear();
524         Collections.addAll(this.mBatteryValues, batteryValues);
525     }
526 
527     /** Sets whether the battery advertisement packet is within suppress type or not. */
setSuppressBatteryNotification(boolean suppressBatteryNotification)528     public void setSuppressBatteryNotification(boolean suppressBatteryNotification) {
529         this.mSuppressBatteryNotification = suppressBatteryNotification;
530     }
531 
532     /** Sets whether the account key data is within suppress type or not. */
setSuppressSubsequentPairingNotification(boolean isSuppress)533     public void setSuppressSubsequentPairingNotification(boolean isSuppress) {
534         mSuppressSubsequentPairingNotification = isSuppress;
535     }
536 
537     /** Calls this to start advertising after some values are changed. */
startAdvertising()538     public void startAdvertising() {
539         mAdvertiser.startAdvertising(getServiceData());
540     }
541 
542     /** Send Event Message on to rfcomm connected devices. */
sendEventStreamMessageToRfcommDevices(EventGroup eventGroup)543     public void sendEventStreamMessageToRfcommDevices(EventGroup eventGroup) {
544         // Send fake log when event code is logging and type is not using Log_Full event.
545         if (eventGroup == EventGroup.LOGGING && !mUseLogFullEvent) {
546             mRfcommServer.sendFakeEventStreamLoggingMessage(
547                     getDeviceName()
548                             + " "
549                             + getBleAddress()
550                             + " send log at "
551                             + new SimpleDateFormat("HH:mm:ss:SSS", Locale.US)
552                             .format(Calendar.getInstance().getTime()));
553         } else {
554             mRfcommServer.sendFakeEventStreamMessage(eventGroup);
555         }
556     }
557 
setUseLogFullEvent(boolean useLogFullEvent)558     public void setUseLogFullEvent(boolean useLogFullEvent) {
559         this.mUseLogFullEvent = useLogFullEvent;
560     }
561 
562     /** An optional way to get advertising status updates. */
563     public interface AdvertisingChangedCallback {
564         /**
565          * Called when we change our BLE advertisement.
566          *
567          * @param isAdvertising the advertising status.
568          */
onAdvertisingChanged(boolean isAdvertising)569         void onAdvertisingChanged(boolean isAdvertising);
570     }
571 
572     /** A way for tests to get callbacks when passkey confirmation is invoked. */
573     public interface PasskeyConfirmationCallback {
onPasskeyConfirmation(boolean confirm)574         void onPasskeyConfirmation(boolean confirm);
575     }
576 
577     /** A way for simulator UI update to get callback when device name is changed. */
578     public interface DeviceNameCallback {
onNameChanged(String deviceName)579         void onNameChanged(String deviceName);
580     }
581 
582     /**
583      * Callback when there comes a passkey input request from BT service, or receiving remote
584      * device's passkey.
585      */
586     public interface PasskeyEventCallback {
onPasskeyRequested(KeyInputCallback keyInputCallback)587         void onPasskeyRequested(KeyInputCallback keyInputCallback);
588 
onRemotePasskeyReceived(int passkey)589         void onRemotePasskeyReceived(int passkey);
590 
onPasskeyConfirmation(int passkey, Consumer<Boolean> isConfirmed)591         default void onPasskeyConfirmation(int passkey, Consumer<Boolean> isConfirmed) {
592         }
593     }
594 
595     /** Options for the simulator. */
596     public static class Options {
597         private final String mModelId;
598 
599         // TODO(b/143117318):Remove this when app-launch type has its own anti-spoofing key.
600         private final String mAdvertisingModelId;
601 
602         @Nullable
603         private final String mBluetoothAddress;
604 
605         @Nullable
606         private final String mBleAddress;
607 
608         private final boolean mDataOnlyConnection;
609 
610         private final int mTxPowerLevel;
611 
612         private final boolean mEnableNameCharacteristic;
613 
614         private final AdvertisingChangedCallback mAdvertisingChangedCallback;
615 
616         private final boolean mIncludeTransportDataDescriptor;
617 
618         @Nullable
619         private final byte[] mAntiSpoofingPrivateKey;
620 
621         private final boolean mUseRandomSaltForAccountKeyRotation;
622 
623         private final boolean mBecomeDiscoverable;
624 
625         private final boolean mShowsPasskeyConfirmation;
626 
627         private final boolean mEnableBeaconActionsCharacteristic;
628 
629         private final boolean mRemoveAllDevicesDuringPairing;
630 
631         @Nullable
632         private final ByteString mEddystoneIdentityKey;
633 
Options( String modelId, String advertisingModelId, @Nullable String bluetoothAddress, @Nullable String bleAddress, boolean dataOnlyConnection, int txPowerLevel, boolean enableNameCharacteristic, AdvertisingChangedCallback advertisingChangedCallback, boolean includeTransportDataDescriptor, @Nullable byte[] antiSpoofingPrivateKey, boolean useRandomSaltForAccountKeyRotation, boolean becomeDiscoverable, boolean showsPasskeyConfirmation, boolean enableBeaconActionsCharacteristic, boolean removeAllDevicesDuringPairing, @Nullable ByteString eddystoneIdentityKey)634         private Options(
635                 String modelId,
636                 String advertisingModelId,
637                 @Nullable String bluetoothAddress,
638                 @Nullable String bleAddress,
639                 boolean dataOnlyConnection,
640                 int txPowerLevel,
641                 boolean enableNameCharacteristic,
642                 AdvertisingChangedCallback advertisingChangedCallback,
643                 boolean includeTransportDataDescriptor,
644                 @Nullable byte[] antiSpoofingPrivateKey,
645                 boolean useRandomSaltForAccountKeyRotation,
646                 boolean becomeDiscoverable,
647                 boolean showsPasskeyConfirmation,
648                 boolean enableBeaconActionsCharacteristic,
649                 boolean removeAllDevicesDuringPairing,
650                 @Nullable ByteString eddystoneIdentityKey) {
651             this.mModelId = modelId;
652             this.mAdvertisingModelId = advertisingModelId;
653             this.mBluetoothAddress = bluetoothAddress;
654             this.mBleAddress = bleAddress;
655             this.mDataOnlyConnection = dataOnlyConnection;
656             this.mTxPowerLevel = txPowerLevel;
657             this.mEnableNameCharacteristic = enableNameCharacteristic;
658             this.mAdvertisingChangedCallback = advertisingChangedCallback;
659             this.mIncludeTransportDataDescriptor = includeTransportDataDescriptor;
660             this.mAntiSpoofingPrivateKey = antiSpoofingPrivateKey;
661             this.mUseRandomSaltForAccountKeyRotation = useRandomSaltForAccountKeyRotation;
662             this.mBecomeDiscoverable = becomeDiscoverable;
663             this.mShowsPasskeyConfirmation = showsPasskeyConfirmation;
664             this.mEnableBeaconActionsCharacteristic = enableBeaconActionsCharacteristic;
665             this.mRemoveAllDevicesDuringPairing = removeAllDevicesDuringPairing;
666             this.mEddystoneIdentityKey = eddystoneIdentityKey;
667         }
668 
getModelId()669         public String getModelId() {
670             return mModelId;
671         }
672 
673         // TODO(b/143117318):Remove this when app-launch type has its own anti-spoofing key.
getAdvertisingModelId()674         public String getAdvertisingModelId() {
675             return mAdvertisingModelId;
676         }
677 
678         @Nullable
getBluetoothAddress()679         public String getBluetoothAddress() {
680             return mBluetoothAddress;
681         }
682 
683         @Nullable
getBleAddress()684         public String getBleAddress() {
685             return mBleAddress;
686         }
687 
getDataOnlyConnection()688         public boolean getDataOnlyConnection() {
689             return mDataOnlyConnection;
690         }
691 
getTxPowerLevel()692         public int getTxPowerLevel() {
693             return mTxPowerLevel;
694         }
695 
getEnableNameCharacteristic()696         public boolean getEnableNameCharacteristic() {
697             return mEnableNameCharacteristic;
698         }
699 
getAdvertisingChangedCallback()700         public AdvertisingChangedCallback getAdvertisingChangedCallback() {
701             return mAdvertisingChangedCallback;
702         }
703 
getIncludeTransportDataDescriptor()704         public boolean getIncludeTransportDataDescriptor() {
705             return mIncludeTransportDataDescriptor;
706         }
707 
708         @Nullable
getAntiSpoofingPrivateKey()709         public byte[] getAntiSpoofingPrivateKey() {
710             return mAntiSpoofingPrivateKey;
711         }
712 
getUseRandomSaltForAccountKeyRotation()713         public boolean getUseRandomSaltForAccountKeyRotation() {
714             return mUseRandomSaltForAccountKeyRotation;
715         }
716 
getBecomeDiscoverable()717         public boolean getBecomeDiscoverable() {
718             return mBecomeDiscoverable;
719         }
720 
getShowsPasskeyConfirmation()721         public boolean getShowsPasskeyConfirmation() {
722             return mShowsPasskeyConfirmation;
723         }
724 
getEnableBeaconActionsCharacteristic()725         public boolean getEnableBeaconActionsCharacteristic() {
726             return mEnableBeaconActionsCharacteristic;
727         }
728 
getRemoveAllDevicesDuringPairing()729         public boolean getRemoveAllDevicesDuringPairing() {
730             return mRemoveAllDevicesDuringPairing;
731         }
732 
733         @Nullable
getEddystoneIdentityKey()734         public ByteString getEddystoneIdentityKey() {
735             return mEddystoneIdentityKey;
736         }
737 
738         /** Converts an instance to a builder. */
toBuilder()739         public Builder toBuilder() {
740             return new Options.Builder(this);
741         }
742 
743         /** Constructs a builder. */
builder()744         public static Builder builder() {
745             return new Options.Builder();
746         }
747 
748         /** @param modelId Must be a 3-byte hex string. */
builder(String modelId)749         public static Builder builder(String modelId) {
750             return new Options.Builder()
751                     .setModelId(Ascii.toUpperCase(modelId))
752                     .setAdvertisingModelId(Ascii.toUpperCase(modelId))
753                     .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
754                     .setAdvertisingChangedCallback(isAdvertising -> {
755                     })
756                     .setIncludeTransportDataDescriptor(true)
757                     .setUseRandomSaltForAccountKeyRotation(false)
758                     .setEnableNameCharacteristic(true)
759                     .setDataOnlyConnection(false)
760                     .setBecomeDiscoverable(true)
761                     .setShowsPasskeyConfirmation(false)
762                     .setEnableBeaconActionsCharacteristic(true)
763                     .setRemoveAllDevicesDuringPairing(true);
764         }
765 
766         /** A builder for {@link Options}. */
767         public static class Builder {
768 
769             private String mModelId;
770 
771             // TODO(b/143117318):Remove this when app-launch type has its own anti-spoofing key.
772             private String mAdvertisingModelId;
773 
774             @Nullable
775             private String mBluetoothAddress;
776 
777             @Nullable
778             private String mBleAddress;
779 
780             private boolean mDataOnlyConnection;
781 
782             private int mTxPowerLevel;
783 
784             private boolean mEnableNameCharacteristic;
785 
786             private AdvertisingChangedCallback mAdvertisingChangedCallback;
787 
788             private boolean mIncludeTransportDataDescriptor;
789 
790             @Nullable
791             private byte[] mAntiSpoofingPrivateKey;
792 
793             private boolean mUseRandomSaltForAccountKeyRotation;
794 
795             private boolean mBecomeDiscoverable;
796 
797             private boolean mShowsPasskeyConfirmation;
798 
799             private boolean mEnableBeaconActionsCharacteristic;
800 
801             private boolean mRemoveAllDevicesDuringPairing;
802 
803             @Nullable
804             private ByteString mEddystoneIdentityKey;
805 
Builder()806             private Builder() {
807             }
808 
Builder(Options option)809             private Builder(Options option) {
810                 this.mModelId = option.mModelId;
811                 this.mAdvertisingModelId = option.mAdvertisingModelId;
812                 this.mBluetoothAddress = option.mBluetoothAddress;
813                 this.mBleAddress = option.mBleAddress;
814                 this.mDataOnlyConnection = option.mDataOnlyConnection;
815                 this.mTxPowerLevel = option.mTxPowerLevel;
816                 this.mEnableNameCharacteristic = option.mEnableNameCharacteristic;
817                 this.mAdvertisingChangedCallback = option.mAdvertisingChangedCallback;
818                 this.mIncludeTransportDataDescriptor = option.mIncludeTransportDataDescriptor;
819                 this.mAntiSpoofingPrivateKey = option.mAntiSpoofingPrivateKey;
820                 this.mUseRandomSaltForAccountKeyRotation =
821                         option.mUseRandomSaltForAccountKeyRotation;
822                 this.mBecomeDiscoverable = option.mBecomeDiscoverable;
823                 this.mShowsPasskeyConfirmation = option.mShowsPasskeyConfirmation;
824                 this.mEnableBeaconActionsCharacteristic = option.mEnableBeaconActionsCharacteristic;
825                 this.mRemoveAllDevicesDuringPairing = option.mRemoveAllDevicesDuringPairing;
826                 this.mEddystoneIdentityKey = option.mEddystoneIdentityKey;
827             }
828 
829             /**
830              * Must be one of the {@code ADVERTISE_TX_POWER_*} levels in {@link AdvertiseSettings}.
831              * Default is HIGH.
832              */
setTxPowerLevel(int txPowerLevel)833             public Builder setTxPowerLevel(int txPowerLevel) {
834                 this.mTxPowerLevel = txPowerLevel;
835                 return this;
836             }
837 
838             /**
839              * Must be a 6-byte hex string (optionally with colons).
840              * Default is this device's BT MAC.
841              */
setBluetoothAddress(@ullable String bluetoothAddress)842             public Builder setBluetoothAddress(@Nullable String bluetoothAddress) {
843                 this.mBluetoothAddress = bluetoothAddress;
844                 return this;
845             }
846 
setBleAddress(@ullable String bleAddress)847             public Builder setBleAddress(@Nullable String bleAddress) {
848                 this.mBleAddress = bleAddress;
849                 return this;
850             }
851 
852             /** A boolean to decide if enable name characteristic as simulator characteristic. */
setEnableNameCharacteristic(boolean enable)853             public Builder setEnableNameCharacteristic(boolean enable) {
854                 this.mEnableNameCharacteristic = enable;
855                 return this;
856             }
857 
858             /** @see AdvertisingChangedCallback */
setAdvertisingChangedCallback( AdvertisingChangedCallback advertisingChangedCallback)859             public Builder setAdvertisingChangedCallback(
860                     AdvertisingChangedCallback advertisingChangedCallback) {
861                 this.mAdvertisingChangedCallback = advertisingChangedCallback;
862                 return this;
863             }
864 
setDataOnlyConnection(boolean dataOnlyConnection)865             public Builder setDataOnlyConnection(boolean dataOnlyConnection) {
866                 this.mDataOnlyConnection = dataOnlyConnection;
867                 return this;
868             }
869 
870             /**
871              * Set whether to include the Transport Data descriptor, which has the list of supported
872              * profiles. This is required by the spec, but if we can't get it, we recover gracefully
873              * by assuming support for one of {A2DP, Headset}. Default is true.
874              */
setIncludeTransportDataDescriptor( boolean includeTransportDataDescriptor)875             public Builder setIncludeTransportDataDescriptor(
876                     boolean includeTransportDataDescriptor) {
877                 this.mIncludeTransportDataDescriptor = includeTransportDataDescriptor;
878                 return this;
879             }
880 
setAntiSpoofingPrivateKey(@ullable byte[] antiSpoofingPrivateKey)881             public Builder setAntiSpoofingPrivateKey(@Nullable byte[] antiSpoofingPrivateKey) {
882                 this.mAntiSpoofingPrivateKey = antiSpoofingPrivateKey;
883                 return this;
884             }
885 
setUseRandomSaltForAccountKeyRotation( boolean useRandomSaltForAccountKeyRotation)886             public Builder setUseRandomSaltForAccountKeyRotation(
887                     boolean useRandomSaltForAccountKeyRotation) {
888                 this.mUseRandomSaltForAccountKeyRotation = useRandomSaltForAccountKeyRotation;
889                 return this;
890             }
891 
892             // TODO(b/143117318):Remove this when app-launch type has its own anti-spoofing key.
setAdvertisingModelId(String modelId)893             public Builder setAdvertisingModelId(String modelId) {
894                 this.mAdvertisingModelId = modelId;
895                 return this;
896             }
897 
setBecomeDiscoverable(boolean becomeDiscoverable)898             public Builder setBecomeDiscoverable(boolean becomeDiscoverable) {
899                 this.mBecomeDiscoverable = becomeDiscoverable;
900                 return this;
901             }
902 
setShowsPasskeyConfirmation(boolean showsPasskeyConfirmation)903             public Builder setShowsPasskeyConfirmation(boolean showsPasskeyConfirmation) {
904                 this.mShowsPasskeyConfirmation = showsPasskeyConfirmation;
905                 return this;
906             }
907 
setEnableBeaconActionsCharacteristic( boolean enableBeaconActionsCharacteristic)908             public Builder setEnableBeaconActionsCharacteristic(
909                     boolean enableBeaconActionsCharacteristic) {
910                 this.mEnableBeaconActionsCharacteristic = enableBeaconActionsCharacteristic;
911                 return this;
912             }
913 
setRemoveAllDevicesDuringPairing(boolean removeAllDevicesDuringPairing)914             public Builder setRemoveAllDevicesDuringPairing(boolean removeAllDevicesDuringPairing) {
915                 this.mRemoveAllDevicesDuringPairing = removeAllDevicesDuringPairing;
916                 return this;
917             }
918 
919             /**
920              * Non-public because this is required to create a builder. See
921              * {@link Options#builder}.
922              */
setModelId(String modelId)923             public Builder setModelId(String modelId) {
924                 this.mModelId = modelId;
925                 return this;
926             }
927 
setEddystoneIdentityKey(@ullable ByteString eddystoneIdentityKey)928             public Builder setEddystoneIdentityKey(@Nullable ByteString eddystoneIdentityKey) {
929                 this.mEddystoneIdentityKey = eddystoneIdentityKey;
930                 return this;
931             }
932 
933             // Custom builder in order to normalize properties. go/autovalue/builders-howto
build()934             public Options build() {
935                 return new Options(
936                         Ascii.toUpperCase(mModelId),
937                         Ascii.toUpperCase(mAdvertisingModelId),
938                         mBluetoothAddress,
939                         mBleAddress,
940                         mDataOnlyConnection,
941                         mTxPowerLevel,
942                         mEnableNameCharacteristic,
943                         mAdvertisingChangedCallback,
944                         mIncludeTransportDataDescriptor,
945                         mAntiSpoofingPrivateKey,
946                         mUseRandomSaltForAccountKeyRotation,
947                         mBecomeDiscoverable,
948                         mShowsPasskeyConfirmation,
949                         mEnableBeaconActionsCharacteristic,
950                         mRemoveAllDevicesDuringPairing,
951                         mEddystoneIdentityKey);
952             }
953         }
954     }
955 
FastPairSimulator(Context context, Options options)956     public FastPairSimulator(Context context, Options options) {
957         this(context, options, new Logger(TAG));
958     }
959 
FastPairSimulator(Context context, Options options, Logger logger)960     public FastPairSimulator(Context context, Options options, Logger logger) {
961         this.mContext = context;
962         this.mOptions = options;
963         this.mLogger = logger;
964 
965         this.mBatteryValues = new ArrayList<>();
966 
967         String bluetoothAddress =
968                 !TextUtils.isEmpty(options.getBluetoothAddress())
969                         ? options.getBluetoothAddress()
970                         : Settings.Secure.getString(context.getContentResolver(),
971                                 "bluetooth_address");
972         if (bluetoothAddress == null && VERSION.SDK_INT >= VERSION_CODES.O) {
973             // Requires a modified Android O build for access to bluetoothAdapter.getAddress().
974             // See http://google3/java/com/google/location/nearby/apps/fastpair/simulator/README.md.
975             bluetoothAddress = mBluetoothAdapter.getAddress();
976         }
977         this.mBluetoothAddress =
978                 new Value(BluetoothAddress.decode(bluetoothAddress), ByteOrder.BIG_ENDIAN);
979         this.mBleAddress = options.getBleAddress();
980         this.mAdvertiser = new OreoFastPairAdvertiser(this);
981 
982         mFastPairSimulatorDatabase = new FastPairSimulatorDatabase(context);
983 
984         byte[] deviceName = getDeviceNameInBytes();
985         mLogger.log(
986                 "Provider default device name is %s",
987                 deviceName != null ? new String(deviceName, StandardCharsets.UTF_8) : null);
988 
989         if (mOptions.getDataOnlyConnection()) {
990             // To get BLE address, we need to start advertising first, and then
991             // {@code#setBleAddress} will be called with BLE address.
992             mAdvertiser.startAdvertising(modelIdServiceData(/* forAdvertising= */ true));
993         } else {
994             // Make this so that the simulator doesn't start automatically.
995             // This is tricky since the simulator is used in our integ tests as well.
996             start(mBleAddress != null ? mBleAddress : bluetoothAddress);
997         }
998     }
999 
start(String address)1000     public void start(String address) {
1001         IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
1002         filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
1003         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
1004         filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
1005         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
1006         mContext.registerReceiver(mBroadcastReceiver, filter);
1007 
1008         BluetoothManager bluetoothManager = mContext.getSystemService(BluetoothManager.class);
1009         BluetoothGattServerHelper bluetoothGattServerHelper =
1010                 new BluetoothGattServerHelper(mContext, wrap(bluetoothManager));
1011         mBluetoothGattServerHelpers.put(address, bluetoothGattServerHelper);
1012 
1013         if (mOptions.getBecomeDiscoverable()) {
1014             try {
1015                 becomeDiscoverable();
1016             } catch (InterruptedException | TimeoutException e) {
1017                 mLogger.log(e, "Error becoming discoverable");
1018             }
1019         }
1020 
1021         mAdvertiser.startAdvertising(modelIdServiceData(/* forAdvertising= */ true));
1022         startGattServer(bluetoothGattServerHelper);
1023         startRfcommServer();
1024         scheduleAdvertisingRefresh();
1025     }
1026 
1027     /**
1028      * Regenerate service data on a fixed interval.
1029      * This causes the bloom filter to be refreshed and a different salt to be used for rotation.
1030      */
1031     @SuppressWarnings("FutureReturnValueIgnored")
scheduleAdvertisingRefresh()1032     private void scheduleAdvertisingRefresh() {
1033         mExecutor.scheduleAtFixedRate(() -> {
1034             if (mIsAdvertising) {
1035                 mAdvertiser.startAdvertising(getServiceData());
1036             }
1037         }, ADVERTISING_REFRESH_DELAY_1_MIN, ADVERTISING_REFRESH_DELAY_1_MIN, TimeUnit.MILLISECONDS);
1038     }
1039 
destroy()1040     public void destroy() {
1041         try {
1042             mLogger.log("Destroying simulator");
1043             mIsDestroyed = true;
1044             mContext.unregisterReceiver(mBroadcastReceiver);
1045             mAdvertiser.stopAdvertising();
1046             for (BluetoothGattServerHelper helper : mBluetoothGattServerHelpers.values()) {
1047                 helper.close();
1048             }
1049             stopRfcommServer();
1050             mDeviceNameCallback = null;
1051             mExecutor.shutdownNow();
1052         } catch (IllegalArgumentException ignored) {
1053             // Happens if you haven't given us permissions yet, so we didn't register the receiver.
1054         }
1055     }
1056 
isDestroyed()1057     public boolean isDestroyed() {
1058         return mIsDestroyed;
1059     }
1060 
1061     @Nullable
getBluetoothAddress()1062     public String getBluetoothAddress() {
1063         return BluetoothAddress.encode(mBluetoothAddress.getBytes(ByteOrder.BIG_ENDIAN));
1064     }
1065 
isAdvertising()1066     public boolean isAdvertising() {
1067         return mIsAdvertising;
1068     }
1069 
setIsAdvertising(boolean isAdvertising)1070     public void setIsAdvertising(boolean isAdvertising) {
1071         if (this.mIsAdvertising != isAdvertising) {
1072             this.mIsAdvertising = isAdvertising;
1073             mOptions.getAdvertisingChangedCallback().onAdvertisingChanged(isAdvertising);
1074         }
1075     }
1076 
stopAdvertising()1077     public void stopAdvertising() {
1078         mAdvertiser.stopAdvertising();
1079     }
1080 
setBleAddress(String bleAddress)1081     public void setBleAddress(String bleAddress) {
1082         this.mBleAddress = bleAddress;
1083         if (mOptions.getDataOnlyConnection()) {
1084             mBluetoothAddress = new Value(BluetoothAddress.decode(bleAddress),
1085                     ByteOrder.BIG_ENDIAN);
1086             start(bleAddress);
1087         }
1088         // When BLE address changes, needs to send BLE address to the client again.
1089         sendDeviceBleAddress(bleAddress);
1090 
1091         // If we are advertising something other than the model id (e.g. the bloom filter), restart
1092         // the advertisement so that it is updated with the new address.
1093         if (isAdvertising() && !isDiscoverable()) {
1094             mAdvertiser.startAdvertising(getServiceData());
1095         }
1096     }
1097 
1098     @Nullable
getBleAddress()1099     public String getBleAddress() {
1100         return mBleAddress;
1101     }
1102 
1103     // This method is only for testing to make test block until write name success or time out.
1104     @VisibleForTesting
setCountDownLatchToWriteName(CountDownLatch countDownLatch)1105     public void setCountDownLatchToWriteName(CountDownLatch countDownLatch) {
1106         mLogger.log("Set up count down latch to write device name.");
1107         mWriteNameCountDown = countDownLatch;
1108     }
1109 
areBeaconActionsNotificationsEnabled()1110     public boolean areBeaconActionsNotificationsEnabled() {
1111         return mBeaconActionsServlet.areNotificationsEnabled();
1112     }
1113 
1114     private abstract class NotifiableGattServlet extends BluetoothGattServlet {
1115         private final Map<BluetoothGattServerConnection, Notifier> mConnections = new HashMap<>();
1116 
getBaseCharacteristic()1117         abstract BluetoothGattCharacteristic getBaseCharacteristic();
1118 
1119         @Override
getCharacteristic()1120         public BluetoothGattCharacteristic getCharacteristic() {
1121             // Enabling indication requires the Client Characteristic Configuration descriptor.
1122             BluetoothGattCharacteristic characteristic = getBaseCharacteristic();
1123             characteristic.addDescriptor(
1124                     new BluetoothGattDescriptor(
1125                             Constants.CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR_UUID,
1126                             BluetoothGattDescriptor.PERMISSION_READ
1127                                     | BluetoothGattDescriptor.PERMISSION_WRITE));
1128             return characteristic;
1129         }
1130 
1131         @Override
enableNotification(BluetoothGattServerConnection connection, Notifier notifier)1132         public void enableNotification(BluetoothGattServerConnection connection, Notifier notifier)
1133                 throws BluetoothGattException {
1134             mLogger.log("Registering notifier for %s", getCharacteristic());
1135             mConnections.put(connection, notifier);
1136         }
1137 
1138         @Override
disableNotification(BluetoothGattServerConnection connection, Notifier notifier)1139         public void disableNotification(BluetoothGattServerConnection connection, Notifier notifier)
1140                 throws BluetoothGattException {
1141             mLogger.log("Removing notifier for %s", getCharacteristic());
1142             mConnections.remove(connection);
1143         }
1144 
areNotificationsEnabled()1145         boolean areNotificationsEnabled() {
1146             return !mConnections.isEmpty();
1147         }
1148 
sendNotification(byte[] data)1149         void sendNotification(byte[] data) {
1150             if (mConnections.isEmpty()) {
1151                 mLogger.log("Not sending notify as no notifier registered");
1152                 return;
1153             }
1154             // Needs to be on a separate thread to avoid deadlocking and timing out (waits for a
1155             // callback from OS, which happens on the main thread).
1156             mExecutor.execute(
1157                     () -> {
1158                         for (Map.Entry<BluetoothGattServerConnection, Notifier> entry :
1159                                 mConnections.entrySet()) {
1160                             try {
1161                                 mLogger.log("Sending notify %s to %s",
1162                                         getCharacteristic(),
1163                                         entry.getKey().getDevice().getAddress());
1164                                 entry.getValue().notify(data);
1165                             } catch (BluetoothException e) {
1166                                 mLogger.log(
1167                                         e,
1168                                         "Failed to notify (indicate) result of %s to %s",
1169                                         getCharacteristic(),
1170                                         entry.getKey().getDevice().getAddress());
1171                             }
1172                         }
1173                     });
1174         }
1175     }
1176 
startRfcommServer()1177     private void startRfcommServer() {
1178         mRfcommServer.setRequestHandler(this::handleRfcommServerRequest);
1179         mRfcommServer.setStateMonitor(state -> {
1180             mLogger.log("RfcommServer is in %s state", state);
1181             if (CONNECTED.equals(state)) {
1182                 sendModelId();
1183                 sendDeviceBleAddress(mBleAddress);
1184                 sendFirmwareVersion();
1185                 sendSessionNonce();
1186             }
1187         });
1188         mRfcommServer.start();
1189     }
1190 
handleRfcommServerRequest(int eventGroup, int eventCode, byte[] data)1191     private void handleRfcommServerRequest(int eventGroup, int eventCode, byte[] data) {
1192         switch (eventGroup) {
1193             case EventGroup.DEVICE_VALUE:
1194                 if (data == null) {
1195                     break;
1196                 }
1197 
1198                 String deviceValue = base16().encode(data);
1199                 if (eventCode == DeviceEventCode.DEVICE_CAPABILITY_VALUE) {
1200                     mLogger.log("Received phone capability: %s", deviceValue);
1201                 } else if (eventCode == DeviceEventCode.PLATFORM_TYPE_VALUE) {
1202                     mLogger.log("Received platform type: %s", deviceValue);
1203                 }
1204                 break;
1205             case EventGroup.DEVICE_ACTION_VALUE:
1206                 if (eventCode == DeviceActionEventCode.DEVICE_ACTION_RING_VALUE) {
1207                     mLogger.log("receive device action with ring value, data = %d",
1208                             data[0]);
1209                     sendDeviceRingActionResponse();
1210                     // Simulate notifying the seeker that the ringing has stopped due
1211                     // to user interaction (such as tapping the bud).
1212                     mUiThreadHandler.postDelayed(this::sendDeviceRingStoppedAction,
1213                             5000);
1214                 }
1215                 break;
1216             case EventGroup.DEVICE_CONFIGURATION_VALUE:
1217                 if (eventCode == DeviceConfigurationEventCode.CONFIGURATION_BUFFER_SIZE_VALUE) {
1218                     mLogger.log(
1219                             "receive device action with buffer size value, data = %s",
1220                             base16().encode(data));
1221                     sendSetBufferActionResponse(data);
1222                 }
1223                 break;
1224             case EventGroup.DEVICE_CAPABILITY_SYNC_VALUE:
1225                 if (eventCode == DeviceCapabilitySyncEventCode.REQUEST_CAPABILITY_UPDATE_VALUE) {
1226                     mLogger.log("receive device capability update request.");
1227                     sendCapabilitySync();
1228                 }
1229                 break;
1230             default: // fall out
1231                 break;
1232         }
1233     }
1234 
stopRfcommServer()1235     private void stopRfcommServer() {
1236         mRfcommServer.stop();
1237         mRfcommServer.setRequestHandler(null);
1238         mRfcommServer.setStateMonitor(null);
1239     }
1240 
sendModelId()1241     private void sendModelId() {
1242         mLogger.log("Send model ID to the client");
1243         mRfcommServer.send(
1244                 EventGroup.DEVICE_VALUE,
1245                 DeviceEventCode.DEVICE_MODEL_ID_VALUE,
1246                 modelIdServiceData(/* forAdvertising= */ false));
1247     }
1248 
sendDeviceBleAddress(String bleAddress)1249     private void sendDeviceBleAddress(String bleAddress) {
1250         mLogger.log("Send BLE address (%s) to the client", bleAddress);
1251         if (bleAddress != null) {
1252             mRfcommServer.send(
1253                     EventGroup.DEVICE_VALUE,
1254                     DeviceEventCode.DEVICE_BLE_ADDRESS_VALUE,
1255                     BluetoothAddress.decode(bleAddress));
1256         }
1257     }
1258 
sendFirmwareVersion()1259     private void sendFirmwareVersion() {
1260         mLogger.log("Send Firmware Version (%s) to the client", mDeviceFirmwareVersion);
1261         mRfcommServer.send(
1262                 EventGroup.DEVICE_VALUE,
1263                 DeviceEventCode.FIRMWARE_VERSION_VALUE,
1264                 mDeviceFirmwareVersion.getBytes());
1265     }
1266 
sendSessionNonce()1267     private void sendSessionNonce() {
1268         mLogger.log("Send SessionNonce (%s) to the client", mDeviceFirmwareVersion);
1269         SecureRandom secureRandom = new SecureRandom();
1270         mSessionNonce = new byte[SECTION_NONCE_LENGTH];
1271         secureRandom.nextBytes(mSessionNonce);
1272         mRfcommServer.send(
1273                 EventGroup.DEVICE_VALUE, DeviceEventCode.SECTION_NONCE_VALUE, mSessionNonce);
1274     }
1275 
sendDeviceRingActionResponse()1276     private void sendDeviceRingActionResponse() {
1277         mLogger.log("Send device ring action response to the client");
1278         mRfcommServer.send(
1279                 EventGroup.ACKNOWLEDGEMENT_VALUE,
1280                 AcknowledgementEventCode.ACKNOWLEDGEMENT_ACK_VALUE,
1281                 new byte[]{
1282                         EventGroup.DEVICE_ACTION_VALUE,
1283                         DeviceActionEventCode.DEVICE_ACTION_RING_VALUE
1284                 });
1285     }
1286 
sendSetBufferActionResponse(byte[] data)1287     private void sendSetBufferActionResponse(byte[] data) {
1288         boolean hmacPassed = false;
1289         for (ByteString accountKey : getAccountKeys()) {
1290             try {
1291                 if (MessageStreamHmacEncoder.verifyHmac(
1292                         accountKey.toByteArray(), mSessionNonce, data)) {
1293                     hmacPassed = true;
1294                     mLogger.log("Buffer size data matches account key %s",
1295                             base16().encode(accountKey.toByteArray()));
1296                     break;
1297                 }
1298             } catch (GeneralSecurityException e) {
1299                 // Ignore.
1300             }
1301         }
1302         if (hmacPassed) {
1303             mLogger.log("Send buffer size action response %s to the client", base16().encode(data));
1304             mRfcommServer.send(
1305                     EventGroup.ACKNOWLEDGEMENT_VALUE,
1306                     AcknowledgementEventCode.ACKNOWLEDGEMENT_ACK_VALUE,
1307                     new byte[]{
1308                             EventGroup.DEVICE_CONFIGURATION_VALUE,
1309                             DeviceConfigurationEventCode.CONFIGURATION_BUFFER_SIZE_VALUE,
1310                             data[0],
1311                             data[1],
1312                             data[2]
1313                     });
1314         } else {
1315             mLogger.log("No matched account key for sendSetBufferActionResponse");
1316         }
1317     }
1318 
sendCapabilitySync()1319     private void sendCapabilitySync() {
1320         mLogger.log("Send capability sync to the client");
1321         if (mSupportDynamicBufferSize) {
1322             mLogger.log("Send dynamic buffer size range to the client");
1323             mRfcommServer.send(
1324                     EventGroup.DEVICE_CAPABILITY_SYNC_VALUE,
1325                     DeviceCapabilitySyncEventCode.CONFIGURABLE_BUFFER_SIZE_RANGE_VALUE,
1326                     new byte[]{
1327                             0x00, 0x01, (byte) 0xf4, 0x00, 0x64, 0x00, (byte) 0xc8,
1328                             0x01, 0x00, (byte) 0xff, 0x00, 0x01, 0x00, (byte) 0x88,
1329                             0x02, 0x01, (byte) 0xff, 0x01, 0x01, 0x01, (byte) 0x88,
1330                             0x03, 0x02, (byte) 0xff, 0x02, 0x01, 0x02, (byte) 0x88,
1331                             0x04, 0x03, (byte) 0xff, 0x03, 0x01, 0x03, (byte) 0x88
1332                     });
1333         }
1334     }
1335 
sendDeviceRingStoppedAction()1336     private void sendDeviceRingStoppedAction() {
1337         mLogger.log("Sending device ring stopped action to the client");
1338         mRfcommServer.send(
1339                 EventGroup.DEVICE_ACTION_VALUE,
1340                 DeviceActionEventCode.DEVICE_ACTION_RING_VALUE,
1341                 // Additional data for stopping ringing on all components.
1342                 new byte[]{0x00});
1343     }
1344 
startGattServer(BluetoothGattServerHelper helper)1345     private void startGattServer(BluetoothGattServerHelper helper) {
1346         BluetoothGattServlet tdsControlPointServlet =
1347                 new NotifiableGattServlet() {
1348                     @Override
1349                     public BluetoothGattCharacteristic getBaseCharacteristic() {
1350                         return new BluetoothGattCharacteristic(ControlPointCharacteristic.ID,
1351                                 PROPERTY_WRITE | PROPERTY_INDICATE, PERMISSION_WRITE);
1352                     }
1353 
1354                     @Override
1355                     public void write(
1356                             BluetoothGattServerConnection connection, int offset, byte[] value)
1357                             throws BluetoothGattException {
1358                         mLogger.log("Requested TDS Control Point write, value=%s",
1359                                 base16().encode(value));
1360 
1361                         ResultCode resultCode = checkTdsControlPointRequest(value);
1362                         if (resultCode == ResultCode.SUCCESS) {
1363                             try {
1364                                 becomeDiscoverable();
1365                             } catch (TimeoutException | InterruptedException e) {
1366                                 mLogger.log(e, "Failed to become discoverable");
1367                                 resultCode = ResultCode.OPERATION_FAILED;
1368                             }
1369                         }
1370 
1371                         mLogger.log("Request complete, resultCode=%s", resultCode);
1372 
1373                         mLogger.log("Sending TDS Control Point response indication");
1374                         sendNotification(
1375                                 Bytes.concat(
1376                                         new byte[]{
1377                                                 getTdsControlPointOpCode(value),
1378                                                 resultCode.mByteValue,
1379                                         },
1380                                         resultCode == ResultCode.SUCCESS
1381                                                 ? TDS_CONTROL_POINT_RESPONSE_PARAMETER
1382                                                 : new byte[0]));
1383                     }
1384                 };
1385 
1386         BluetoothGattServlet brHandoverDataServlet =
1387                 new BluetoothGattServlet() {
1388 
1389                     @Override
1390                     public BluetoothGattCharacteristic getCharacteristic() {
1391                         return new BluetoothGattCharacteristic(BrHandoverDataCharacteristic.ID,
1392                                 PROPERTY_READ, PERMISSION_READ);
1393                     }
1394 
1395                     @Override
1396                     public byte[] read(BluetoothGattServerConnection connection, int offset) {
1397                         return Bytes.concat(
1398                                 new byte[]{BrHandoverDataCharacteristic.BR_EDR_FEATURES},
1399                                 mBluetoothAddress.getBytes(ByteOrder.LITTLE_ENDIAN),
1400                                 CLASS_OF_DEVICE.getBytes(ByteOrder.LITTLE_ENDIAN));
1401                     }
1402                 };
1403 
1404         BluetoothGattServlet bluetoothSigServlet =
1405                 new BluetoothGattServlet() {
1406 
1407                     @Override
1408                     public BluetoothGattCharacteristic getCharacteristic() {
1409                         BluetoothGattCharacteristic characteristic =
1410                                 new BluetoothGattCharacteristic(
1411                                         TransportDiscoveryService.BluetoothSigDataCharacteristic.ID,
1412                                         0 /* no properties */,
1413                                         0 /* no permissions */);
1414 
1415                         if (mOptions.getIncludeTransportDataDescriptor()) {
1416                             characteristic.addDescriptor(
1417                                     new BluetoothGattDescriptor(
1418                                             TransportDiscoveryService.BluetoothSigDataCharacteristic
1419                                                     .BrTransportBlockDataDescriptor.ID,
1420                                             BluetoothGattDescriptor.PERMISSION_READ));
1421                         }
1422                         return characteristic;
1423                     }
1424 
1425                     @Override
1426                     public byte[] readDescriptor(
1427                             BluetoothGattServerConnection connection,
1428                             BluetoothGattDescriptor descriptor,
1429                             int offset)
1430                             throws BluetoothGattException {
1431                         return transportDiscoveryData();
1432                     }
1433                 };
1434 
1435         BluetoothGattServlet accountKeyServlet =
1436                 new BluetoothGattServlet() {
1437                     @Override
1438                     // Simulating deprecated API {@code AccountKeyCharacteristic.ID} for testing.
1439                     @SuppressWarnings("deprecation")
1440                     public BluetoothGattCharacteristic getCharacteristic() {
1441                         return new BluetoothGattCharacteristic(
1442                                 AccountKeyCharacteristic.CUSTOM_128_BIT_UUID,
1443                                 PROPERTY_WRITE,
1444                                 PERMISSION_WRITE);
1445                     }
1446 
1447                     @Override
1448                     public void write(
1449                             BluetoothGattServerConnection connection, int offset, byte[] value) {
1450                         mLogger.log("Got value from account key servlet: %s",
1451                                 base16().encode(value));
1452                         try {
1453                             addAccountKey(AesEcbSingleBlockEncryption.decrypt(mSecret, value),
1454                                     mPairingDevice);
1455                         } catch (GeneralSecurityException e) {
1456                             mLogger.log(e, "Failed to decrypt account key.");
1457                         }
1458                         mUiThreadHandler.post(
1459                                 () -> mAdvertiser.startAdvertising(accountKeysServiceData()));
1460                     }
1461                 };
1462 
1463         BluetoothGattServlet firmwareVersionServlet =
1464                 new BluetoothGattServlet() {
1465                     @Override
1466                     public BluetoothGattCharacteristic getCharacteristic() {
1467                         return new BluetoothGattCharacteristic(
1468                                 FirmwareVersionCharacteristic.ID, PROPERTY_READ, PERMISSION_READ);
1469                     }
1470 
1471                     @Override
1472                     public byte[] read(BluetoothGattServerConnection connection, int offset) {
1473                         return mDeviceFirmwareVersion.getBytes();
1474                     }
1475                 };
1476 
1477         BluetoothGattServlet keyBasedPairingServlet =
1478                 new NotifiableGattServlet() {
1479                     @Override
1480                     // Simulating deprecated API {@code KeyBasedPairingCharacteristic.ID} for
1481                     // testing.
1482                     @SuppressWarnings("deprecation")
1483                     public BluetoothGattCharacteristic getBaseCharacteristic() {
1484                         return new BluetoothGattCharacteristic(
1485                                 KeyBasedPairingCharacteristic.CUSTOM_128_BIT_UUID,
1486                                 PROPERTY_WRITE | PROPERTY_INDICATE,
1487                                 PERMISSION_WRITE);
1488                     }
1489 
1490                     @Override
1491                     public void write(
1492                             BluetoothGattServerConnection connection, int offset, byte[] value) {
1493                         mLogger.log("Requesting key based pairing handshake, value=%s",
1494                                 base16().encode(value));
1495 
1496                         mSecret = null;
1497                         byte[] seekerPublicAddress = null;
1498                         if (value.length == AES_BLOCK_LENGTH) {
1499 
1500                             for (ByteString key : getAccountKeys()) {
1501                                 byte[] candidateSecret = key.toByteArray();
1502                                 try {
1503                                     seekerPublicAddress = handshake(candidateSecret, value);
1504                                     mSecret = candidateSecret;
1505                                     mIsSubsequentPair = true;
1506                                     break;
1507                                 } catch (GeneralSecurityException e) {
1508                                     mLogger.log(e, "Failed to decrypt with %s",
1509                                             base16().encode(candidateSecret));
1510                                 }
1511                             }
1512                         } else if (value.length == AES_BLOCK_LENGTH + PUBLIC_KEY_LENGTH
1513                                 && mOptions.getAntiSpoofingPrivateKey() != null) {
1514                             try {
1515                                 byte[] encryptedRequest = Arrays.copyOf(value, AES_BLOCK_LENGTH);
1516                                 byte[] receivedPublicKey =
1517                                         Arrays.copyOfRange(value, AES_BLOCK_LENGTH, value.length);
1518                                 byte[] candidateSecret =
1519                                         EllipticCurveDiffieHellmanExchange.create(
1520                                                         mOptions.getAntiSpoofingPrivateKey())
1521                                                 .generateSecret(receivedPublicKey);
1522                                 seekerPublicAddress = handshake(candidateSecret, encryptedRequest);
1523                                 mSecret = candidateSecret;
1524                             } catch (Exception e) {
1525                                 mLogger.log(
1526                                         e,
1527                                         "Failed to decrypt with anti-spoofing private key %s",
1528                                         base16().encode(mOptions.getAntiSpoofingPrivateKey()));
1529                             }
1530                         } else {
1531                             mLogger.log("Packet length invalid, %d", value.length);
1532                             return;
1533                         }
1534 
1535                         if (mSecret == null) {
1536                             mLogger.log("Couldn't find a usable key to decrypt with.");
1537                             return;
1538                         }
1539 
1540                         mLogger.log("Found valid decryption key, %s", base16().encode(mSecret));
1541                         byte[] salt = new byte[9];
1542                         new Random().nextBytes(salt);
1543                         try {
1544                             byte[] data = concat(
1545                                     new byte[]{KeyBasedPairingCharacteristic.Response.TYPE},
1546                                     mBluetoothAddress.getBytes(ByteOrder.BIG_ENDIAN), salt);
1547                             byte[] encryptedAddress = encrypt(mSecret, data);
1548                             mLogger.log(
1549                                     "Sending handshake response %s with size %d",
1550                                     base16().encode(encryptedAddress), encryptedAddress.length);
1551                             sendNotification(encryptedAddress);
1552 
1553                             // Notify seeker for NameCharacteristic to get provider device name
1554                             // when seeker request device name flag is true.
1555                             if (mOptions.getEnableNameCharacteristic()
1556                                     && mHandshakeRequest.requestDeviceName()) {
1557                                 byte[] encryptedResponse =
1558                                         getDeviceNameInBytes() != null ? createEncryptedDeviceName()
1559                                                 : new byte[0];
1560                                 mLogger.log(
1561                                         "Sending device name response %s with size %d",
1562                                         base16().encode(encryptedResponse),
1563                                         encryptedResponse.length);
1564                                 mDeviceNameServlet.sendNotification(encryptedResponse);
1565                             }
1566 
1567                             // Disconnects the current connection to allow the following pairing
1568                             // request. Needs to be on a separate thread to avoid deadlocking and
1569                             // timing out (waits for a callback from OS, which happens on this
1570                             // thread).
1571                             //
1572                             // Note: The spec does not require you to disconnect from other
1573                             // devices at this point.
1574                             // If headphones support multiple simultaneous connections, they
1575                             // should stay connected. But Android fails to pair with the new
1576                             // device if we don't first disconnect from any other device.
1577                             mLogger.log("Skip remove bond, value=%s",
1578                                     mOptions.getRemoveAllDevicesDuringPairing());
1579                             if (mOptions.getRemoveAllDevicesDuringPairing()
1580                                     && mHandshakeRequest.getType()
1581                                     == HandshakeRequest.Type.KEY_BASED_PAIRING_REQUEST
1582                                     && !mHandshakeRequest.requestRetroactivePair()) {
1583                                 mExecutor.execute(() -> disconnectAllBondedDevices());
1584                             }
1585 
1586                             if (mHandshakeRequest.getType()
1587                                     == HandshakeRequest.Type.KEY_BASED_PAIRING_REQUEST
1588                                     && mHandshakeRequest.requestProviderInitialBonding()) {
1589                                 // Run on executor to ensure it doesn't happen until after the
1590                                 // notify (which tells the remote device what address to expect).
1591                                 String seekerPublicAddressString =
1592                                         BluetoothAddress.encode(seekerPublicAddress);
1593                                 mExecutor.execute(() -> {
1594                                     mLogger.log("Sending pairing request to %s",
1595                                             seekerPublicAddressString);
1596                                     mBluetoothAdapter.getRemoteDevice(
1597                                             seekerPublicAddressString).createBond();
1598                                 });
1599                             }
1600                         } catch (GeneralSecurityException e) {
1601                             mLogger.log(e, "Failed to notify of static mac address");
1602                         }
1603                     }
1604 
1605                     @Nullable
1606                     private byte[] handshake(byte[] key, byte[] encryptedPairingRequest)
1607                             throws GeneralSecurityException {
1608                         mHandshakeRequest = new HandshakeRequest(key, encryptedPairingRequest);
1609 
1610                         byte[] decryptedAddress = mHandshakeRequest.getVerificationData();
1611                         if (mBleAddress != null
1612                                 && Arrays.equals(decryptedAddress,
1613                                 BluetoothAddress.decode(mBleAddress))
1614                                 || Arrays.equals(decryptedAddress,
1615                                 mBluetoothAddress.getBytes(ByteOrder.BIG_ENDIAN))) {
1616                             mLogger.log("Address matches: %s", base16().encode(decryptedAddress));
1617                         } else {
1618                             throw new GeneralSecurityException(
1619                                     "Address (BLE or BR/EDR) is not correct: "
1620                                             + base16().encode(decryptedAddress)
1621                                             + ", "
1622                                             + mBleAddress
1623                                             + ", "
1624                                             + getBluetoothAddress());
1625                         }
1626 
1627                         switch (mHandshakeRequest.getType()) {
1628                             case KEY_BASED_PAIRING_REQUEST:
1629                                 return handleKeyBasedPairingRequest(mHandshakeRequest);
1630                             case ACTION_OVER_BLE:
1631                                 return handleActionOverBleRequest(mHandshakeRequest);
1632                             case UNKNOWN:
1633                                 // continue to throw the exception;
1634                         }
1635                         throw new GeneralSecurityException(
1636                                 "Type is not correct: " + mHandshakeRequest.getType());
1637                     }
1638 
1639                     @Nullable
1640                     private byte[] handleKeyBasedPairingRequest(HandshakeRequest handshakeRequest)
1641                             throws GeneralSecurityException {
1642                         if (handshakeRequest.requestDiscoverable()) {
1643                             mLogger.log("Requested discoverability");
1644                             setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
1645                         }
1646 
1647                         mLogger.log(
1648                                 "KeyBasedPairing: initialBonding=%s, requestDeviceName=%s, "
1649                                         + "retroactivePair=%s",
1650                                 handshakeRequest.requestProviderInitialBonding(),
1651                                 handshakeRequest.requestDeviceName(),
1652                                 handshakeRequest.requestRetroactivePair());
1653 
1654                         byte[] seekerPublicAddress = null;
1655                         if (handshakeRequest.requestProviderInitialBonding()
1656                                 || handshakeRequest.requestRetroactivePair()) {
1657                             seekerPublicAddress = handshakeRequest.getSeekerPublicAddress();
1658                             mLogger.log(
1659                                     "Seeker sends BR/EDR address %s to provider",
1660                                     BluetoothAddress.encode(seekerPublicAddress));
1661                         }
1662 
1663                         if (handshakeRequest.requestRetroactivePair()) {
1664                             if (mBluetoothAdapter.getRemoteDevice(
1665                                     seekerPublicAddress).getBondState()
1666                                     != BluetoothDevice.BOND_BONDED) {
1667                                 throw new GeneralSecurityException(
1668                                         "Address (BR/EDR) is not bonded: "
1669                                                 + BluetoothAddress.encode(seekerPublicAddress));
1670                             }
1671                         }
1672 
1673                         return seekerPublicAddress;
1674                     }
1675 
1676                     @Nullable
1677                     private byte[] handleActionOverBleRequest(HandshakeRequest handshakeRequest) {
1678                         // TODO(wollohchou): implement action over ble request.
1679                         if (handshakeRequest.requestDeviceAction()) {
1680                             mLogger.log("Requesting action over BLE, device action");
1681                         } else if (handshakeRequest.requestFollowedByAdditionalData()) {
1682                             mLogger.log(
1683                                     "Requesting action over BLE, followed by additional data, "
1684                                             + "type:%s",
1685                                     handshakeRequest.getAdditionalDataType());
1686                         } else {
1687                             mLogger.log("Requesting action over BLE");
1688                         }
1689                         return null;
1690                     }
1691 
1692                     /**
1693                      * @return The encrypted device name from provider for seeker to use.
1694                      */
1695                     private byte[] createEncryptedDeviceName() throws GeneralSecurityException {
1696                         byte[] deviceName = getDeviceNameInBytes();
1697                         String providerName = new String(deviceName, StandardCharsets.UTF_8);
1698                         mLogger.log(
1699                                 "Sending handshake response for device name %s with size %d",
1700                                 providerName, deviceName.length);
1701                         return NamingEncoder.encodeNamingPacket(mSecret, providerName);
1702                     }
1703                 };
1704 
1705         mBeaconActionsServlet =
1706                 new NotifiableGattServlet() {
1707                     private static final int GATT_ERROR_UNAUTHENTICATED = 0x80;
1708                     private static final int GATT_ERROR_INVALID_VALUE = 0x81;
1709                     private static final int NONCE_LENGTH = 8;
1710                     private static final int ONE_TIME_AUTH_KEY_OFFSET = 2;
1711                     private static final int ONE_TIME_AUTH_KEY_LENGTH = 8;
1712                     private static final int IDENTITY_KEY_LENGTH = 32;
1713                     private static final byte TRANSMISSION_POWER = 0;
1714 
1715                     private final SecureRandom mRandom = new SecureRandom();
1716                     private final MessageDigest mSha256;
1717                     @Nullable
1718                     private byte[] mLastNonce;
1719                     @Nullable
1720                     private ByteString mIdentityKey = mOptions.getEddystoneIdentityKey();
1721 
1722                     {
1723                         try {
1724                             mSha256 = MessageDigest.getInstance("SHA-256");
1725                             mSha256.reset();
1726                         } catch (NoSuchAlgorithmException e) {
1727                             throw new IllegalStateException(
1728                                     "System missing SHA-256 implementation.", e);
1729                         }
1730                     }
1731 
1732                     @Override
1733                     // Simulating deprecated API {@code BeaconActionsCharacteristic.ID} for testing.
1734                     @SuppressWarnings("deprecation")
1735                     public BluetoothGattCharacteristic getBaseCharacteristic() {
1736                         return new BluetoothGattCharacteristic(
1737                                 BeaconActionsCharacteristic.CUSTOM_128_BIT_UUID,
1738                                 PROPERTY_READ | PROPERTY_WRITE | PROPERTY_NOTIFY,
1739                                 PERMISSION_READ | PERMISSION_WRITE);
1740                     }
1741 
1742                     @Override
1743                     public byte[] read(BluetoothGattServerConnection connection, int offset) {
1744                         mLastNonce = new byte[NONCE_LENGTH];
1745                         mRandom.nextBytes(mLastNonce);
1746                         return mLastNonce;
1747                     }
1748 
1749                     @Override
1750                     public void write(
1751                             BluetoothGattServerConnection connection, int offset, byte[] value)
1752                             throws BluetoothGattException {
1753                         mLogger.log("Got value from beacon actions servlet: %s",
1754                                 base16().encode(value));
1755                         if (value.length == 0) {
1756                             mLogger.log("Packet length invalid, %d", value.length);
1757                             throw new BluetoothGattException("Packet length invalid",
1758                                     GATT_ERROR_INVALID_VALUE);
1759                         }
1760                         switch (value[0]) {
1761                             case BeaconActionType.READ_BEACON_PARAMETERS:
1762                                 handleReadBeaconParameters(value);
1763                                 break;
1764                             case BeaconActionType.READ_PROVISIONING_STATE:
1765                                 handleReadProvisioningState(value);
1766                                 break;
1767                             case BeaconActionType.SET_EPHEMERAL_IDENTITY_KEY:
1768                                 handleSetEphemeralIdentityKey(value);
1769                                 break;
1770                             case BeaconActionType.CLEAR_EPHEMERAL_IDENTITY_KEY:
1771                             case BeaconActionType.READ_EPHEMERAL_IDENTITY_KEY:
1772                             case BeaconActionType.RING:
1773                             case BeaconActionType.READ_RINGING_STATE:
1774                                 throw new BluetoothGattException(
1775                                         "Unimplemented beacon action",
1776                                         BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED);
1777                             default:
1778                                 throw new BluetoothGattException(
1779                                         "Unknown beacon action",
1780                                         BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED);
1781                         }
1782                     }
1783 
1784                     private boolean verifyAccountKeyToken(byte[] value, boolean ownerOnly)
1785                             throws BluetoothGattException {
1786                         if (value.length < ONE_TIME_AUTH_KEY_LENGTH + ONE_TIME_AUTH_KEY_OFFSET) {
1787                             mLogger.log("Packet length invalid, %d", value.length);
1788                             throw new BluetoothGattException(
1789                                     "Packet length invalid", GATT_ERROR_INVALID_VALUE);
1790                         }
1791                         byte[] hashedAccountKey =
1792                                 Arrays.copyOfRange(
1793                                         value,
1794                                         ONE_TIME_AUTH_KEY_OFFSET,
1795                                         ONE_TIME_AUTH_KEY_LENGTH + ONE_TIME_AUTH_KEY_OFFSET);
1796                         if (mLastNonce == null) {
1797                             throw new BluetoothGattException(
1798                                     "Nonce wasn't set", GATT_ERROR_UNAUTHENTICATED);
1799                         }
1800                         if (ownerOnly) {
1801                             ByteString accountKey = getOwnerAccountKey();
1802                             if (accountKey != null) {
1803                                 mSha256.update(accountKey.toByteArray());
1804                                 mSha256.update(mLastNonce);
1805                                 return Arrays.equals(
1806                                         hashedAccountKey,
1807                                         Arrays.copyOf(mSha256.digest(), ONE_TIME_AUTH_KEY_LENGTH));
1808                             }
1809                         } else {
1810                             Set<ByteString> accountKeys = getAccountKeys();
1811                             for (ByteString accountKey : accountKeys) {
1812                                 mSha256.update(accountKey.toByteArray());
1813                                 mSha256.update(mLastNonce);
1814                                 if (Arrays.equals(
1815                                         hashedAccountKey,
1816                                         Arrays.copyOf(mSha256.digest(),
1817                                                 ONE_TIME_AUTH_KEY_LENGTH))) {
1818                                     return true;
1819                                 }
1820                             }
1821                         }
1822                         return false;
1823                     }
1824 
1825                     private int getBeaconClock() {
1826                         return (int) TimeUnit.MILLISECONDS.toSeconds(SystemClock.elapsedRealtime());
1827                     }
1828 
1829                     private ByteString fromBytes(byte... bytes) {
1830                         return ByteString.copyFrom(bytes);
1831                     }
1832 
1833                     private byte[] intToByteArray(int value) {
1834                         byte[] data = new byte[4];
1835                         data[3] = (byte) value;
1836                         data[2] = (byte) (value >>> 8);
1837                         data[1] = (byte) (value >>> 16);
1838                         data[0] = (byte) (value >>> 24);
1839                         return data;
1840                     }
1841 
1842                     private void handleReadBeaconParameters(byte[] value)
1843                             throws BluetoothGattException {
1844                         if (!verifyAccountKeyToken(value, /* ownerOnly= */ false)) {
1845                             throw new BluetoothGattException(
1846                                     "failed to authenticate account key",
1847                                     GATT_ERROR_UNAUTHENTICATED);
1848                         }
1849                         sendNotification(
1850                                 fromBytes(
1851                                         (byte) BeaconActionType.READ_BEACON_PARAMETERS,
1852                                         (byte) 5 /* data length */,
1853                                         TRANSMISSION_POWER)
1854                                         .concat(ByteString.copyFrom(
1855                                                 intToByteArray(getBeaconClock())))
1856                                         .toByteArray());
1857                     }
1858 
1859                     private void handleReadProvisioningState(byte[] value)
1860                             throws BluetoothGattException {
1861                         if (!verifyAccountKeyToken(value, /* ownerOnly= */ false)) {
1862                             throw new BluetoothGattException(
1863                                     "failed to authenticate account key",
1864                                     GATT_ERROR_UNAUTHENTICATED);
1865                         }
1866                         byte flags = 0;
1867                         if (verifyAccountKeyToken(value, /* ownerOnly= */ true)) {
1868                             flags |= (byte) (1 << 1);
1869                         }
1870                         if (mIdentityKey == null) {
1871                             sendNotification(
1872                                     fromBytes(
1873                                             (byte) BeaconActionType.READ_PROVISIONING_STATE,
1874                                             (byte) 1 /* data length */,
1875                                             flags)
1876                                             .toByteArray());
1877                         } else {
1878                             flags |= (byte) 1;
1879                             sendNotification(
1880                                     fromBytes(
1881                                             (byte) BeaconActionType.READ_PROVISIONING_STATE,
1882                                             (byte) 21 /* data length */,
1883                                             flags)
1884                                             .concat(
1885                                                     E2eeCalculator.computeE2eeEid(
1886                                                             mIdentityKey, /* exponent= */ 10,
1887                                                             getBeaconClock()))
1888                                             .toByteArray());
1889                         }
1890                     }
1891 
1892                     private void handleSetEphemeralIdentityKey(byte[] value)
1893                             throws BluetoothGattException {
1894                         if (!verifyAccountKeyToken(value, /* ownerOnly= */ true)) {
1895                             throw new BluetoothGattException(
1896                                     "failed to authenticate owner account key",
1897                                     GATT_ERROR_UNAUTHENTICATED);
1898                         }
1899                         if (value.length
1900                                 != ONE_TIME_AUTH_KEY_LENGTH + ONE_TIME_AUTH_KEY_OFFSET
1901                                 + IDENTITY_KEY_LENGTH) {
1902                             mLogger.log("Packet length invalid, %d", value.length);
1903                             throw new BluetoothGattException("Packet length invalid",
1904                                     GATT_ERROR_INVALID_VALUE);
1905                         }
1906                         if (mIdentityKey != null) {
1907                             throw new BluetoothGattException(
1908                                     "Device is already provisioned as Eddystone",
1909                                     GATT_ERROR_UNAUTHENTICATED);
1910                         }
1911                         mIdentityKey = Crypto.aesEcbNoPaddingDecrypt(
1912                                 ByteString.copyFrom(mOwnerAccountKey),
1913                                 ByteString.copyFrom(value)
1914                                         .substring(ONE_TIME_AUTH_KEY_LENGTH
1915                                                 + ONE_TIME_AUTH_KEY_OFFSET));
1916                     }
1917                 };
1918 
1919         ServiceConfig fastPairServiceConfig =
1920                 new ServiceConfig()
1921                         .addCharacteristic(accountKeyServlet)
1922                         .addCharacteristic(keyBasedPairingServlet)
1923                         .addCharacteristic(mPasskeyServlet)
1924                         .addCharacteristic(firmwareVersionServlet);
1925         if (mOptions.getEnableBeaconActionsCharacteristic()) {
1926             fastPairServiceConfig.addCharacteristic(mBeaconActionsServlet);
1927         }
1928 
1929         BluetoothGattServerConfig config =
1930                 new BluetoothGattServerConfig()
1931                         .addService(
1932                                 TransportDiscoveryService.ID,
1933                                 new ServiceConfig()
1934                                         .addCharacteristic(tdsControlPointServlet)
1935                                         .addCharacteristic(brHandoverDataServlet)
1936                                         .addCharacteristic(bluetoothSigServlet))
1937                         .addService(
1938                                 FastPairService.ID,
1939                                 mOptions.getEnableNameCharacteristic()
1940                                         ? fastPairServiceConfig.addCharacteristic(
1941                                         mDeviceNameServlet)
1942                                         : fastPairServiceConfig);
1943 
1944         mLogger.log(
1945                 "Starting GATT server, support name characteristic %b",
1946                 mOptions.getEnableNameCharacteristic());
1947         try {
1948             helper.open(config);
1949         } catch (BluetoothException e) {
1950             mLogger.log(e, "Error starting GATT server");
1951         }
1952     }
1953 
1954     /** Callback for passkey/pin input. */
1955     public interface KeyInputCallback {
onKeyInput(int key)1956         void onKeyInput(int key);
1957     }
1958 
enterPassKey(int passkey)1959     public void enterPassKey(int passkey) {
1960         mLogger.log("enterPassKey called with passkey %d.", passkey);
1961         mPairingDevice.setPairingConfirmation(true);
1962     }
1963 
checkPasskey()1964     private void checkPasskey() {
1965         // There's a race between the PAIRING_REQUEST broadcast from the OS giving us the local
1966         // passkey, and the remote passkey received over GATT. Skip the check until we have both.
1967         if (mLocalPasskey == 0 || mRemotePasskey == 0) {
1968             mLogger.log(
1969                     "Skipping passkey check, missing local (%s) or remote (%s).",
1970                     mLocalPasskey, mRemotePasskey);
1971             return;
1972         }
1973 
1974         // Regardless of whether it matches, send our (encrypted) passkey to the seeker.
1975         sendPasskeyToRemoteDevice(mLocalPasskey);
1976 
1977         mLogger.log("Checking localPasskey %s == remotePasskey %s", mLocalPasskey, mRemotePasskey);
1978         boolean passkeysMatched = mLocalPasskey == mRemotePasskey;
1979         if (mOptions.getShowsPasskeyConfirmation() && passkeysMatched
1980                 && mPasskeyEventCallback != null) {
1981             mLogger.log("callbacks the UI for passkey confirmation.");
1982             mPasskeyEventCallback.onPasskeyConfirmation(mLocalPasskey,
1983                     this::setPasskeyConfirmation);
1984         } else {
1985             setPasskeyConfirmation(passkeysMatched);
1986         }
1987     }
1988 
sendPasskeyToRemoteDevice(int passkey)1989     private void sendPasskeyToRemoteDevice(int passkey) {
1990         try {
1991             mPasskeyServlet.sendNotification(
1992                     PasskeyCharacteristic.encrypt(
1993                             PasskeyCharacteristic.Type.PROVIDER, mSecret, passkey));
1994         } catch (GeneralSecurityException e) {
1995             mLogger.log(e, "Failed to encrypt passkey response.");
1996         }
1997     }
1998 
setFirmwareVersion(String versionNumber)1999     public void setFirmwareVersion(String versionNumber) {
2000         mDeviceFirmwareVersion = versionNumber;
2001     }
2002 
setDynamicBufferSize(boolean support)2003     public void setDynamicBufferSize(boolean support) {
2004         if (mSupportDynamicBufferSize != support) {
2005             mSupportDynamicBufferSize = support;
2006             sendCapabilitySync();
2007         }
2008     }
2009 
2010     @VisibleForTesting
setPasskeyConfirmationCallback(PasskeyConfirmationCallback callback)2011     void setPasskeyConfirmationCallback(PasskeyConfirmationCallback callback) {
2012         this.mPasskeyConfirmationCallback = callback;
2013     }
2014 
setDeviceNameCallback(DeviceNameCallback callback)2015     public void setDeviceNameCallback(DeviceNameCallback callback) {
2016         this.mDeviceNameCallback = callback;
2017     }
2018 
setPasskeyEventCallback(PasskeyEventCallback passkeyEventCallback)2019     public void setPasskeyEventCallback(PasskeyEventCallback passkeyEventCallback) {
2020         this.mPasskeyEventCallback = passkeyEventCallback;
2021     }
2022 
setPasskeyConfirmation(boolean confirm)2023     private void setPasskeyConfirmation(boolean confirm) {
2024         mPairingDevice.setPairingConfirmation(confirm);
2025         if (mPasskeyConfirmationCallback != null) {
2026             mPasskeyConfirmationCallback.onPasskeyConfirmation(confirm);
2027         }
2028         mLocalPasskey = 0;
2029         mRemotePasskey = 0;
2030     }
2031 
becomeDiscoverable()2032     private void becomeDiscoverable() throws InterruptedException, TimeoutException {
2033         setDiscoverable(true);
2034     }
2035 
cancelDiscovery()2036     public void cancelDiscovery() throws InterruptedException, TimeoutException {
2037         setDiscoverable(false);
2038     }
2039 
setDiscoverable(boolean discoverable)2040     private void setDiscoverable(boolean discoverable)
2041             throws InterruptedException, TimeoutException {
2042         mIsDiscoverableLatch = new CountDownLatch(1);
2043         setScanMode(discoverable ? SCAN_MODE_CONNECTABLE_DISCOVERABLE : SCAN_MODE_CONNECTABLE);
2044         // If we're already discoverable, count down the latch right away. Otherwise,
2045         // we'll get a broadcast when we successfully become discoverable.
2046         if (isDiscoverable()) {
2047             mIsDiscoverableLatch.countDown();
2048         }
2049         if (mIsDiscoverableLatch.await(BECOME_DISCOVERABLE_TIMEOUT_SEC, TimeUnit.SECONDS)) {
2050             mLogger.log("Successfully became switched discoverable mode %s", discoverable);
2051         } else {
2052             throw new TimeoutException();
2053         }
2054     }
2055 
setScanMode(int scanMode)2056     private void setScanMode(int scanMode) {
2057         if (mRevertDiscoverableFuture != null) {
2058             mRevertDiscoverableFuture.cancel(false /* may interrupt if running */);
2059         }
2060 
2061         mLogger.log("Setting scan mode to %s", scanModeToString(scanMode));
2062         try {
2063             Method method = mBluetoothAdapter.getClass().getMethod("setScanMode", Integer.TYPE);
2064             method.invoke(mBluetoothAdapter, scanMode);
2065 
2066             if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
2067                 mRevertDiscoverableFuture =
2068                         mExecutor.schedule(() -> setScanMode(SCAN_MODE_CONNECTABLE),
2069                                 SCAN_MODE_REFRESH_SEC, TimeUnit.SECONDS);
2070             }
2071         } catch (Exception e) {
2072             mLogger.log(e, "Error setting scan mode to %d", scanMode);
2073         }
2074     }
2075 
scanModeToString(int scanMode)2076     public static String scanModeToString(int scanMode) {
2077         switch (scanMode) {
2078             case SCAN_MODE_CONNECTABLE_DISCOVERABLE:
2079                 return "DISCOVERABLE";
2080             case SCAN_MODE_CONNECTABLE:
2081                 return "CONNECTABLE";
2082             case SCAN_MODE_NONE:
2083                 return "NOT CONNECTABLE";
2084             default:
2085                 return "UNKNOWN(" + scanMode + ")";
2086         }
2087     }
2088 
checkTdsControlPointRequest(byte[] request)2089     private ResultCode checkTdsControlPointRequest(byte[] request) {
2090         if (request.length < 2) {
2091             mLogger.log(
2092                     new IllegalArgumentException(), "Expected length >= 2 for %s",
2093                     base16().encode(request));
2094             return ResultCode.INVALID_PARAMETER;
2095         }
2096         byte opCode = getTdsControlPointOpCode(request);
2097         if (opCode != ControlPointCharacteristic.ACTIVATE_TRANSPORT_OP_CODE) {
2098             mLogger.log(
2099                     new IllegalArgumentException(),
2100                     "Expected Activate Transport op code (0x01), got %d",
2101                     opCode);
2102             return ResultCode.OP_CODE_NOT_SUPPORTED;
2103         }
2104         if (request[1] != BLUETOOTH_SIG_ORGANIZATION_ID) {
2105             mLogger.log(
2106                     new IllegalArgumentException(),
2107                     "Expected Bluetooth SIG organization ID (0x01), got %d",
2108                     request[1]);
2109             return ResultCode.UNSUPPORTED_ORGANIZATION_ID;
2110         }
2111         return ResultCode.SUCCESS;
2112     }
2113 
getTdsControlPointOpCode(byte[] request)2114     private static byte getTdsControlPointOpCode(byte[] request) {
2115         return request.length < 1 ? 0x00 : request[0];
2116     }
2117 
isDiscoverable()2118     private boolean isDiscoverable() {
2119         return mBluetoothAdapter.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE;
2120     }
2121 
modelIdServiceData(boolean forAdvertising)2122     private byte[] modelIdServiceData(boolean forAdvertising) {
2123         // Note: This used to be little-endian but is now big-endian. See b/78229467 for details.
2124         byte[] modelIdPacket =
2125                 base16().decode(
2126                         forAdvertising ? mOptions.getAdvertisingModelId() : mOptions.getModelId());
2127         if (!mBatteryValues.isEmpty()) {
2128             // If we are going to advertise battery values with the packet, then switch to the
2129             // non-3-byte model ID format.
2130             modelIdPacket = concat(new byte[]{0b00000110}, modelIdPacket);
2131         }
2132         return modelIdPacket;
2133     }
2134 
accountKeysServiceData()2135     private byte[] accountKeysServiceData() {
2136         try {
2137             return concat(new byte[]{0x00}, generateBloomFilterFields());
2138         } catch (NoSuchAlgorithmException e) {
2139             throw new IllegalStateException("Unable to build bloom filter.", e);
2140         }
2141     }
2142 
transportDiscoveryData()2143     private byte[] transportDiscoveryData() {
2144         byte[] transportData = SUPPORTED_SERVICES_LTV;
2145         return Bytes.concat(
2146                 new byte[]{BLUETOOTH_SIG_ORGANIZATION_ID},
2147                 new byte[]{tdsFlags(isDiscoverable() ? TransportState.ON : TransportState.OFF)},
2148                 new byte[]{(byte) transportData.length},
2149                 transportData);
2150     }
2151 
generateBloomFilterFields()2152     private byte[] generateBloomFilterFields() throws NoSuchAlgorithmException {
2153         Set<ByteString> accountKeys = getAccountKeys();
2154         if (accountKeys.isEmpty()) {
2155             return new byte[0];
2156         }
2157         BloomFilter bloomFilter =
2158                 new BloomFilter(
2159                         new byte[(int) (1.2 * accountKeys.size()) + 3],
2160                         new FastPairBloomFilterHasher());
2161         String address = mBleAddress == null ? SIMULATOR_FAKE_BLE_ADDRESS : mBleAddress;
2162 
2163         // Simulator supports Central Address Resolution characteristic, so when paired, the BLE
2164         // address in Seeker will be resolved to BR/EDR address. This caused Seeker fails on
2165         // checking the bloom filter due to different address is used for salting. In order to
2166         // let battery values notification be shown on paired device, we use random salt to
2167         // workaround it.
2168         boolean advertisingBatteryValues = !mBatteryValues.isEmpty();
2169         byte[] salt;
2170         if (mOptions.getUseRandomSaltForAccountKeyRotation() || advertisingBatteryValues) {
2171             salt = new byte[1];
2172             new SecureRandom().nextBytes(salt);
2173             mLogger.log("Using random salt %s for bloom filter", base16().encode(salt));
2174         } else {
2175             salt = BluetoothAddress.decode(address);
2176             mLogger.log("Using address %s for bloom filter", address);
2177         }
2178 
2179         // To prevent tampering, account filter shall be slightly modified to include battery data
2180         // when the battery values are included in the advertisement. Normally, when building the
2181         // account filter, a value V is produce by combining the account key with a salt. Instead,
2182         // when battery values are also being advertised, it be constructed as follows:
2183         // - the first 16 bytes are account key.
2184         // - the next bytes are the salt.
2185         // - the remaining bytes are the battery data.
2186         byte[] saltAndBatteryData =
2187                 advertisingBatteryValues ? concat(salt, generateBatteryData()) : salt;
2188 
2189         for (ByteString accountKey : accountKeys) {
2190             bloomFilter.add(concat(accountKey.toByteArray(), saltAndBatteryData));
2191         }
2192         byte[] packet = generateAccountKeyData(bloomFilter);
2193         return mOptions.getUseRandomSaltForAccountKeyRotation() || advertisingBatteryValues
2194                 // Create a header with length 1 and type 1 for a random salt.
2195                 ? concat(packet, createField((byte) 0x11, salt))
2196                 // Exclude the salt from the packet, BLE address will be assumed by the client.
2197                 : packet;
2198     }
2199 
2200     /**
2201      * Creates a new field for the packet.
2202      *
2203      * The header is formatted 0xLLLLTTTT where LLLL is the
2204      * length of the field and TTTT is the type (0 for bloom filter, 1 for salt).
2205      */
createField(byte header, byte[] value)2206     private byte[] createField(byte header, byte[] value) {
2207         return concat(new byte[]{header}, value);
2208     }
2209 
getTxPower()2210     public int getTxPower() {
2211         return mOptions.getTxPowerLevel();
2212     }
2213 
2214     @Nullable
getServiceData()2215     byte[] getServiceData() {
2216         byte[] packet =
2217                 isDiscoverable()
2218                         ? modelIdServiceData(/* forAdvertising= */ true)
2219                         : !getAccountKeys().isEmpty() ? accountKeysServiceData() : null;
2220         return addBatteryValues(packet);
2221     }
2222 
2223     @Nullable
addBatteryValues(byte[] packet)2224     private byte[] addBatteryValues(byte[] packet) {
2225         if (mBatteryValues.isEmpty() || packet == null) {
2226             return packet;
2227         }
2228 
2229         return concat(packet, generateBatteryData());
2230     }
2231 
generateBatteryData()2232     private byte[] generateBatteryData() {
2233         // Byte 0: Battery length and type, first 4 bits are the number of battery values, second
2234         // 4 are the type.
2235         // Byte 1 - length: Battery values, the first bit is charging status, the remaining bits are
2236         // the actual value between 0 and 100, or -1 for unknown.
2237         byte[] batteryData = new byte[mBatteryValues.size() + 1];
2238         batteryData[0] = (byte) (mBatteryValues.size() << 4
2239                 | (mSuppressBatteryNotification ? 0b0100 : 0b0011));
2240 
2241         int batteryValueIndex = 1;
2242         for (BatteryValue batteryValue : mBatteryValues) {
2243             batteryData[batteryValueIndex++] =
2244                     (byte)
2245                             ((batteryValue.mCharging ? 0b10000000 : 0b00000000)
2246                                     | (0b01111111 & batteryValue.mLevel));
2247         }
2248 
2249         return batteryData;
2250     }
2251 
generateAccountKeyData(BloomFilter bloomFilter)2252     private byte[] generateAccountKeyData(BloomFilter bloomFilter) {
2253         // Byte 0: length and type, first 4 bits are the length of bloom filter, second 4 are the
2254         // type which indicating the subsequent pairing notification is suppressed or not.
2255         // The following bytes are the data of bloom filter.
2256         byte[] filterBytes = bloomFilter.asBytes();
2257         byte lengthAndType = (byte) (filterBytes.length << 4
2258                 | (mSuppressSubsequentPairingNotification ? 0b0010 : 0b0000));
2259         mLogger.log(
2260                 "Generate bloom filter with suppress subsequent pairing notification:%b",
2261                 mSuppressSubsequentPairingNotification);
2262         return createField(lengthAndType, filterBytes);
2263     }
2264 
2265     /** Disconnects all bonded devices. */
disconnectAllBondedDevices()2266     public void disconnectAllBondedDevices() {
2267         for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
2268             if (device.getBluetoothClass().getMajorDeviceClass() == Major.PHONE) {
2269                 removeBond(device);
2270             }
2271         }
2272     }
2273 
disconnect(BluetoothProfile profile, BluetoothDevice device)2274     public void disconnect(BluetoothProfile profile, BluetoothDevice device) {
2275         device.disconnect();
2276     }
2277 
removeBond(BluetoothDevice device)2278     public void removeBond(BluetoothDevice device) {
2279         device.removeBond();
2280     }
2281 
resetAccountKeys()2282     public void resetAccountKeys() {
2283         mFastPairSimulatorDatabase.setAccountKeys(new HashSet<>());
2284         mFastPairSimulatorDatabase.setFastPairSeekerDevices(new HashSet<>());
2285         mAccountKey = null;
2286         mOwnerAccountKey = null;
2287         mLogger.log("Remove all account keys");
2288     }
2289 
addAccountKey(byte[] key)2290     public void addAccountKey(byte[] key) {
2291         addAccountKey(key, /* device= */ null);
2292     }
2293 
addAccountKey(byte[] key, @Nullable BluetoothDevice device)2294     private void addAccountKey(byte[] key, @Nullable BluetoothDevice device) {
2295         mAccountKey = key;
2296         if (mOwnerAccountKey == null) {
2297             mOwnerAccountKey = key;
2298         }
2299 
2300         mFastPairSimulatorDatabase.addAccountKey(key);
2301         mFastPairSimulatorDatabase.addFastPairSeekerDevice(device, key);
2302         mLogger.log("Add account key: key=%s, device=%s", base16().encode(key), device);
2303     }
2304 
getAccountKeys()2305     private Set<ByteString> getAccountKeys() {
2306         return mFastPairSimulatorDatabase.getAccountKeys();
2307     }
2308 
2309     /** Get the latest account key. */
2310     @Nullable
getAccountKey()2311     public ByteString getAccountKey() {
2312         if (mAccountKey == null) {
2313             return null;
2314         }
2315         return ByteString.copyFrom(mAccountKey);
2316     }
2317 
2318     /** Get the owner account key (the first account key registered). */
2319     @Nullable
getOwnerAccountKey()2320     public ByteString getOwnerAccountKey() {
2321         if (mOwnerAccountKey == null) {
2322             return null;
2323         }
2324         return ByteString.copyFrom(mOwnerAccountKey);
2325     }
2326 
resetDeviceName()2327     public void resetDeviceName() {
2328         mFastPairSimulatorDatabase.setLocalDeviceName(null);
2329         // Trigger simulator to update device name text view.
2330         if (mDeviceNameCallback != null) {
2331             mDeviceNameCallback.onNameChanged(getDeviceName());
2332         }
2333     }
2334 
2335     // This method is used in test case with default name in provider.
setDeviceName(String deviceName)2336     public void setDeviceName(String deviceName) {
2337         setDeviceName(deviceName.getBytes(StandardCharsets.UTF_8));
2338     }
2339 
setDeviceName(@ullable byte[] deviceName)2340     private void setDeviceName(@Nullable byte[] deviceName) {
2341         mFastPairSimulatorDatabase.setLocalDeviceName(deviceName);
2342 
2343         mLogger.log("Save device name : %s", getDeviceName());
2344         // Trigger simulator to update device name text view.
2345         if (mDeviceNameCallback != null) {
2346             mDeviceNameCallback.onNameChanged(getDeviceName());
2347         }
2348     }
2349 
2350     @Nullable
getDeviceNameInBytes()2351     private byte[] getDeviceNameInBytes() {
2352         return mFastPairSimulatorDatabase.getLocalDeviceName();
2353     }
2354 
2355     @Nullable
getDeviceName()2356     public String getDeviceName() {
2357         String providerDeviceName =
2358                 getDeviceNameInBytes() != null
2359                         ? new String(getDeviceNameInBytes(), StandardCharsets.UTF_8)
2360                         : null;
2361         mLogger.log("get device name = %s", providerDeviceName);
2362         return providerDeviceName;
2363     }
2364 
2365     /**
2366      * Bit index: Description - Value
2367      *
2368      * <ul>
2369      *   <li>0-1: Role - 0b10 (Provider only)
2370      *   <li>2: Transport Data Incomplete: 0 (false)
2371      *   <li>3-4: Transport State (0b00: Off, 0b01: On, 0b10: Temporarily Unavailable)
2372      *   <li>5-7: Reserved for future use
2373      * </ul>
2374      */
tdsFlags(TransportState transportState)2375     private static byte tdsFlags(TransportState transportState) {
2376         return (byte) (0b00000010 & (transportState.mByteValue << 3));
2377     }
2378 
2379     /** Detailed information about battery value. */
2380     public static class BatteryValue {
2381         boolean mCharging;
2382 
2383         // The range is 0 ~ 100, and -1 represents the battery level is unknown.
2384         int mLevel;
2385 
BatteryValue(boolean charging, int level)2386         public BatteryValue(boolean charging, int level) {
2387             this.mCharging = charging;
2388             this.mLevel = level;
2389         }
2390     }
2391 }
2392