• 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.Arrays;
51 import java.util.HashSet;
52 import java.util.Objects;
53 import java.util.Optional;
54 import java.util.Set;
55 
56 /**
57  * This state machine handles Bluetooth Adapter State.
58  * States:
59  *      {@link StableState} :  No device is in bonding / unbonding state.
60  *      {@link PendingCommandState} : Some device is in bonding / unbonding state.
61  * TODO(BT) This class can be removed and this logic moved to the stack.
62  */
63 
64 final class BondStateMachine extends StateMachine {
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 
309     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
cancelBond(BluetoothDevice dev)310     private boolean cancelBond(BluetoothDevice dev) {
311         if (dev.getBondState() == BluetoothDevice.BOND_BONDING) {
312             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
313             if (!mAdapterService.cancelBondNative(addr)) {
314                 Log.e(TAG, "Unexpected error while cancelling bond:");
315             } else {
316                 return true;
317             }
318         }
319         return false;
320     }
321 
removeBond(BluetoothDevice dev, boolean transition)322     private boolean removeBond(BluetoothDevice dev, boolean transition) {
323         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(dev);
324         if (devProp != null && devProp.getBondState() == BluetoothDevice.BOND_BONDED) {
325             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
326             if (!mAdapterService.removeBondNative(addr)) {
327                 Log.e(TAG, "Unexpected error while removing bond:");
328             } else {
329                 if (transition) {
330                     transitionTo(mPendingCommandState);
331                 }
332                 return true;
333             }
334         }
335         return false;
336     }
337 
338     @RequiresPermission(allOf = {
339                 android.Manifest.permission.BLUETOOTH_CONNECT,
340                 android.Manifest.permission.INTERACT_ACROSS_USERS,
341     })
createBond(BluetoothDevice dev, int transport, OobData remoteP192Data, OobData remoteP256Data, boolean transition)342     private boolean createBond(BluetoothDevice dev, int transport, OobData remoteP192Data,
343             OobData remoteP256Data, boolean transition) {
344         if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
345             infoLog("Bond address is:" + dev);
346             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
347             int addrType = dev.getAddressType();
348             boolean result;
349             // If we have some data
350             if (remoteP192Data != null || remoteP256Data != null) {
351                 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
352                       mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
353                       BluetoothDevice.BOND_BONDING,
354                       BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_START_PAIRING_OOB,
355                       BluetoothProtoEnums.UNBOND_REASON_UNKNOWN, mAdapterService.getMetricId(dev));
356                 result = mAdapterService.createBondOutOfBandNative(addr, transport,
357                     remoteP192Data, remoteP256Data);
358             } else {
359                 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
360                       mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
361                       BluetoothDevice.BOND_BONDING,
362                       BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_START_PAIRING,
363                       BluetoothProtoEnums.UNBOND_REASON_UNKNOWN, mAdapterService.getMetricId(dev));
364                 result = mAdapterService.createBondNative(addr, addrType, transport);
365             }
366             BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_DEVICE_NAME_REPORTED,
367                     mAdapterService.getMetricId(dev), dev.getName());
368             BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
369                     mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
370                     BluetoothDevice.BOND_BONDING,
371                     remoteP192Data == null && remoteP256Data == null
372                             ? BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN
373                             : BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_OOB_DATA_PROVIDED,
374                     BluetoothProtoEnums.UNBOND_REASON_UNKNOWN);
375 
376             if (!result) {
377                 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
378                         mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
379                         BluetoothDevice.BOND_NONE, BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN,
380                         BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS);
381                 // Using UNBOND_REASON_REMOVED for legacy reason
382                 sendIntent(dev, BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED,
383                         false);
384                 return false;
385             } else if (transition) {
386                 transitionTo(mPendingCommandState);
387             }
388             return true;
389         }
390         return false;
391     }
392 
sendDisplayPinIntent(byte[] address, Optional<Integer> maybePin, int variant)393     private void sendDisplayPinIntent(byte[] address, Optional<Integer> maybePin, int variant) {
394         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
395         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevices.getDevice(address));
396         maybePin.ifPresent(pin -> intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin));
397         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant);
398         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
399         // Workaround for Android Auto until pre-accepting pairing requests is added.
400         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
401         Utils.sendOrderedBroadcast(mAdapterService, intent, BLUETOOTH_CONNECT,
402                 Utils.getTempAllowlistBroadcastOptions(), null/* resultReceiver */,
403                 null/* scheduler */, Activity.RESULT_OK/* initialCode */, null/* initialData */,
404                 null/* initialExtras */);
405     }
406 
407     @VisibleForTesting
408     @RequiresPermission(allOf = {
409                 android.Manifest.permission.BLUETOOTH_CONNECT,
410                 android.Manifest.permission.INTERACT_ACROSS_USERS,
411     })
sendIntent(BluetoothDevice device, int newState, int reason, boolean isTriggerFromDelayMessage)412     void sendIntent(BluetoothDevice device, int newState, int reason,
413             boolean isTriggerFromDelayMessage) {
414         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device);
415         int oldState = BluetoothDevice.BOND_NONE;
416         if (newState != BluetoothDevice.BOND_NONE
417                 && newState != BluetoothDevice.BOND_BONDING
418                 && newState != BluetoothDevice.BOND_BONDED) {
419             infoLog("Invalid bond state " + newState);
420             return;
421         }
422         if (devProp != null) {
423             oldState = devProp.getBondState();
424         }
425         if (isTriggerFromDelayMessage && (oldState != BluetoothDevice.BOND_BONDED
426                 || newState != BluetoothDevice.BOND_BONDED
427                 || !mPendingBondedDevices.contains(device))) {
428             infoLog("Invalid state when doing delay send bonded intent, oldState: " + oldState
429                     + ", newState: " + newState + ", in PendingBondedDevices list? "
430                     + mPendingBondedDevices.contains(device));
431             return;
432         }
433         if (mPendingBondedDevices.contains(device)) {
434             mPendingBondedDevices.remove(device);
435             if (oldState == BluetoothDevice.BOND_BONDED) {
436                 if (newState == BluetoothDevice.BOND_BONDING) {
437                     mAdapterProperties.onBondStateChanged(device, newState);
438                 }
439                 oldState = BluetoothDevice.BOND_BONDING;
440             } else {
441                 // Should not enter here.
442                 throw new IllegalArgumentException("Invalid old state " + oldState);
443             }
444         }
445         if (oldState == newState) {
446             return;
447         }
448         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
449                 mAdapterService.obfuscateAddress(device), 0, device.getType(),
450                 newState, BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_BOND_STATE_INTENT_SENT, reason,
451                 mAdapterService.getMetricId(device));
452         BluetoothClass deviceClass = device.getBluetoothClass();
453         int classOfDevice = deviceClass == null ? 0 : deviceClass.getClassOfDevice();
454         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED,
455                 mAdapterService.obfuscateAddress(device), classOfDevice,
456                 mAdapterService.getMetricId(device));
457         mAdapterProperties.onBondStateChanged(device, newState);
458 
459         if (!isTriggerFromDelayMessage && newState == BluetoothDevice.BOND_BONDED
460                 && devProp != null && devProp.getUuids() == null) {
461             infoLog(device + " is bonded, wait for SDP complete to broadcast bonded intent");
462             if (!mPendingBondedDevices.contains(device)) {
463                 mPendingBondedDevices.add(device);
464                 Message msg = obtainMessage(BONDED_INTENT_DELAY);
465                 msg.obj = device;
466                 sendMessageDelayed(msg, sPendingUuidUpdateTimeoutMillis);
467             }
468             if (oldState == BluetoothDevice.BOND_NONE) {
469                 // Broadcast NONE->BONDING for NONE->BONDED case.
470                 newState = BluetoothDevice.BOND_BONDING;
471             } else {
472                 return;
473             }
474         }
475 
476         Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
477         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
478         intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState);
479         intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
480         if (newState == BluetoothDevice.BOND_NONE) {
481             intent.putExtra(BluetoothDevice.EXTRA_UNBOND_REASON, reason);
482         }
483         mAdapterService.onBondStateChanged(device, newState);
484         mAdapterService.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT,
485                 Utils.getTempAllowlistBroadcastOptions());
486         infoLog("Bond State Change Intent:" + device + " " + state2str(oldState) + " => "
487                 + state2str(newState));
488     }
489 
bondStateChangeCallback(int status, byte[] address, int newState, int hciReason)490     void bondStateChangeCallback(int status, byte[] address, int newState, int hciReason) {
491         BluetoothDevice device = mRemoteDevices.getDevice(address);
492 
493         if (device == null) {
494             infoLog("No record of the device:" + device);
495             // This device will be added as part of the BONDING_STATE_CHANGE intent processing
496             // in sendIntent above
497             device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
498         }
499 
500         infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device + " newState: "
501                 + newState + " hciReason: " + hciReason);
502 
503         Message msg = obtainMessage(BONDING_STATE_CHANGE);
504         msg.obj = device;
505 
506         if (newState == BOND_STATE_BONDED) {
507             msg.arg1 = BluetoothDevice.BOND_BONDED;
508         } else if (newState == BOND_STATE_BONDING) {
509             msg.arg1 = BluetoothDevice.BOND_BONDING;
510         } else {
511             msg.arg1 = BluetoothDevice.BOND_NONE;
512         }
513         msg.arg2 = status;
514 
515         sendMessage(msg);
516     }
517 
518     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, int passkey)519     void sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, int passkey) {
520         //TODO(BT): Get wakelock and update name and cod
521         BluetoothDevice bdDevice = mRemoteDevices.getDevice(address);
522         if (bdDevice == null) {
523             mRemoteDevices.addDeviceProperties(address);
524         }
525         infoLog("sspRequestCallback: " + Arrays.toString(address)
526                 + " name: " + Arrays.toString(name)
527                 + " cod: " + cod
528                 + " pairingVariant " + pairingVariant
529                 + " passkey: " + (Build.isDebuggable() ? passkey : "******"));
530         int variant;
531         boolean displayPasskey = false;
532         switch (pairingVariant) {
533 
534             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION:
535                 variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION;
536                 displayPasskey = true;
537                 break;
538 
539             case AbstractionLayer.BT_SSP_VARIANT_CONSENT:
540                 variant = BluetoothDevice.PAIRING_VARIANT_CONSENT;
541                 break;
542 
543             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY:
544                 variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY;
545                 break;
546 
547             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_NOTIFICATION:
548                 variant = BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY;
549                 displayPasskey = true;
550                 break;
551 
552             default:
553                 errorLog("SSP Pairing variant not present");
554                 return;
555         }
556         BluetoothDevice device = mRemoteDevices.getDevice(address);
557         if (device == null) {
558             warnLog("Device is not known for:" + Utils.getRedactedAddressStringFromByte(address));
559             mRemoteDevices.addDeviceProperties(address);
560             device = Objects.requireNonNull(mRemoteDevices.getDevice(address));
561         }
562 
563         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
564                 mAdapterService.obfuscateAddress(device), 0, device.getType(),
565                 BluetoothDevice.BOND_BONDING,
566                 BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_SSP_REQUESTED, 0);
567 
568         Message msg = obtainMessage(SSP_REQUEST);
569         msg.obj = device;
570         if (displayPasskey) {
571             msg.arg1 = passkey;
572             Bundle bundle = new Bundle();
573             bundle.putByte(BondStateMachine.DISPLAY_PASSKEY, (byte) 1 /* true */);
574             msg.setData(bundle);
575         }
576         msg.arg2 = variant;
577         sendMessage(msg);
578     }
579 
580     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
pinRequestCallback(byte[] address, byte[] name, int cod, boolean min16Digits)581     void pinRequestCallback(byte[] address, byte[] name, int cod, boolean min16Digits) {
582         //TODO(BT): Get wakelock and update name and cod
583 
584         BluetoothDevice bdDevice = mRemoteDevices.getDevice(address);
585         if (bdDevice == null) {
586             mRemoteDevices.addDeviceProperties(address);
587             bdDevice = Objects.requireNonNull(mRemoteDevices.getDevice(address));
588         }
589 
590         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
591                 mAdapterService.obfuscateAddress(bdDevice), 0, bdDevice.getType(),
592                 BluetoothDevice.BOND_BONDING,
593                 BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_PIN_REQUESTED, 0);
594 
595         infoLog("pinRequestCallback: " + bdDevice
596                 + " name:" + Utils.getName(bdDevice) + " cod:" + new BluetoothClass(cod));
597 
598         Message msg = obtainMessage(PIN_REQUEST);
599         msg.obj = bdDevice;
600         msg.arg2 = min16Digits ? 1 : 0; // Use arg2 to pass the min16Digit boolean
601 
602         sendMessage(msg);
603     }
604 
605     /*
606      * Check whether has the specific message in message queue
607      */
608     @VisibleForTesting
hasMessage(int what)609     public boolean hasMessage(int what) {
610         return hasMessages(what);
611     }
612 
613     /*
614      * Remove the specific message from message queue
615      */
616     @VisibleForTesting
removeMessage(int what)617     public void removeMessage(int what) {
618         removeMessages(what);
619     }
620 
621     @RequiresPermission(allOf = {
622             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
623             android.Manifest.permission.MODIFY_PHONE_STATE,
624     })
clearProfilePriority(BluetoothDevice device)625     private void clearProfilePriority(BluetoothDevice device) {
626         HidHostService hidService = HidHostService.getHidHostService();
627         A2dpService a2dpService = A2dpService.getA2dpService();
628         HeadsetService headsetService = HeadsetService.getHeadsetService();
629         HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService();
630         A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
631         PbapClientService pbapClientService = PbapClientService.getPbapClientService();
632 
633         if (hidService != null) {
634             hidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
635         }
636         if (a2dpService != null) {
637             a2dpService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
638         }
639         if (headsetService != null) {
640             headsetService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
641         }
642         if (headsetClientService != null) {
643             headsetClientService.setConnectionPolicy(device,
644                     BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
645         }
646         if (a2dpSinkService != null) {
647             a2dpSinkService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
648         }
649         if (pbapClientService != null) {
650             pbapClientService.setConnectionPolicy(device,
651                     BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
652         }
653     }
654 
state2str(int state)655     private String state2str(int state) {
656         if (state == BluetoothDevice.BOND_NONE) {
657             return "BOND_NONE";
658         } else if (state == BluetoothDevice.BOND_BONDING) {
659             return "BOND_BONDING";
660         } else if (state == BluetoothDevice.BOND_BONDED) {
661             return "BOND_BONDED";
662         } else return "UNKNOWN(" + state + ")";
663     }
664 
infoLog(String msg)665     private void infoLog(String msg) {
666         Log.i(TAG, msg);
667     }
668 
errorLog(String msg)669     private void errorLog(String msg) {
670         Log.e(TAG, msg);
671     }
672 
warnLog(String msg)673     private void warnLog(String msg) {
674         Log.w(TAG, msg);
675     }
676 
getUnbondReasonFromHALCode(int reason)677     private int getUnbondReasonFromHALCode(int reason) {
678         if (reason == AbstractionLayer.BT_STATUS_SUCCESS) {
679             return BluetoothDevice.BOND_SUCCESS;
680         } else if (reason == AbstractionLayer.BT_STATUS_RMT_DEV_DOWN) {
681             return BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN;
682         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_FAILURE) {
683             return BluetoothDevice.UNBOND_REASON_AUTH_FAILED;
684         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_REJECTED) {
685             return BluetoothDevice.UNBOND_REASON_AUTH_REJECTED;
686         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_TIMEOUT) {
687             return BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT;
688         }
689 
690         /* default */
691         return BluetoothDevice.UNBOND_REASON_REMOVED;
692     }
693 }
694