BondStateMachine.java revision 73d192095093759688cccc896714f71fcee218d8
1/*
2 * Copyright (C) 2012 Google Inc.
3 */
4
5package com.android.bluetooth.btservice;
6
7import android.bluetooth.BluetoothAdapter;
8import android.bluetooth.BluetoothProfile;
9import android.bluetooth.BluetoothDevice;
10import com.android.bluetooth.a2dp.A2dpService;
11import com.android.bluetooth.hid.HidService;
12import com.android.bluetooth.hfp.HeadsetService;
13import android.content.Context;
14import android.content.Intent;
15import android.os.Message;
16import android.util.Log;
17
18import com.android.bluetooth.Utils;
19import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
20import com.android.internal.util.State;
21import com.android.internal.util.StateMachine;
22
23import java.util.ArrayList;
24
25/**
26 * This state machine handles Bluetooth Adapter State.
27 * States:
28 *      {@link StableState} :  No device is in bonding / unbonding state.
29 *      {@link PendingCommandState} : Some device is in bonding / unbonding state.
30 * TODO(BT) This class can be removed and this logic moved to the stack.
31 */
32
33final class BondStateMachine extends StateMachine {
34    private static final boolean DBG = true;
35    private static final String TAG = "BluetoothBondStateMachine";
36
37    static final int CREATE_BOND = 1;
38    static final int CANCEL_BOND = 2;
39    static final int REMOVE_BOND = 3;
40    static final int BONDING_STATE_CHANGE = 4;
41
42    static final int BOND_STATE_NONE = 0;
43    static final int BOND_STATE_BONDING = 1;
44    static final int BOND_STATE_BONDED = 2;
45
46    private AdapterService mAdapterService;
47    private AdapterProperties mAdapterProperties;
48    private RemoteDevices mRemoteDevices;
49    private BluetoothAdapter mAdapter;
50
51    private PendingCommandState mPendingCommandState = new PendingCommandState();
52    private StableState mStableState = new StableState();
53
54    public BondStateMachine(AdapterService service,
55            AdapterProperties prop, RemoteDevices remoteDevices) {
56        super("BondStateMachine:");
57        addState(mStableState);
58        addState(mPendingCommandState);
59        mRemoteDevices = remoteDevices;
60        mAdapterService = service;
61        mAdapterProperties = prop;
62        mAdapter = BluetoothAdapter.getDefaultAdapter();
63        setInitialState(mStableState);
64    }
65
66    public void doQuit() {
67        quitNow();
68    }
69
70    public void cleanup() {
71        mAdapterService = null;
72        mRemoteDevices = null;
73        mAdapterProperties = null;
74    }
75
76    private class StableState extends State {
77        @Override
78        public void enter() {
79            infoLog("StableState(): Entering Off State");
80        }
81
82        @Override
83        public boolean processMessage(Message msg) {
84
85            BluetoothDevice dev = (BluetoothDevice)msg.obj;
86
87            switch(msg.what) {
88
89              case CREATE_BOND:
90                  createBond(dev, true);
91                  break;
92              case REMOVE_BOND:
93                  removeBond(dev, true);
94                  break;
95              case BONDING_STATE_CHANGE:
96                int newState = msg.arg1;
97                /* if incoming pairing, transition to pending state */
98                if (newState == BluetoothDevice.BOND_BONDING)
99                {
100                    sendIntent(dev, newState);
101                    transitionTo(mPendingCommandState);
102                }
103                else
104                {
105                    Log.e(TAG, "In stable state, received invalid newState: " + newState);
106                }
107                break;
108
109              case CANCEL_BOND:
110              default:
111                   Log.e(TAG, "Received unhandled state: " + msg.what);
112                   return false;
113            }
114            return true;
115        }
116    }
117
118
119    private class PendingCommandState extends State {
120        private final ArrayList<BluetoothDevice> mDevices =
121            new ArrayList<BluetoothDevice>();
122
123        @Override
124        public void enter() {
125            infoLog("Entering PendingCommandState State");
126            BluetoothDevice dev = (BluetoothDevice)getCurrentMessage().obj;
127        }
128
129        @Override
130        public boolean processMessage(Message msg) {
131
132            BluetoothDevice dev = (BluetoothDevice)msg.obj;
133            boolean result = false;
134            if (mDevices.contains(dev) &&
135                    msg.what != CANCEL_BOND && msg.what != BONDING_STATE_CHANGE) {
136                deferMessage(msg);
137                return true;
138            }
139
140            switch (msg.what) {
141                case CREATE_BOND:
142                    result = createBond(dev, false);
143                    break;
144                case REMOVE_BOND:
145                    result = removeBond(dev, false);
146                    break;
147                case CANCEL_BOND:
148                    result = cancelBond(dev);
149                    break;
150                case BONDING_STATE_CHANGE:
151                    int newState = msg.arg1;
152                    sendIntent(dev, newState);
153                    if(newState != BluetoothDevice.BOND_BONDING )
154                    {
155                        /* this is either none/bonded, remove and transition */
156                        result = !mDevices.remove(dev);
157                        if (mDevices.isEmpty()) {
158                            // Whenever mDevices is empty, then we need to
159                            // set result=false. Else, we will end up adding
160                            // the device to the list again. This prevents us
161                            // from pairing with a device that we just unpaired
162                            result = false;
163                            transitionTo(mStableState);
164                        }
165                        if (newState == BluetoothDevice.BOND_NONE)
166                        {
167                            // Set the profile Priorities to undefined
168                            clearProfilePriorty(dev);
169                        }
170                        else if (newState == BluetoothDevice.BOND_BONDED)
171                        {
172                           // Restore the profile priorty settings
173                           setProfilePriorty(dev);
174                        }
175                    }
176                    else if(!mDevices.contains(dev))
177                        result=true;
178                    break;
179                default:
180                    Log.e(TAG, "Received unhandled event:" + msg.what);
181                    return false;
182            }
183            if (result) mDevices.add(dev);
184
185            return true;
186        }
187    }
188
189    private boolean cancelBond(BluetoothDevice dev) {
190        if (dev.getBondState() == BluetoothDevice.BOND_BONDING) {
191            byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
192            if (!mAdapterService.cancelBondNative(addr)) {
193               Log.e(TAG, "Unexpected error while cancelling bond:");
194            } else {
195                return true;
196            }
197        }
198        return false;
199    }
200
201    private boolean removeBond(BluetoothDevice dev, boolean transition) {
202        if (dev.getBondState() == BluetoothDevice.BOND_BONDED) {
203            byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
204            if (!mAdapterService.removeBondNative(addr)) {
205               Log.e(TAG, "Unexpected error while removing bond:");
206            } else {
207                if (transition) transitionTo(mPendingCommandState);
208                return true;
209            }
210
211        }
212        return false;
213    }
214
215    private boolean createBond(BluetoothDevice dev, boolean transition) {
216        if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
217            infoLog("Bond address is:" + dev);
218            byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
219            if (!mAdapterService.createBondNative(addr)) {
220                sendIntent(dev, BluetoothDevice.BOND_NONE);
221                return false;
222            } else if (transition) {
223                transitionTo(mPendingCommandState);
224            }
225            return true;
226        }
227        return false;
228    }
229
230    private void sendIntent(BluetoothDevice device, int newState) {
231        DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device);
232        int oldState = BluetoothDevice.BOND_NONE;
233        if (devProp != null) {
234            oldState = devProp.getBondState();
235        }
236        if (oldState == newState) return;
237        mAdapterProperties.onBondStateChanged(device, newState);
238
239        Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
240        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
241        intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState);
242        intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
243        mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
244        infoLog("Bond State Change Intent:" + device + " OldState: " + oldState
245                + " NewState: " + newState);
246    }
247
248    void bondStateChangeCallback(int status, byte[] address, int newState) {
249        BluetoothDevice device = mRemoteDevices.getDevice(address);
250
251        if (device == null) {
252            infoLog("No record of the device:" + device);
253            // This device will be added as part of the BONDING_STATE_CHANGE intent processing
254            // in sendIntent above
255            device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
256        }
257
258        infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device
259                + " newState: " + newState);
260
261        Message msg = obtainMessage(BONDING_STATE_CHANGE);
262        msg.obj = device;
263
264        if (newState == BOND_STATE_BONDED)
265            msg.arg1 = BluetoothDevice.BOND_BONDED;
266        else if (newState == BOND_STATE_BONDING)
267            msg.arg1 = BluetoothDevice.BOND_BONDING;
268        else
269            msg.arg1 = BluetoothDevice.BOND_NONE;
270
271        sendMessage(msg);
272    }
273
274    private void setProfilePriorty (BluetoothDevice device){
275        HidService hidService = HidService.getHidService();
276        A2dpService a2dpService = A2dpService.getA2dpService();
277        HeadsetService headsetService = HeadsetService.getHeadsetService();
278
279        if ((hidService != null) &&
280            (hidService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){
281            hidService.setPriority(device,BluetoothProfile.PRIORITY_ON);
282        }
283
284        if ((a2dpService != null) &&
285            (a2dpService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){
286            a2dpService.setPriority(device,BluetoothProfile.PRIORITY_ON);
287        }
288
289        if ((headsetService != null) &&
290            (headsetService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){
291            headsetService.setPriority(device,BluetoothProfile.PRIORITY_ON);
292        }
293    }
294
295    private void clearProfilePriorty (BluetoothDevice device){
296        HidService hidService = HidService.getHidService();
297        A2dpService a2dpService = A2dpService.getA2dpService();
298        HeadsetService headsetService = HeadsetService.getHeadsetService();
299
300        if (hidService != null)
301            hidService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED);
302        if(a2dpService != null)
303            a2dpService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED);
304        if(headsetService != null)
305            headsetService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED);
306    }
307
308    private void infoLog(String msg) {
309        Log.i(TAG, msg);
310    }
311
312    private void errorLog(String msg) {
313        Log.e(TAG, msg);
314    }
315
316}
317