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