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