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 android.bluetooth.BluetoothAdapter; 20 import android.bluetooth.BluetoothClass; 21 import android.bluetooth.BluetoothProfile; 22 import android.bluetooth.BluetoothDevice; 23 import com.android.bluetooth.a2dp.A2dpService; 24 import com.android.bluetooth.hid.HidService; 25 import com.android.bluetooth.hfp.HeadsetService; 26 import com.android.bluetooth.a2dpsink.A2dpSinkService; 27 import com.android.bluetooth.hfpclient.HeadsetClientService; 28 import com.android.bluetooth.pbapclient.PbapClientService; 29 30 import android.bluetooth.OobData; 31 import android.content.Intent; 32 import android.os.Message; 33 import android.os.UserHandle; 34 import android.util.Log; 35 36 import com.android.bluetooth.R; 37 import com.android.bluetooth.Utils; 38 import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties; 39 import com.android.internal.util.State; 40 import com.android.internal.util.StateMachine; 41 42 import java.util.ArrayList; 43 import java.util.HashSet; 44 import java.util.Set; 45 46 /** 47 * This state machine handles Bluetooth Adapter State. 48 * States: 49 * {@link StableState} : No device is in bonding / unbonding state. 50 * {@link PendingCommandState} : Some device is in bonding / unbonding state. 51 * TODO(BT) This class can be removed and this logic moved to the stack. 52 */ 53 54 final class BondStateMachine extends StateMachine { 55 private static final boolean DBG = false; 56 private static final String TAG = "BluetoothBondStateMachine"; 57 58 static final int CREATE_BOND = 1; 59 static final int CANCEL_BOND = 2; 60 static final int REMOVE_BOND = 3; 61 static final int BONDING_STATE_CHANGE = 4; 62 static final int SSP_REQUEST = 5; 63 static final int PIN_REQUEST = 6; 64 static final int UUID_UPDATE = 10; 65 static final int BOND_STATE_NONE = 0; 66 static final int BOND_STATE_BONDING = 1; 67 static final int BOND_STATE_BONDED = 2; 68 69 private AdapterService mAdapterService; 70 private AdapterProperties mAdapterProperties; 71 private RemoteDevices mRemoteDevices; 72 private BluetoothAdapter mAdapter; 73 74 private Set<BluetoothDevice> mPendingBondedDevices = new HashSet<>(); 75 private PendingCommandState mPendingCommandState = new PendingCommandState(); 76 private StableState mStableState = new StableState(); 77 78 public static final String OOBDATA = "oobdata"; 79 BondStateMachine(AdapterService service, AdapterProperties prop, RemoteDevices remoteDevices)80 private BondStateMachine(AdapterService service, 81 AdapterProperties prop, RemoteDevices remoteDevices) { 82 super("BondStateMachine:"); 83 addState(mStableState); 84 addState(mPendingCommandState); 85 mRemoteDevices = remoteDevices; 86 mAdapterService = service; 87 mAdapterProperties = prop; 88 mAdapter = BluetoothAdapter.getDefaultAdapter(); 89 setInitialState(mStableState); 90 } 91 make(AdapterService service, AdapterProperties prop, RemoteDevices remoteDevices)92 public static BondStateMachine make(AdapterService service, 93 AdapterProperties prop, RemoteDevices remoteDevices) { 94 Log.d(TAG, "make"); 95 BondStateMachine bsm = new BondStateMachine(service, prop, remoteDevices); 96 bsm.start(); 97 return bsm; 98 } 99 doQuit()100 public void doQuit() { 101 quitNow(); 102 } 103 cleanup()104 public void cleanup() { 105 mAdapterService = null; 106 mRemoteDevices = null; 107 mAdapterProperties = null; 108 } 109 110 private class StableState extends State { 111 @Override enter()112 public void enter() { 113 infoLog("StableState(): Entering Off State"); 114 } 115 116 @Override processMessage(Message msg)117 public boolean processMessage(Message msg) { 118 119 BluetoothDevice dev = (BluetoothDevice)msg.obj; 120 121 switch(msg.what) { 122 123 case CREATE_BOND: 124 OobData oobData = null; 125 if (msg.getData() != null) 126 oobData = msg.getData().getParcelable(OOBDATA); 127 128 createBond(dev, msg.arg1, oobData, true); 129 break; 130 case REMOVE_BOND: 131 removeBond(dev, true); 132 break; 133 case BONDING_STATE_CHANGE: 134 int newState = msg.arg1; 135 /* if incoming pairing, transition to pending state */ 136 if (newState == BluetoothDevice.BOND_BONDING) 137 { 138 sendIntent(dev, newState, 0); 139 transitionTo(mPendingCommandState); 140 } 141 else if (newState == BluetoothDevice.BOND_NONE) 142 { 143 /* if the link key was deleted by the stack */ 144 sendIntent(dev, newState, 0); 145 } 146 else 147 { 148 Log.e(TAG, "In stable state, received invalid newState: " + newState); 149 } 150 break; 151 case UUID_UPDATE: 152 if (mPendingBondedDevices.contains(dev)) { 153 sendIntent(dev, BluetoothDevice.BOND_BONDED, 0); 154 } 155 break; 156 157 case CANCEL_BOND: 158 default: 159 Log.e(TAG, "Received unhandled state: " + msg.what); 160 return false; 161 } 162 return true; 163 } 164 } 165 166 167 private class PendingCommandState extends State { 168 private final ArrayList<BluetoothDevice> mDevices = 169 new ArrayList<BluetoothDevice>(); 170 171 @Override enter()172 public void enter() { 173 infoLog("Entering PendingCommandState State"); 174 BluetoothDevice dev = (BluetoothDevice)getCurrentMessage().obj; 175 } 176 177 @Override processMessage(Message msg)178 public boolean processMessage(Message msg) { 179 BluetoothDevice dev = (BluetoothDevice)msg.obj; 180 DeviceProperties devProp = mRemoteDevices.getDeviceProperties(dev); 181 boolean result = false; 182 if (mDevices.contains(dev) && msg.what != CANCEL_BOND && 183 msg.what != BONDING_STATE_CHANGE && msg.what != SSP_REQUEST && 184 msg.what != PIN_REQUEST) { 185 deferMessage(msg); 186 return true; 187 } 188 189 switch (msg.what) { 190 case CREATE_BOND: 191 OobData oobData = null; 192 if (msg.getData() != null) 193 oobData = msg.getData().getParcelable(OOBDATA); 194 195 result = createBond(dev, msg.arg1, oobData, false); 196 break; 197 case REMOVE_BOND: 198 result = removeBond(dev, false); 199 break; 200 case CANCEL_BOND: 201 result = cancelBond(dev); 202 break; 203 case BONDING_STATE_CHANGE: 204 int newState = msg.arg1; 205 int reason = getUnbondReasonFromHALCode(msg.arg2); 206 sendIntent(dev, newState, reason); 207 if(newState != BluetoothDevice.BOND_BONDING ) 208 { 209 /* this is either none/bonded, remove and transition */ 210 result = !mDevices.remove(dev); 211 if (mDevices.isEmpty()) { 212 // Whenever mDevices is empty, then we need to 213 // set result=false. Else, we will end up adding 214 // the device to the list again. This prevents us 215 // from pairing with a device that we just unpaired 216 result = false; 217 transitionTo(mStableState); 218 } 219 if (newState == BluetoothDevice.BOND_NONE) 220 { 221 mAdapterService.setPhonebookAccessPermission(dev, 222 BluetoothDevice.ACCESS_UNKNOWN); 223 mAdapterService.setMessageAccessPermission(dev, 224 BluetoothDevice.ACCESS_UNKNOWN); 225 mAdapterService.setSimAccessPermission(dev, 226 BluetoothDevice.ACCESS_UNKNOWN); 227 // Set the profile Priorities to undefined 228 clearProfilePriority(dev); 229 } 230 } 231 else if(!mDevices.contains(dev)) 232 result=true; 233 break; 234 case SSP_REQUEST: 235 int passkey = msg.arg1; 236 int variant = msg.arg2; 237 sendDisplayPinIntent(devProp.getAddress(), passkey, variant); 238 break; 239 case PIN_REQUEST: 240 BluetoothClass btClass = dev.getBluetoothClass(); 241 int btDeviceClass = btClass.getDeviceClass(); 242 if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD || 243 btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) { 244 // Its a keyboard. Follow the HID spec recommendation of creating the 245 // passkey and displaying it to the user. If the keyboard doesn't follow 246 // the spec recommendation, check if the keyboard has a fixed PIN zero 247 // and pair. 248 //TODO: Maintain list of devices that have fixed pin 249 // Generate a variable 6-digit PIN in range of 100000-999999 250 // This is not truly random but good enough. 251 int pin = 100000 + (int)Math.floor((Math.random() * (999999 - 100000))); 252 sendDisplayPinIntent(devProp.getAddress(), pin, 253 BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN); 254 break; 255 } 256 257 if (msg.arg2 == 1) { // Minimum 16 digit pin required here 258 sendDisplayPinIntent(devProp.getAddress(), 0, 259 BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS); 260 } else { 261 // In PIN_REQUEST, there is no passkey to display.So do not send the 262 // EXTRA_PAIRING_KEY type in the intent( 0 in SendDisplayPinIntent() ) 263 sendDisplayPinIntent(devProp.getAddress(), 0, 264 BluetoothDevice.PAIRING_VARIANT_PIN); 265 } 266 267 break; 268 default: 269 Log.e(TAG, "Received unhandled event:" + msg.what); 270 return false; 271 } 272 if (result) mDevices.add(dev); 273 274 return true; 275 } 276 } 277 cancelBond(BluetoothDevice dev)278 private boolean cancelBond(BluetoothDevice dev) { 279 if (dev.getBondState() == BluetoothDevice.BOND_BONDING) { 280 byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); 281 if (!mAdapterService.cancelBondNative(addr)) { 282 Log.e(TAG, "Unexpected error while cancelling bond:"); 283 } else { 284 return true; 285 } 286 } 287 return false; 288 } 289 removeBond(BluetoothDevice dev, boolean transition)290 private boolean removeBond(BluetoothDevice dev, boolean transition) { 291 if (dev.getBondState() == BluetoothDevice.BOND_BONDED) { 292 byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); 293 if (!mAdapterService.removeBondNative(addr)) { 294 Log.e(TAG, "Unexpected error while removing bond:"); 295 } else { 296 if (transition) transitionTo(mPendingCommandState); 297 return true; 298 } 299 300 } 301 return false; 302 } 303 createBond(BluetoothDevice dev, int transport, OobData oobData, boolean transition)304 private boolean createBond(BluetoothDevice dev, int transport, OobData oobData, 305 boolean transition) { 306 if (dev.getBondState() == BluetoothDevice.BOND_NONE) { 307 infoLog("Bond address is:" + dev); 308 byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); 309 boolean result; 310 if (oobData != null) { 311 result = mAdapterService.createBondOutOfBandNative(addr, transport, oobData); 312 } else { 313 result = mAdapterService.createBondNative(addr, transport); 314 } 315 316 if (!result) { 317 sendIntent(dev, BluetoothDevice.BOND_NONE, 318 BluetoothDevice.UNBOND_REASON_REMOVED); 319 return false; 320 } else if (transition) { 321 transitionTo(mPendingCommandState); 322 } 323 return true; 324 } 325 return false; 326 } 327 sendDisplayPinIntent(byte[] address, int pin, int variant)328 private void sendDisplayPinIntent(byte[] address, int pin, int variant) { 329 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST); 330 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevices.getDevice(address)); 331 if (pin != 0) { 332 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin); 333 } 334 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant); 335 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 336 // Workaround for Android Auto until pre-accepting pairing requests is added. 337 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 338 mAdapterService.sendOrderedBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM); 339 } 340 sendIntent(BluetoothDevice device, int newState, int reason)341 private void sendIntent(BluetoothDevice device, int newState, int reason) { 342 DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device); 343 int oldState = BluetoothDevice.BOND_NONE; 344 if (newState != BluetoothDevice.BOND_NONE 345 && newState != BluetoothDevice.BOND_BONDING 346 && newState != BluetoothDevice.BOND_BONDED) { 347 infoLog("Invalid bond state " + newState); 348 return; 349 } 350 if (devProp != null) { 351 oldState = devProp.getBondState(); 352 } 353 if (mPendingBondedDevices.contains(device)) { 354 mPendingBondedDevices.remove(device); 355 if (oldState == BluetoothDevice.BOND_BONDED) { 356 if (newState == BluetoothDevice.BOND_BONDING) { 357 mAdapterProperties.onBondStateChanged(device, newState); 358 } 359 oldState = BluetoothDevice.BOND_BONDING; 360 } else { 361 // Should not enter here. 362 throw new IllegalArgumentException("Invalid old state " + oldState); 363 } 364 } 365 366 mAdapterProperties.onBondStateChanged(device, newState); 367 368 if (devProp != null && ((devProp.getDeviceType() == BluetoothDevice.DEVICE_TYPE_CLASSIC 369 || devProp.getDeviceType() == BluetoothDevice.DEVICE_TYPE_DUAL) 370 && newState == BluetoothDevice.BOND_BONDED && devProp.getUuids() == null)) { 371 infoLog(device + " is bonded, wait for SDP complete to broadcast bonded intent"); 372 if (!mPendingBondedDevices.contains(device)) { 373 mPendingBondedDevices.add(device); 374 } 375 if (oldState == BluetoothDevice.BOND_NONE) { 376 // Broadcast NONE->BONDING for NONE->BONDED case. 377 newState = BluetoothDevice.BOND_BONDING; 378 } else { 379 return; 380 } 381 } 382 383 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 384 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 385 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState); 386 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState); 387 if (newState == BluetoothDevice.BOND_NONE) 388 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason); 389 mAdapterService.sendBroadcastAsUser(intent, UserHandle.ALL, 390 AdapterService.BLUETOOTH_PERM); 391 infoLog("Bond State Change Intent:" + device + " OldState: " + oldState 392 + " NewState: " + newState); 393 } 394 bondStateChangeCallback(int status, byte[] address, int newState)395 void bondStateChangeCallback(int status, byte[] address, int newState) { 396 BluetoothDevice device = mRemoteDevices.getDevice(address); 397 398 if (device == null) { 399 infoLog("No record of the device:" + device); 400 // This device will be added as part of the BONDING_STATE_CHANGE intent processing 401 // in sendIntent above 402 device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 403 } 404 405 infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device 406 + " newState: " + newState); 407 408 Message msg = obtainMessage(BONDING_STATE_CHANGE); 409 msg.obj = device; 410 411 if (newState == BOND_STATE_BONDED) 412 msg.arg1 = BluetoothDevice.BOND_BONDED; 413 else if (newState == BOND_STATE_BONDING) 414 msg.arg1 = BluetoothDevice.BOND_BONDING; 415 else 416 msg.arg1 = BluetoothDevice.BOND_NONE; 417 msg.arg2 = status; 418 419 sendMessage(msg); 420 } 421 sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, int passkey)422 void sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, 423 int passkey) { 424 //TODO(BT): Get wakelock and update name and cod 425 BluetoothDevice bdDevice = mRemoteDevices.getDevice(address); 426 if (bdDevice == null) { 427 mRemoteDevices.addDeviceProperties(address); 428 } 429 infoLog("sspRequestCallback: " + address + " name: " + name + " cod: " + 430 cod + " pairingVariant " + pairingVariant + " passkey: " + passkey); 431 int variant; 432 boolean displayPasskey = false; 433 switch(pairingVariant) { 434 435 case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION : 436 variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION; 437 displayPasskey = true; 438 break; 439 440 case AbstractionLayer.BT_SSP_VARIANT_CONSENT : 441 variant = BluetoothDevice.PAIRING_VARIANT_CONSENT; 442 break; 443 444 case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY : 445 variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY; 446 break; 447 448 case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_NOTIFICATION : 449 variant = BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY; 450 displayPasskey = true; 451 break; 452 453 default: 454 errorLog("SSP Pairing variant not present"); 455 return; 456 } 457 BluetoothDevice device = mRemoteDevices.getDevice(address); 458 if (device == null) { 459 warnLog("Device is not known for:" + Utils.getAddressStringFromByte(address)); 460 mRemoteDevices.addDeviceProperties(address); 461 device = mRemoteDevices.getDevice(address); 462 } 463 464 Message msg = obtainMessage(SSP_REQUEST); 465 msg.obj = device; 466 if(displayPasskey) 467 msg.arg1 = passkey; 468 msg.arg2 = variant; 469 sendMessage(msg); 470 } 471 pinRequestCallback(byte[] address, byte[] name, int cod, boolean min16Digits)472 void pinRequestCallback(byte[] address, byte[] name, int cod, boolean min16Digits) { 473 //TODO(BT): Get wakelock and update name and cod 474 475 BluetoothDevice bdDevice = mRemoteDevices.getDevice(address); 476 if (bdDevice == null) { 477 mRemoteDevices.addDeviceProperties(address); 478 } 479 infoLog("pinRequestCallback: " + address + " name:" + name + " cod:" + 480 cod); 481 482 Message msg = obtainMessage(PIN_REQUEST); 483 msg.obj = bdDevice; 484 msg.arg2 = min16Digits ? 1 : 0; // Use arg2 to pass the min16Digit boolean 485 486 sendMessage(msg); 487 } 488 clearProfilePriority(BluetoothDevice device)489 private void clearProfilePriority(BluetoothDevice device) { 490 HidService hidService = HidService.getHidService(); 491 A2dpService a2dpService = A2dpService.getA2dpService(); 492 HeadsetService headsetService = HeadsetService.getHeadsetService(); 493 HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService(); 494 A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); 495 PbapClientService pbapClientService = PbapClientService.getPbapClientService(); 496 497 if (hidService != null) 498 hidService.setPriority(device, BluetoothProfile.PRIORITY_UNDEFINED); 499 if (a2dpService != null) 500 a2dpService.setPriority(device, BluetoothProfile.PRIORITY_UNDEFINED); 501 if (headsetService != null) 502 headsetService.setPriority(device, BluetoothProfile.PRIORITY_UNDEFINED); 503 if (headsetClientService != null) 504 headsetClientService.setPriority(device, BluetoothProfile.PRIORITY_UNDEFINED); 505 if (a2dpSinkService != null) 506 a2dpSinkService.setPriority(device, BluetoothProfile.PRIORITY_UNDEFINED); 507 if (pbapClientService != null) 508 pbapClientService.setPriority(device, BluetoothProfile.PRIORITY_UNDEFINED); 509 510 // Clear Absolute Volume black list 511 if(a2dpService != null) 512 a2dpService.resetAvrcpBlacklist(device); 513 } 514 infoLog(String msg)515 private void infoLog(String msg) { 516 Log.i(TAG, msg); 517 } 518 errorLog(String msg)519 private void errorLog(String msg) { 520 Log.e(TAG, msg); 521 } 522 warnLog(String msg)523 private void warnLog(String msg) { 524 Log.w(TAG, msg); 525 } 526 getUnbondReasonFromHALCode(int reason)527 private int getUnbondReasonFromHALCode (int reason) { 528 if (reason == AbstractionLayer.BT_STATUS_SUCCESS) 529 return BluetoothDevice.BOND_SUCCESS; 530 else if (reason == AbstractionLayer.BT_STATUS_RMT_DEV_DOWN) 531 return BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN; 532 else if (reason == AbstractionLayer.BT_STATUS_AUTH_FAILURE) 533 return BluetoothDevice.UNBOND_REASON_AUTH_FAILED; 534 else if (reason == AbstractionLayer.BT_STATUS_AUTH_REJECTED) 535 return BluetoothDevice.UNBOND_REASON_AUTH_REJECTED; 536 else if (reason == AbstractionLayer.BT_STATUS_AUTH_TIMEOUT) 537 return BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT; 538 539 /* default */ 540 return BluetoothDevice.UNBOND_REASON_REMOVED; 541 } 542 } 543