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