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