BondStateMachine.java revision 73d192095093759688cccc896714f71fcee218d8
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 = true; 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 public 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 void doQuit() { 67 quitNow(); 68 } 69 70 public void cleanup() { 71 mAdapterService = null; 72 mRemoteDevices = null; 73 mAdapterProperties = null; 74 } 75 76 private class StableState extends State { 77 @Override 78 public void enter() { 79 infoLog("StableState(): Entering Off State"); 80 } 81 82 @Override 83 public boolean processMessage(Message msg) { 84 85 BluetoothDevice dev = (BluetoothDevice)msg.obj; 86 87 switch(msg.what) { 88 89 case CREATE_BOND: 90 createBond(dev, true); 91 break; 92 case REMOVE_BOND: 93 removeBond(dev, true); 94 break; 95 case BONDING_STATE_CHANGE: 96 int newState = msg.arg1; 97 /* if incoming pairing, transition to pending state */ 98 if (newState == BluetoothDevice.BOND_BONDING) 99 { 100 sendIntent(dev, newState); 101 transitionTo(mPendingCommandState); 102 } 103 else 104 { 105 Log.e(TAG, "In stable state, received invalid newState: " + newState); 106 } 107 break; 108 109 case CANCEL_BOND: 110 default: 111 Log.e(TAG, "Received unhandled state: " + msg.what); 112 return false; 113 } 114 return true; 115 } 116 } 117 118 119 private class PendingCommandState extends State { 120 private final ArrayList<BluetoothDevice> mDevices = 121 new ArrayList<BluetoothDevice>(); 122 123 @Override 124 public void enter() { 125 infoLog("Entering PendingCommandState State"); 126 BluetoothDevice dev = (BluetoothDevice)getCurrentMessage().obj; 127 } 128 129 @Override 130 public boolean processMessage(Message msg) { 131 132 BluetoothDevice dev = (BluetoothDevice)msg.obj; 133 boolean result = false; 134 if (mDevices.contains(dev) && 135 msg.what != CANCEL_BOND && msg.what != BONDING_STATE_CHANGE) { 136 deferMessage(msg); 137 return true; 138 } 139 140 switch (msg.what) { 141 case CREATE_BOND: 142 result = createBond(dev, false); 143 break; 144 case REMOVE_BOND: 145 result = removeBond(dev, false); 146 break; 147 case CANCEL_BOND: 148 result = cancelBond(dev); 149 break; 150 case BONDING_STATE_CHANGE: 151 int newState = msg.arg1; 152 sendIntent(dev, newState); 153 if(newState != BluetoothDevice.BOND_BONDING ) 154 { 155 /* this is either none/bonded, remove and transition */ 156 result = !mDevices.remove(dev); 157 if (mDevices.isEmpty()) { 158 // Whenever mDevices is empty, then we need to 159 // set result=false. Else, we will end up adding 160 // the device to the list again. This prevents us 161 // from pairing with a device that we just unpaired 162 result = false; 163 transitionTo(mStableState); 164 } 165 if (newState == BluetoothDevice.BOND_NONE) 166 { 167 // Set the profile Priorities to undefined 168 clearProfilePriorty(dev); 169 } 170 else if (newState == BluetoothDevice.BOND_BONDED) 171 { 172 // Restore the profile priorty settings 173 setProfilePriorty(dev); 174 } 175 } 176 else if(!mDevices.contains(dev)) 177 result=true; 178 break; 179 default: 180 Log.e(TAG, "Received unhandled event:" + msg.what); 181 return false; 182 } 183 if (result) mDevices.add(dev); 184 185 return true; 186 } 187 } 188 189 private boolean cancelBond(BluetoothDevice dev) { 190 if (dev.getBondState() == BluetoothDevice.BOND_BONDING) { 191 byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); 192 if (!mAdapterService.cancelBondNative(addr)) { 193 Log.e(TAG, "Unexpected error while cancelling bond:"); 194 } else { 195 return true; 196 } 197 } 198 return false; 199 } 200 201 private boolean removeBond(BluetoothDevice dev, boolean transition) { 202 if (dev.getBondState() == BluetoothDevice.BOND_BONDED) { 203 byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); 204 if (!mAdapterService.removeBondNative(addr)) { 205 Log.e(TAG, "Unexpected error while removing bond:"); 206 } else { 207 if (transition) transitionTo(mPendingCommandState); 208 return true; 209 } 210 211 } 212 return false; 213 } 214 215 private boolean createBond(BluetoothDevice dev, boolean transition) { 216 if (dev.getBondState() == BluetoothDevice.BOND_NONE) { 217 infoLog("Bond address is:" + dev); 218 byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); 219 if (!mAdapterService.createBondNative(addr)) { 220 sendIntent(dev, BluetoothDevice.BOND_NONE); 221 return false; 222 } else if (transition) { 223 transitionTo(mPendingCommandState); 224 } 225 return true; 226 } 227 return false; 228 } 229 230 private void sendIntent(BluetoothDevice device, int newState) { 231 DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device); 232 int oldState = BluetoothDevice.BOND_NONE; 233 if (devProp != null) { 234 oldState = devProp.getBondState(); 235 } 236 if (oldState == newState) return; 237 mAdapterProperties.onBondStateChanged(device, newState); 238 239 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 240 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 241 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState); 242 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState); 243 mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM); 244 infoLog("Bond State Change Intent:" + device + " OldState: " + oldState 245 + " NewState: " + newState); 246 } 247 248 void bondStateChangeCallback(int status, byte[] address, int newState) { 249 BluetoothDevice device = mRemoteDevices.getDevice(address); 250 251 if (device == null) { 252 infoLog("No record of the device:" + device); 253 // This device will be added as part of the BONDING_STATE_CHANGE intent processing 254 // in sendIntent above 255 device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 256 } 257 258 infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device 259 + " newState: " + newState); 260 261 Message msg = obtainMessage(BONDING_STATE_CHANGE); 262 msg.obj = device; 263 264 if (newState == BOND_STATE_BONDED) 265 msg.arg1 = BluetoothDevice.BOND_BONDED; 266 else if (newState == BOND_STATE_BONDING) 267 msg.arg1 = BluetoothDevice.BOND_BONDING; 268 else 269 msg.arg1 = BluetoothDevice.BOND_NONE; 270 271 sendMessage(msg); 272 } 273 274 private void setProfilePriorty (BluetoothDevice device){ 275 HidService hidService = HidService.getHidService(); 276 A2dpService a2dpService = A2dpService.getA2dpService(); 277 HeadsetService headsetService = HeadsetService.getHeadsetService(); 278 279 if ((hidService != null) && 280 (hidService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){ 281 hidService.setPriority(device,BluetoothProfile.PRIORITY_ON); 282 } 283 284 if ((a2dpService != null) && 285 (a2dpService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){ 286 a2dpService.setPriority(device,BluetoothProfile.PRIORITY_ON); 287 } 288 289 if ((headsetService != null) && 290 (headsetService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){ 291 headsetService.setPriority(device,BluetoothProfile.PRIORITY_ON); 292 } 293 } 294 295 private void clearProfilePriorty (BluetoothDevice device){ 296 HidService hidService = HidService.getHidService(); 297 A2dpService a2dpService = A2dpService.getA2dpService(); 298 HeadsetService headsetService = HeadsetService.getHeadsetService(); 299 300 if (hidService != null) 301 hidService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED); 302 if(a2dpService != null) 303 a2dpService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED); 304 if(headsetService != null) 305 headsetService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED); 306 } 307 308 private void infoLog(String msg) { 309 Log.i(TAG, msg); 310 } 311 312 private void errorLog(String msg) { 313 Log.e(TAG, msg); 314 } 315 316} 317