• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 com.android.bluetooth.btservice;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 
21 import android.annotation.RequiresPermission;
22 import android.app.Activity;
23 import android.bluetooth.BluetoothAdapter;
24 import android.bluetooth.BluetoothClass;
25 import android.bluetooth.BluetoothDevice;
26 import android.bluetooth.BluetoothProfile;
27 import android.bluetooth.BluetoothProtoEnums;
28 import android.bluetooth.OobData;
29 import android.content.Intent;
30 import android.os.Build;
31 import android.os.Bundle;
32 import android.os.Message;
33 import android.os.UserHandle;
34 import android.util.Log;
35 
36 import com.android.bluetooth.BluetoothStatsLog;
37 import com.android.bluetooth.Utils;
38 import com.android.bluetooth.a2dp.A2dpService;
39 import com.android.bluetooth.a2dpsink.A2dpSinkService;
40 import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
41 import com.android.bluetooth.hfp.HeadsetService;
42 import com.android.bluetooth.hfpclient.HeadsetClientService;
43 import com.android.bluetooth.hid.HidHostService;
44 import com.android.bluetooth.pbapclient.PbapClientService;
45 import com.android.internal.annotations.VisibleForTesting;
46 import com.android.internal.util.State;
47 import com.android.internal.util.StateMachine;
48 
49 import java.util.ArrayList;
50 import java.util.HashSet;
51 import java.util.Objects;
52 import java.util.Optional;
53 import java.util.Set;
54 
55 /**
56  * This state machine handles Bluetooth Adapter State.
57  * States:
58  *      {@link StableState} :  No device is in bonding / unbonding state.
59  *      {@link PendingCommandState} : Some device is in bonding / unbonding state.
60  * TODO(BT) This class can be removed and this logic moved to the stack.
61  */
62 
63 final class BondStateMachine extends StateMachine {
64     private static final boolean DBG = false;
65     private static final String TAG = "BluetoothBondStateMachine";
66 
67     static final int CREATE_BOND = 1;
68     static final int CANCEL_BOND = 2;
69     static final int REMOVE_BOND = 3;
70     static final int BONDING_STATE_CHANGE = 4;
71     static final int SSP_REQUEST = 5;
72     static final int PIN_REQUEST = 6;
73     static final int UUID_UPDATE = 10;
74     static final int BONDED_INTENT_DELAY = 11;
75     static final int BOND_STATE_NONE = 0;
76     static final int BOND_STATE_BONDING = 1;
77     static final int BOND_STATE_BONDED = 2;
78 
79     static int sPendingUuidUpdateTimeoutMillis = 3000; // 3s
80 
81     private AdapterService mAdapterService;
82     private AdapterProperties mAdapterProperties;
83     private RemoteDevices mRemoteDevices;
84     private BluetoothAdapter mAdapter;
85 
86     private PendingCommandState mPendingCommandState = new PendingCommandState();
87     private StableState mStableState = new StableState();
88 
89     public static final String OOBDATAP192 = "oobdatap192";
90     public static final String OOBDATAP256 = "oobdatap256";
91     public static final String DISPLAY_PASSKEY = "display_passkey";
92 
93     @VisibleForTesting Set<BluetoothDevice> mPendingBondedDevices = new HashSet<>();
94 
BondStateMachine(AdapterService service, AdapterProperties prop, RemoteDevices remoteDevices)95     private BondStateMachine(AdapterService service, AdapterProperties prop,
96             RemoteDevices remoteDevices) {
97         super("BondStateMachine:");
98         addState(mStableState);
99         addState(mPendingCommandState);
100         mRemoteDevices = remoteDevices;
101         mAdapterService = service;
102         mAdapterProperties = prop;
103         mAdapter = BluetoothAdapter.getDefaultAdapter();
104         setInitialState(mStableState);
105     }
106 
make(AdapterService service, AdapterProperties prop, RemoteDevices remoteDevices)107     public static BondStateMachine make(AdapterService service, AdapterProperties prop,
108             RemoteDevices remoteDevices) {
109         Log.d(TAG, "make");
110         BondStateMachine bsm = new BondStateMachine(service, prop, remoteDevices);
111         bsm.start();
112         return bsm;
113     }
114 
doQuit()115     public synchronized void doQuit() {
116         quitNow();
117     }
118 
cleanup()119     private void cleanup() {
120         mAdapterService = null;
121         mRemoteDevices = null;
122         mAdapterProperties = null;
123     }
124 
125     @Override
onQuitting()126     protected void onQuitting() {
127         cleanup();
128     }
129 
130     private class StableState extends State {
131         @Override
enter()132         public void enter() {
133             infoLog("StableState(): Entering Off State");
134         }
135 
136         @Override
processMessage(Message msg)137         public synchronized boolean processMessage(Message msg) {
138 
139             BluetoothDevice dev = (BluetoothDevice) msg.obj;
140 
141             switch (msg.what) {
142 
143                 case CREATE_BOND:
144                     OobData p192Data = (msg.getData() != null)
145                             ? msg.getData().getParcelable(OOBDATAP192) : null;
146                     OobData p256Data = (msg.getData() != null)
147                             ? msg.getData().getParcelable(OOBDATAP256) : null;
148                     createBond(dev, msg.arg1, p192Data, p256Data, true);
149                     break;
150                 case REMOVE_BOND:
151                     removeBond(dev, true);
152                     break;
153                 case BONDING_STATE_CHANGE:
154                     int newState = msg.arg1;
155                 /* if incoming pairing, transition to pending state */
156                     if (newState == BluetoothDevice.BOND_BONDING) {
157                         deferMessage(msg);
158                         transitionTo(mPendingCommandState);
159                     } else if (newState == BluetoothDevice.BOND_NONE) {
160                     /* if the link key was deleted by the stack */
161                         sendIntent(dev, newState, 0, false);
162                     } else {
163                         Log.e(TAG, "In stable state, received invalid newState: "
164                                 + state2str(newState));
165                     }
166                     break;
167                 case BONDED_INTENT_DELAY:
168                     if (mPendingBondedDevices.contains(dev)) {
169                         sendIntent(dev, BluetoothDevice.BOND_BONDED, 0, true);
170                     }
171                     break;
172                 case UUID_UPDATE:
173                     if (mPendingBondedDevices.contains(dev)) {
174                         sendIntent(dev, BluetoothDevice.BOND_BONDED, 0, false);
175                     }
176                     break;
177                 case CANCEL_BOND:
178                 default:
179                     Log.e(TAG, "Received unhandled state: " + msg.what);
180                     return false;
181             }
182             return true;
183         }
184     }
185 
186     private class PendingCommandState extends State {
187         private final ArrayList<BluetoothDevice> mDevices = new ArrayList<BluetoothDevice>();
188 
189         @Override
enter()190         public void enter() {
191             infoLog("Entering PendingCommandState State");
192         }
193 
194         @Override
processMessage(Message msg)195         public synchronized boolean processMessage(Message msg) {
196             BluetoothDevice dev = (BluetoothDevice) msg.obj;
197 
198             DeviceProperties devProp = mRemoteDevices.getDeviceProperties(dev);
199             boolean result = false;
200             if ((mDevices.contains(dev) || mPendingBondedDevices.contains(dev))
201                     && msg.what != CANCEL_BOND && msg.what != BONDING_STATE_CHANGE
202                     && msg.what != SSP_REQUEST && msg.what != PIN_REQUEST) {
203                 deferMessage(msg);
204                 return true;
205             }
206 
207             switch (msg.what) {
208                 case CREATE_BOND:
209                     OobData p192Data = (msg.getData() != null)
210                             ? msg.getData().getParcelable(OOBDATAP192) : null;
211                     OobData p256Data = (msg.getData() != null)
212                             ? msg.getData().getParcelable(OOBDATAP256) : null;
213                     result = createBond(dev, msg.arg1, p192Data, p256Data, false);
214                     break;
215                 case REMOVE_BOND:
216                     result = removeBond(dev, false);
217                     break;
218                 case CANCEL_BOND:
219                     result = cancelBond(dev);
220                     break;
221                 case BONDING_STATE_CHANGE:
222                     int newState = msg.arg1;
223                     int reason = getUnbondReasonFromHALCode(msg.arg2);
224                     // Bond is explicitly removed if we are in pending command state
225                     if (newState == BluetoothDevice.BOND_NONE
226                             && reason == BluetoothDevice.BOND_SUCCESS) {
227                         reason = BluetoothDevice.UNBOND_REASON_REMOVED;
228                     }
229                     sendIntent(dev, newState, reason, false);
230                     if (newState != BluetoothDevice.BOND_BONDING) {
231                         // This is either none/bonded, remove and transition, and also set
232                         // result=false to avoid adding the device to mDevices.
233                         mDevices.remove(dev);
234                         result = false;
235                         if (mDevices.isEmpty()) {
236                             transitionTo(mStableState);
237                         }
238                         if (newState == BluetoothDevice.BOND_NONE) {
239                             mAdapterService.setPhonebookAccessPermission(dev,
240                                     BluetoothDevice.ACCESS_UNKNOWN);
241                             mAdapterService.setMessageAccessPermission(dev,
242                                     BluetoothDevice.ACCESS_UNKNOWN);
243                             mAdapterService.setSimAccessPermission(dev,
244                                     BluetoothDevice.ACCESS_UNKNOWN);
245                             // Set the profile Priorities to undefined
246                             clearProfilePriority(dev);
247                         }
248                     } else if (!mDevices.contains(dev)) {
249                         result = true;
250                     }
251                     break;
252                 case SSP_REQUEST:
253                     int passkey = msg.arg1;
254                     int variant = msg.arg2;
255                     boolean displayPasskey =
256                             (msg.getData() != null)
257                                     ? msg.getData().getByte(DISPLAY_PASSKEY) == 1 /* 1 == true */
258                                     : false;
259                     sendDisplayPinIntent(
260                             devProp.getAddress(),
261                             displayPasskey ? Optional.of(passkey) : Optional.empty(),
262                             variant);
263                     break;
264                 case PIN_REQUEST:
265                     BluetoothClass btClass = dev.getBluetoothClass();
266                     int btDeviceClass = btClass.getDeviceClass();
267                     if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD || btDeviceClass
268                             == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) {
269                         // Its a keyboard. Follow the HID spec recommendation of creating the
270                         // passkey and displaying it to the user. If the keyboard doesn't follow
271                         // the spec recommendation, check if the keyboard has a fixed PIN zero
272                         // and pair.
273                         //TODO: Maintain list of devices that have fixed pin
274                         // Generate a variable 6-digit PIN in range of 100000-999999
275                         // This is not truly random but good enough.
276                         int pin = 100000 + (int) Math.floor((Math.random() * (999999 - 100000)));
277                         sendDisplayPinIntent(
278                                 devProp.getAddress(),
279                                 Optional.of(pin),
280                                 BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN);
281                         break;
282                     }
283 
284                     if (msg.arg2 == 1) { // Minimum 16 digit pin required here
285                         sendDisplayPinIntent(
286                                 devProp.getAddress(),
287                                 Optional.empty(),
288                                 BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS);
289                     } else {
290                         // In PIN_REQUEST, there is no passkey to display.So do not send the
291                         // EXTRA_PAIRING_KEY type in the intent
292                         sendDisplayPinIntent(
293                                 devProp.getAddress(),
294                                 Optional.empty(),
295                                 BluetoothDevice.PAIRING_VARIANT_PIN);
296                     }
297                     break;
298                 default:
299                     Log.e(TAG, "Received unhandled event:" + msg.what);
300                     return false;
301             }
302             if (result) {
303                 mDevices.add(dev);
304             }
305             return true;
306         }
307     }
308 
cancelBond(BluetoothDevice dev)309     private boolean cancelBond(BluetoothDevice dev) {
310         if (dev.getBondState() == BluetoothDevice.BOND_BONDING) {
311             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
312             if (!mAdapterService.cancelBondNative(addr)) {
313                 Log.e(TAG, "Unexpected error while cancelling bond:");
314             } else {
315                 return true;
316             }
317         }
318         return false;
319     }
320 
removeBond(BluetoothDevice dev, boolean transition)321     private boolean removeBond(BluetoothDevice dev, boolean transition) {
322         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(dev);
323         if (devProp != null && devProp.getBondState() == BluetoothDevice.BOND_BONDED) {
324             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
325             if (!mAdapterService.removeBondNative(addr)) {
326                 Log.e(TAG, "Unexpected error while removing bond:");
327             } else {
328                 if (transition) {
329                     transitionTo(mPendingCommandState);
330                 }
331                 return true;
332             }
333         }
334         return false;
335     }
336 
createBond(BluetoothDevice dev, int transport, OobData remoteP192Data, OobData remoteP256Data, boolean transition)337     private boolean createBond(BluetoothDevice dev, int transport, OobData remoteP192Data,
338             OobData remoteP256Data, boolean transition) {
339         if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
340             infoLog("Bond address is:" + dev);
341             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
342             boolean result;
343             // If we have some data
344             if (remoteP192Data != null || remoteP256Data != null) {
345                 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
346                       mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
347                       BluetoothDevice.BOND_BONDING,
348                       BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_START_PAIRING_OOB,
349                       BluetoothProtoEnums.UNBOND_REASON_UNKNOWN, mAdapterService.getMetricId(dev));
350                 result = mAdapterService.createBondOutOfBandNative(addr, transport,
351                     remoteP192Data, remoteP256Data);
352             } else {
353                 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
354                       mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
355                       BluetoothDevice.BOND_BONDING,
356                       BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_START_PAIRING,
357                       BluetoothProtoEnums.UNBOND_REASON_UNKNOWN, mAdapterService.getMetricId(dev));
358                 result = mAdapterService.createBondNative(addr, transport);
359             }
360             BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_DEVICE_NAME_REPORTED,
361                     mAdapterService.getMetricId(dev), dev.getName());
362             BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
363                     mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
364                     BluetoothDevice.BOND_BONDING,
365                     remoteP192Data == null && remoteP256Data == null
366                             ? BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN
367                             : BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_OOB_DATA_PROVIDED,
368                     BluetoothProtoEnums.UNBOND_REASON_UNKNOWN);
369 
370             if (!result) {
371                 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
372                         mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
373                         BluetoothDevice.BOND_NONE, BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN,
374                         BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS);
375                 // Using UNBOND_REASON_REMOVED for legacy reason
376                 sendIntent(dev, BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED,
377                         false);
378                 return false;
379             } else if (transition) {
380                 transitionTo(mPendingCommandState);
381             }
382             return true;
383         }
384         return false;
385     }
386 
sendDisplayPinIntent(byte[] address, Optional<Integer> maybePin, int variant)387     private void sendDisplayPinIntent(byte[] address, Optional<Integer> maybePin, int variant) {
388         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
389         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevices.getDevice(address));
390         maybePin.ifPresent(pin -> intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin));
391         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant);
392         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
393         // Workaround for Android Auto until pre-accepting pairing requests is added.
394         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
395         mAdapterService.sendOrderedBroadcast(intent, BLUETOOTH_CONNECT,
396                 Utils.getTempAllowlistBroadcastOptions(), null/* resultReceiver */,
397                 null/* scheduler */, Activity.RESULT_OK/* initialCode */, null/* initialData */,
398                 null/* initialExtras */);
399     }
400 
401     @VisibleForTesting
sendIntent(BluetoothDevice device, int newState, int reason, boolean isTriggerFromDelayMessage)402     void sendIntent(BluetoothDevice device, int newState, int reason,
403             boolean isTriggerFromDelayMessage) {
404         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device);
405         int oldState = BluetoothDevice.BOND_NONE;
406         if (newState != BluetoothDevice.BOND_NONE
407                 && newState != BluetoothDevice.BOND_BONDING
408                 && newState != BluetoothDevice.BOND_BONDED) {
409             infoLog("Invalid bond state " + newState);
410             return;
411         }
412         if (devProp != null) {
413             oldState = devProp.getBondState();
414         }
415         if (isTriggerFromDelayMessage && (oldState != BluetoothDevice.BOND_BONDED
416                 || newState != BluetoothDevice.BOND_BONDED
417                 || !mPendingBondedDevices.contains(device))) {
418             infoLog("Invalid state when doing delay send bonded intent, oldState: " + oldState
419                     + ", newState: " + newState + ", in PendingBondedDevices list? "
420                     + mPendingBondedDevices.contains(device));
421             return;
422         }
423         if (mPendingBondedDevices.contains(device)) {
424             mPendingBondedDevices.remove(device);
425             if (oldState == BluetoothDevice.BOND_BONDED) {
426                 if (newState == BluetoothDevice.BOND_BONDING) {
427                     mAdapterProperties.onBondStateChanged(device, newState);
428                 }
429                 oldState = BluetoothDevice.BOND_BONDING;
430             } else {
431                 // Should not enter here.
432                 throw new IllegalArgumentException("Invalid old state " + oldState);
433             }
434         }
435         if (oldState == newState) {
436             return;
437         }
438         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
439                 mAdapterService.obfuscateAddress(device), 0, device.getType(),
440                 newState, BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_BOND_STATE_INTENT_SENT, reason,
441                 mAdapterService.getMetricId(device));
442         BluetoothClass deviceClass = device.getBluetoothClass();
443         int classOfDevice = deviceClass == null ? 0 : deviceClass.getClassOfDevice();
444         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED,
445                 mAdapterService.obfuscateAddress(device), classOfDevice,
446                 mAdapterService.getMetricId(device));
447         mAdapterProperties.onBondStateChanged(device, newState);
448 
449         if (!isTriggerFromDelayMessage && newState == BluetoothDevice.BOND_BONDED
450                 && devProp != null && devProp.getUuids() == null) {
451             infoLog(device + " is bonded, wait for SDP complete to broadcast bonded intent");
452             if (!mPendingBondedDevices.contains(device)) {
453                 mPendingBondedDevices.add(device);
454                 Message msg = obtainMessage(BONDED_INTENT_DELAY);
455                 msg.obj = device;
456                 sendMessageDelayed(msg, sPendingUuidUpdateTimeoutMillis);
457             }
458             if (oldState == BluetoothDevice.BOND_NONE) {
459                 // Broadcast NONE->BONDING for NONE->BONDED case.
460                 newState = BluetoothDevice.BOND_BONDING;
461             } else {
462                 return;
463             }
464         }
465 
466         Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
467         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
468         intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState);
469         intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
470         if (newState == BluetoothDevice.BOND_NONE) {
471             intent.putExtra(BluetoothDevice.EXTRA_UNBOND_REASON, reason);
472         }
473         mAdapterService.onBondStateChanged(device, newState);
474         mAdapterService.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT,
475                 Utils.getTempAllowlistBroadcastOptions());
476         infoLog("Bond State Change Intent:" + device + " " + state2str(oldState) + " => "
477                 + state2str(newState));
478     }
479 
bondStateChangeCallback(int status, byte[] address, int newState, int hciReason)480     void bondStateChangeCallback(int status, byte[] address, int newState, int hciReason) {
481         BluetoothDevice device = mRemoteDevices.getDevice(address);
482 
483         if (device == null) {
484             infoLog("No record of the device:" + device);
485             // This device will be added as part of the BONDING_STATE_CHANGE intent processing
486             // in sendIntent above
487             device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
488         }
489 
490         infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device + " newState: "
491                 + newState + " hciReason: " + hciReason);
492 
493         Message msg = obtainMessage(BONDING_STATE_CHANGE);
494         msg.obj = device;
495 
496         if (newState == BOND_STATE_BONDED) {
497             msg.arg1 = BluetoothDevice.BOND_BONDED;
498         } else if (newState == BOND_STATE_BONDING) {
499             msg.arg1 = BluetoothDevice.BOND_BONDING;
500         } else {
501             msg.arg1 = BluetoothDevice.BOND_NONE;
502         }
503         msg.arg2 = status;
504 
505         sendMessage(msg);
506     }
507 
sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, int passkey)508     void sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, int passkey) {
509         //TODO(BT): Get wakelock and update name and cod
510         BluetoothDevice bdDevice = mRemoteDevices.getDevice(address);
511         if (bdDevice == null) {
512             mRemoteDevices.addDeviceProperties(address);
513         }
514         infoLog("sspRequestCallback: " + address + " name: " + name + " cod: " + cod
515                 + " pairingVariant " + pairingVariant + " passkey: "
516                 + (Build.isDebuggable() ? passkey : "******"));
517         int variant;
518         boolean displayPasskey = false;
519         switch (pairingVariant) {
520 
521             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION:
522                 variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION;
523                 displayPasskey = true;
524                 break;
525 
526             case AbstractionLayer.BT_SSP_VARIANT_CONSENT:
527                 variant = BluetoothDevice.PAIRING_VARIANT_CONSENT;
528                 break;
529 
530             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY:
531                 variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY;
532                 break;
533 
534             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_NOTIFICATION:
535                 variant = BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY;
536                 displayPasskey = true;
537                 break;
538 
539             default:
540                 errorLog("SSP Pairing variant not present");
541                 return;
542         }
543         BluetoothDevice device = mRemoteDevices.getDevice(address);
544         if (device == null) {
545             warnLog("Device is not known for:" + Utils.getAddressStringFromByte(address));
546             mRemoteDevices.addDeviceProperties(address);
547             device = Objects.requireNonNull(mRemoteDevices.getDevice(address));
548         }
549 
550         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
551                 mAdapterService.obfuscateAddress(device), 0, device.getType(),
552                 BluetoothDevice.BOND_BONDING,
553                 BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_SSP_REQUESTED, 0);
554 
555         Message msg = obtainMessage(SSP_REQUEST);
556         msg.obj = device;
557         if (displayPasskey) {
558             msg.arg1 = passkey;
559             Bundle bundle = new Bundle();
560             bundle.putByte(BondStateMachine.DISPLAY_PASSKEY, (byte) 1 /* true */);
561             msg.setData(bundle);
562         }
563         msg.arg2 = variant;
564         sendMessage(msg);
565     }
566 
pinRequestCallback(byte[] address, byte[] name, int cod, boolean min16Digits)567     void pinRequestCallback(byte[] address, byte[] name, int cod, boolean min16Digits) {
568         //TODO(BT): Get wakelock and update name and cod
569 
570         BluetoothDevice bdDevice = mRemoteDevices.getDevice(address);
571         if (bdDevice == null) {
572             mRemoteDevices.addDeviceProperties(address);
573             bdDevice = Objects.requireNonNull(mRemoteDevices.getDevice(address));
574         }
575 
576         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
577                 mAdapterService.obfuscateAddress(bdDevice), 0, bdDevice.getType(),
578                 BluetoothDevice.BOND_BONDING,
579                 BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_PIN_REQUESTED, 0);
580 
581         infoLog("pinRequestCallback: " + bdDevice.getAddress()
582                 + " name:" + Utils.getName(bdDevice) + " cod:" + new BluetoothClass(cod));
583 
584         Message msg = obtainMessage(PIN_REQUEST);
585         msg.obj = bdDevice;
586         msg.arg2 = min16Digits ? 1 : 0; // Use arg2 to pass the min16Digit boolean
587 
588         sendMessage(msg);
589     }
590 
591     /*
592      * Check whether has the specific message in message queue
593      */
594     @VisibleForTesting
hasMessage(int what)595     public boolean hasMessage(int what) {
596         return hasMessages(what);
597     }
598 
599     /*
600      * Remove the specific message from message queue
601      */
602     @VisibleForTesting
removeMessage(int what)603     public void removeMessage(int what) {
604         removeMessages(what);
605     }
606 
607     @RequiresPermission(allOf = {
608             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
609             android.Manifest.permission.MODIFY_PHONE_STATE,
610     })
clearProfilePriority(BluetoothDevice device)611     private void clearProfilePriority(BluetoothDevice device) {
612         HidHostService hidService = HidHostService.getHidHostService();
613         A2dpService a2dpService = A2dpService.getA2dpService();
614         HeadsetService headsetService = HeadsetService.getHeadsetService();
615         HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService();
616         A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
617         PbapClientService pbapClientService = PbapClientService.getPbapClientService();
618 
619         if (hidService != null) {
620             hidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
621         }
622         if (a2dpService != null) {
623             a2dpService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
624         }
625         if (headsetService != null) {
626             headsetService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
627         }
628         if (headsetClientService != null) {
629             headsetClientService.setConnectionPolicy(device,
630                     BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
631         }
632         if (a2dpSinkService != null) {
633             a2dpSinkService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
634         }
635         if (pbapClientService != null) {
636             pbapClientService.setConnectionPolicy(device,
637                     BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
638         }
639     }
640 
state2str(int state)641     private String state2str(int state) {
642         if (state == BluetoothDevice.BOND_NONE) {
643             return "BOND_NONE";
644         } else if (state == BluetoothDevice.BOND_BONDING) {
645             return "BOND_BONDING";
646         } else if (state == BluetoothDevice.BOND_BONDED) {
647             return "BOND_BONDED";
648         } else return "UNKNOWN(" + state + ")";
649     }
650 
infoLog(String msg)651     private void infoLog(String msg) {
652         Log.i(TAG, msg);
653     }
654 
errorLog(String msg)655     private void errorLog(String msg) {
656         Log.e(TAG, msg);
657     }
658 
warnLog(String msg)659     private void warnLog(String msg) {
660         Log.w(TAG, msg);
661     }
662 
getUnbondReasonFromHALCode(int reason)663     private int getUnbondReasonFromHALCode(int reason) {
664         if (reason == AbstractionLayer.BT_STATUS_SUCCESS) {
665             return BluetoothDevice.BOND_SUCCESS;
666         } else if (reason == AbstractionLayer.BT_STATUS_RMT_DEV_DOWN) {
667             return BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN;
668         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_FAILURE) {
669             return BluetoothDevice.UNBOND_REASON_AUTH_FAILED;
670         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_REJECTED) {
671             return BluetoothDevice.UNBOND_REASON_AUTH_REJECTED;
672         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_TIMEOUT) {
673             return BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT;
674         }
675 
676         /* default */
677         return BluetoothDevice.UNBOND_REASON_REMOVED;
678     }
679 }
680