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