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