BondStateMachine.java revision ff4f17bf64978d0738c66e1b6dd70be8664efc24
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    private final AdapterService mAdapterService;
39    private final Context mContext;
40    private final AdapterProperties mAdapterProperties;
41    private final RemoteDevices mRemoteDevices;
42
43    private PendingCommandState mPendingCommandState = new PendingCommandState();
44    private StableState mStableState = new StableState();
45
46    public BondStateMachine(AdapterService service, Context context,
47            AdapterProperties prop) {
48        super("BondStateMachine:");
49        addState(mStableState);
50        addState(mPendingCommandState);
51        setInitialState(mStableState);
52        mAdapterService = service;
53        mAdapterProperties = prop;
54        mContext = context;
55        mRemoteDevices = RemoteDevices.getInstance(service, context);
56    }
57
58    private class StableState extends State {
59        @Override
60        public void enter() {
61            infoLog("Entering Off State");
62        }
63
64        @Override
65        public boolean processMessage(Message msg) {
66            BluetoothDevice dev = (BluetoothDevice)msg.obj;
67            switch(msg.what) {
68              case CREATE_BOND:
69                  createBond(dev, true);
70                  break;
71              case REMOVE_BOND:
72                  removeBond(dev, true);
73                  break;
74              case CANCEL_BOND:
75              case BONDING_STATE_CHANGE:
76              default:
77                   Log.e(TAG, "Received unhandled state: " + msg.what);
78                   return false;
79            }
80            return true;
81        }
82    }
83
84
85    private class PendingCommandState extends State {
86        private final ArrayList<BluetoothDevice> mDevices =
87            new ArrayList<BluetoothDevice>();
88
89        @Override
90        public void enter() {
91            infoLog("Entering PendingCommandState State");
92            BluetoothDevice dev = (BluetoothDevice)getCurrentMessage().obj;
93            mDevices.add(dev);
94        }
95
96        @Override
97        public boolean processMessage(Message msg) {
98            BluetoothDevice dev = (BluetoothDevice)msg.obj;
99            boolean result;
100            if (mDevices.contains(dev) &&
101                    msg.what != CANCEL_BOND && msg.what != BONDING_STATE_CHANGE) {
102                deferMessage(msg);
103                return true;
104            }
105
106            switch (msg.what) {
107                case CREATE_BOND:
108                    result = createBond(dev, false);
109                    break;
110                case REMOVE_BOND:
111                    result = removeBond(dev, false);
112                    break;
113                case CANCEL_BOND:
114                    result = cancelBond(dev);
115                    break;
116                case BONDING_STATE_CHANGE:
117                    int newState = msg.arg1;
118                    sendIntent(dev, newState);
119                    result = mDevices.remove(dev);
120                    if (mDevices.isEmpty()) {
121                        transitionTo(mStableState);
122                    }
123                    break;
124                default:
125                    Log.e(TAG, "Received unhandled event:" + msg.what);
126                    return false;
127            }
128            if (result) mDevices.add(dev);
129
130            return true;
131        }
132    }
133
134    private boolean cancelBond(BluetoothDevice dev) {
135        if (dev.getBondState() == BluetoothDevice.BOND_BONDING) {
136            byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
137            if (!mAdapterService.cancelBondNative(addr)) {
138               Log.e(TAG, "Unexpected error while cancelling bond:");
139            } else {
140                return true;
141            }
142        }
143        return false;
144    }
145
146    private boolean removeBond(BluetoothDevice dev, boolean transition) {
147        if (dev.getBondState() == BluetoothDevice.BOND_BONDED) {
148            byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
149            if (!mAdapterService.removeBondNative(addr)) {
150               Log.e(TAG, "Unexpected error while removing bond:");
151            } else {
152                if (transition) transitionTo(mPendingCommandState);
153                return true;
154            }
155
156        }
157        return false;
158    }
159
160    private boolean createBond(BluetoothDevice dev, boolean transition) {
161        if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
162            sendIntent(dev, BluetoothDevice.BOND_BONDING);
163            infoLog("Bond address is:" + dev);
164            byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
165            if (!mAdapterService.createBondNative(addr)) {
166                sendIntent(dev, BluetoothDevice.BOND_NONE);
167            } else if (transition) {
168                transitionTo(mPendingCommandState);
169            }
170            return true;
171        }
172        return false;
173    }
174
175    private void sendIntent(BluetoothDevice device, int newState) {
176        DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device);
177        int oldState = devProp.getBondState();
178        if (oldState == newState) return;
179
180        devProp.setBondState(newState);
181
182        Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
183        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
184        intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState);
185        intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
186        mContext.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
187        infoLog("Bond State Change Intent:" + device + " OldState: " + oldState
188                + " NewState: " + newState);
189    }
190
191    void bondStateChangeCallback(int status, byte[] address, int newState) {
192        BluetoothDevice device = mRemoteDevices.getDevice(address);
193
194        if (device == null) {
195            errorLog("No record of the device:" + device);
196            return;
197        }
198
199        infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device
200                + " newState: " + newState);
201
202        Message msg = obtainMessage(BONDING_STATE_CHANGE);
203        msg.obj = device;
204
205        int state = (newState == 1 ? BluetoothDevice.BOND_BONDED : BluetoothDevice.BOND_NONE);
206        msg.arg1 = state;
207        sendMessage(msg);
208    }
209
210    private void infoLog(String msg) {
211        Log.i(TAG, msg);
212    }
213
214    private void errorLog(String msg) {
215        Log.e(TAG, msg);
216    }
217}
218