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