BondStateMachine.java revision bd704c741b8c523ad747214f6f0520ac3e2caf8f
1/* 2 * Copyright (C) 2012 Google Inc. 3 */ 4 5package com.android.bluetooth.btservice; 6 7import android.bluetooth.BluetoothAdapter; 8import android.bluetooth.BluetoothProfile; 9import android.bluetooth.BluetoothDevice; 10import com.android.bluetooth.a2dp.A2dpService; 11import com.android.bluetooth.hid.HidService; 12import com.android.bluetooth.hfp.HeadsetService; 13import android.content.Context; 14import android.content.Intent; 15import android.os.Message; 16import android.os.UserHandle; 17import android.util.Log; 18 19import com.android.bluetooth.Utils; 20import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties; 21import com.android.internal.util.State; 22import com.android.internal.util.StateMachine; 23 24import java.util.ArrayList; 25 26/** 27 * This state machine handles Bluetooth Adapter State. 28 * States: 29 * {@link StableState} : No device is in bonding / unbonding state. 30 * {@link PendingCommandState} : Some device is in bonding / unbonding state. 31 * TODO(BT) This class can be removed and this logic moved to the stack. 32 */ 33 34final class BondStateMachine extends StateMachine { 35 private static final boolean DBG = false; 36 private static final String TAG = "BluetoothBondStateMachine"; 37 38 static final int CREATE_BOND = 1; 39 static final int CANCEL_BOND = 2; 40 static final int REMOVE_BOND = 3; 41 static final int BONDING_STATE_CHANGE = 4; 42 43 static final int BOND_STATE_NONE = 0; 44 static final int BOND_STATE_BONDING = 1; 45 static final int BOND_STATE_BONDED = 2; 46 47 private AdapterService mAdapterService; 48 private AdapterProperties mAdapterProperties; 49 private RemoteDevices mRemoteDevices; 50 private BluetoothAdapter mAdapter; 51 52 private PendingCommandState mPendingCommandState = new PendingCommandState(); 53 private StableState mStableState = new StableState(); 54 55 private BondStateMachine(AdapterService service, 56 AdapterProperties prop, RemoteDevices remoteDevices) { 57 super("BondStateMachine:"); 58 addState(mStableState); 59 addState(mPendingCommandState); 60 mRemoteDevices = remoteDevices; 61 mAdapterService = service; 62 mAdapterProperties = prop; 63 mAdapter = BluetoothAdapter.getDefaultAdapter(); 64 setInitialState(mStableState); 65 } 66 67 public static BondStateMachine make(AdapterService service, 68 AdapterProperties prop, RemoteDevices remoteDevices) { 69 Log.d(TAG, "make"); 70 BondStateMachine bsm = new BondStateMachine(service, prop, remoteDevices); 71 bsm.start(); 72 return bsm; 73 } 74 75 public void doQuit() { 76 quitNow(); 77 } 78 79 public void cleanup() { 80 mAdapterService = null; 81 mRemoteDevices = null; 82 mAdapterProperties = null; 83 } 84 85 private class StableState extends State { 86 @Override 87 public void enter() { 88 infoLog("StableState(): Entering Off State"); 89 } 90 91 @Override 92 public boolean processMessage(Message msg) { 93 94 BluetoothDevice dev = (BluetoothDevice)msg.obj; 95 96 switch(msg.what) { 97 98 case CREATE_BOND: 99 createBond(dev, true); 100 break; 101 case REMOVE_BOND: 102 removeBond(dev, true); 103 break; 104 case BONDING_STATE_CHANGE: 105 int newState = msg.arg1; 106 /* if incoming pairing, transition to pending state */ 107 if (newState == BluetoothDevice.BOND_BONDING) 108 { 109 sendIntent(dev, newState, 0); 110 transitionTo(mPendingCommandState); 111 } 112 else 113 { 114 Log.e(TAG, "In stable state, received invalid newState: " + newState); 115 } 116 break; 117 118 case CANCEL_BOND: 119 default: 120 Log.e(TAG, "Received unhandled state: " + msg.what); 121 return false; 122 } 123 return true; 124 } 125 } 126 127 128 private class PendingCommandState extends State { 129 private final ArrayList<BluetoothDevice> mDevices = 130 new ArrayList<BluetoothDevice>(); 131 132 @Override 133 public void enter() { 134 infoLog("Entering PendingCommandState State"); 135 BluetoothDevice dev = (BluetoothDevice)getCurrentMessage().obj; 136 } 137 138 @Override 139 public boolean processMessage(Message msg) { 140 141 BluetoothDevice dev = (BluetoothDevice)msg.obj; 142 boolean result = false; 143 if (mDevices.contains(dev) && 144 msg.what != CANCEL_BOND && msg.what != BONDING_STATE_CHANGE) { 145 deferMessage(msg); 146 return true; 147 } 148 149 switch (msg.what) { 150 case CREATE_BOND: 151 result = createBond(dev, false); 152 break; 153 case REMOVE_BOND: 154 result = removeBond(dev, false); 155 break; 156 case CANCEL_BOND: 157 result = cancelBond(dev); 158 break; 159 case BONDING_STATE_CHANGE: 160 int newState = msg.arg1; 161 int reason = getUnbondReasonFromHALCode(msg.arg2); 162 sendIntent(dev, newState, reason); 163 if(newState != BluetoothDevice.BOND_BONDING ) 164 { 165 /* this is either none/bonded, remove and transition */ 166 result = !mDevices.remove(dev); 167 if (mDevices.isEmpty()) { 168 // Whenever mDevices is empty, then we need to 169 // set result=false. Else, we will end up adding 170 // the device to the list again. This prevents us 171 // from pairing with a device that we just unpaired 172 result = false; 173 transitionTo(mStableState); 174 } 175 if (newState == BluetoothDevice.BOND_NONE) 176 { 177 // Set the profile Priorities to undefined 178 clearProfilePriorty(dev); 179 } 180 else if (newState == BluetoothDevice.BOND_BONDED) 181 { 182 // Restore the profile priorty settings 183 setProfilePriorty(dev); 184 } 185 } 186 else if(!mDevices.contains(dev)) 187 result=true; 188 break; 189 default: 190 Log.e(TAG, "Received unhandled event:" + msg.what); 191 return false; 192 } 193 if (result) mDevices.add(dev); 194 195 return true; 196 } 197 } 198 199 private boolean cancelBond(BluetoothDevice dev) { 200 if (dev.getBondState() == BluetoothDevice.BOND_BONDING) { 201 byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); 202 if (!mAdapterService.cancelBondNative(addr)) { 203 Log.e(TAG, "Unexpected error while cancelling bond:"); 204 } else { 205 return true; 206 } 207 } 208 return false; 209 } 210 211 private boolean removeBond(BluetoothDevice dev, boolean transition) { 212 if (dev.getBondState() == BluetoothDevice.BOND_BONDED) { 213 byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); 214 if (!mAdapterService.removeBondNative(addr)) { 215 Log.e(TAG, "Unexpected error while removing bond:"); 216 } else { 217 if (transition) transitionTo(mPendingCommandState); 218 return true; 219 } 220 221 } 222 return false; 223 } 224 225 private boolean createBond(BluetoothDevice dev, boolean transition) { 226 if (dev.getBondState() == BluetoothDevice.BOND_NONE) { 227 infoLog("Bond address is:" + dev); 228 byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); 229 if (!mAdapterService.createBondNative(addr)) { 230 sendIntent(dev, BluetoothDevice.BOND_NONE, 231 BluetoothDevice.UNBOND_REASON_REMOVED); 232 return false; 233 } else if (transition) { 234 transitionTo(mPendingCommandState); 235 } 236 return true; 237 } 238 return false; 239 } 240 241 private void sendIntent(BluetoothDevice device, int newState, int reason) { 242 DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device); 243 int oldState = BluetoothDevice.BOND_NONE; 244 if (devProp != null) { 245 oldState = devProp.getBondState(); 246 } 247 if (oldState == newState) return; 248 mAdapterProperties.onBondStateChanged(device, newState); 249 250 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 251 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 252 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState); 253 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState); 254 if (newState == BluetoothDevice.BOND_NONE) 255 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason); 256 mAdapterService.sendBroadcastAsUser(intent, UserHandle.ALL, 257 AdapterService.BLUETOOTH_PERM); 258 infoLog("Bond State Change Intent:" + device + " OldState: " + oldState 259 + " NewState: " + newState); 260 } 261 262 void bondStateChangeCallback(int status, byte[] address, int newState) { 263 BluetoothDevice device = mRemoteDevices.getDevice(address); 264 265 if (device == null) { 266 infoLog("No record of the device:" + device); 267 // This device will be added as part of the BONDING_STATE_CHANGE intent processing 268 // in sendIntent above 269 device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 270 } 271 272 infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device 273 + " newState: " + newState); 274 275 Message msg = obtainMessage(BONDING_STATE_CHANGE); 276 msg.obj = device; 277 278 if (newState == BOND_STATE_BONDED) 279 msg.arg1 = BluetoothDevice.BOND_BONDED; 280 else if (newState == BOND_STATE_BONDING) 281 msg.arg1 = BluetoothDevice.BOND_BONDING; 282 else 283 msg.arg1 = BluetoothDevice.BOND_NONE; 284 msg.arg2 = status; 285 286 sendMessage(msg); 287 } 288 289 private void setProfilePriorty (BluetoothDevice device){ 290 HidService hidService = HidService.getHidService(); 291 A2dpService a2dpService = A2dpService.getA2dpService(); 292 HeadsetService headsetService = HeadsetService.getHeadsetService(); 293 294 if ((hidService != null) && 295 (hidService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){ 296 hidService.setPriority(device,BluetoothProfile.PRIORITY_ON); 297 } 298 299 if ((a2dpService != null) && 300 (a2dpService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){ 301 a2dpService.setPriority(device,BluetoothProfile.PRIORITY_ON); 302 } 303 304 if ((headsetService != null) && 305 (headsetService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){ 306 headsetService.setPriority(device,BluetoothProfile.PRIORITY_ON); 307 } 308 } 309 310 private void clearProfilePriorty (BluetoothDevice device){ 311 HidService hidService = HidService.getHidService(); 312 A2dpService a2dpService = A2dpService.getA2dpService(); 313 HeadsetService headsetService = HeadsetService.getHeadsetService(); 314 315 if (hidService != null) 316 hidService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED); 317 if(a2dpService != null) 318 a2dpService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED); 319 if(headsetService != null) 320 headsetService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED); 321 } 322 323 private void infoLog(String msg) { 324 Log.i(TAG, msg); 325 } 326 327 private void errorLog(String msg) { 328 Log.e(TAG, msg); 329 } 330 331 private int getUnbondReasonFromHALCode (int reason) { 332 if (reason == AbstractionLayer.BT_STATUS_SUCCESS) 333 return BluetoothDevice.BOND_SUCCESS; 334 else if (reason == AbstractionLayer.BT_STATUS_RMT_DEV_DOWN) 335 return BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN; 336 else if (reason == AbstractionLayer.BT_STATUS_AUTH_FAILURE) 337 return BluetoothDevice.UNBOND_REASON_AUTH_FAILED; 338 339 /* default */ 340 return BluetoothDevice.UNBOND_REASON_REMOVED; 341 } 342} 343