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