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