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