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