BondStateMachine.java revision 74ae04c73312403e89db0f8e9bd9601d403b4783
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                            transitionTo(mStableState);
158                        }
159                    }
160                    else if(!mDevices.contains(dev))
161                        result=true;
162                    break;
163                default:
164                    Log.e(TAG, "Received unhandled event:" + msg.what);
165                    return false;
166            }
167            if (result) mDevices.add(dev);
168
169            return true;
170        }
171    }
172
173    private boolean cancelBond(BluetoothDevice dev) {
174        if (dev.getBondState() == BluetoothDevice.BOND_BONDING) {
175            byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
176            if (!mAdapterService.cancelBondNative(addr)) {
177               Log.e(TAG, "Unexpected error while cancelling bond:");
178            } else {
179                return true;
180            }
181        }
182        return false;
183    }
184
185    private boolean removeBond(BluetoothDevice dev, boolean transition) {
186        if (dev.getBondState() == BluetoothDevice.BOND_BONDED) {
187            byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
188            if (!mAdapterService.removeBondNative(addr)) {
189               Log.e(TAG, "Unexpected error while removing bond:");
190            } else {
191                if (transition) transitionTo(mPendingCommandState);
192                return true;
193            }
194
195        }
196        return false;
197    }
198
199    private boolean createBond(BluetoothDevice dev, boolean transition) {
200        if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
201            infoLog("Bond address is:" + dev);
202            byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
203            if (!mAdapterService.createBondNative(addr)) {
204                sendIntent(dev, BluetoothDevice.BOND_NONE);
205                return false;
206            } else if (transition) {
207                transitionTo(mPendingCommandState);
208            }
209            return true;
210        }
211        return false;
212    }
213
214    private void sendIntent(BluetoothDevice device, int newState) {
215        DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device);
216        int oldState = devProp.getBondState();
217        if (oldState == newState) return;
218
219        devProp.setBondState(newState);
220
221        Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
222        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
223        intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState);
224        intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
225        mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
226        infoLog("Bond State Change Intent:" + device + " OldState: " + oldState
227                + " NewState: " + newState);
228    }
229
230    void bondStateChangeCallback(int status, byte[] address, int newState) {
231        BluetoothDevice device = mRemoteDevices.getDevice(address);
232
233        if (device == null) {
234            errorLog("No record of the device:" + device);
235            return;
236        }
237
238        infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device
239                + " newState: " + newState);
240
241        Message msg = obtainMessage(BONDING_STATE_CHANGE);
242        msg.obj = device;
243
244        if (newState == BOND_STATE_BONDED)
245            msg.arg1 = BluetoothDevice.BOND_BONDED;
246        else if (newState == BOND_STATE_BONDING)
247            msg.arg1 = BluetoothDevice.BOND_BONDING;
248        else
249            msg.arg1 = BluetoothDevice.BOND_NONE;
250
251        sendMessage(msg);
252    }
253
254    private void infoLog(String msg) {
255        Log.i(TAG, msg);
256    }
257
258    private void errorLog(String msg) {
259        Log.e(TAG, msg);
260    }
261}
262