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