AdapterState.java revision 6654f5c903de510a70f9e72cd5ad7837b615d93f
1/*
2 * Copyright (C) 2012 Google Inc.
3 */
4
5package com.android.bluetooth.btservice;
6
7import android.bluetooth.BluetoothAdapter;
8import android.content.Context;
9import android.content.Intent;
10import android.os.Message;
11import android.util.Log;
12
13import com.android.internal.util.State;
14import com.android.internal.util.StateMachine;
15
16/**
17 * This state machine handles Bluetooth Adapter State.
18 * States:
19 *      {@link OnState} : Bluetooth is on at this state
20 *      {@link OffState}: Bluetooth is off at this state. This is the initial
21 *      state.
22 *      {@link PendingCommandState} : An enable / disable operation is pending.
23 * TODO(BT): Add per process on state.
24 */
25
26final class AdapterState extends StateMachine {
27    private static final boolean DBG = true;
28    private static final String TAG = "BluetoothAdapterState";
29
30    static final int USER_TURN_ON = 1;
31    static final int USER_TURN_OFF = 2;
32    static final int AIRPLANE_MODE_ON = 3;
33    static final int AIRPLANE_MODE_OFF = 4;
34    static final int ENABLED_READY = 5;
35    static final int DISABLED = 6;
36    static final int ALL_DEVICES_DISCONNECTED = 7;
37    static final int ENABLE_TIMEOUT = 8;
38
39    private static final int DISCONNECT_TIMEOUT = 3000;
40    private static final int ENABLE_TIMEOUT_DELAY = 6000; // 6 secs
41
42    private AdapterService mAdapterService;
43    private Context mContext;
44    private AdapterProperties mAdapterProperties;
45    private boolean mPendingPersistEnable;
46    private PendingCommandState mPendingCommandState = new PendingCommandState();
47    private OnState mOnState = new OnState();
48    private OffState mOffState = new OffState();
49
50    public AdapterState(AdapterService service, Context context,
51            AdapterProperties adapterProperties) {
52        super("BluetoothAdapterState:");
53        addState(mOnState);
54        addState(mOffState);
55        addState(mPendingCommandState);
56        mAdapterService = service;
57        mContext = context;
58        mAdapterProperties = adapterProperties;
59        setInitialState(mOffState);
60    }
61
62    public void cleanup() {
63    }
64
65    private class OffState extends State {
66        @Override
67        public void enter() {
68            infoLog("Entering Off State");
69        }
70
71        @Override
72        public boolean processMessage(Message msg) {
73            switch(msg.what) {
74               case USER_TURN_ON:
75                   int persist = msg.arg1;
76                   //if (persist == 1) mAdapterService.persistBluetoothSetting(true);
77                   //Persist enable state only once enable completes
78                   mPendingPersistEnable = (persist ==1);
79                   sendIntent(BluetoothAdapter.STATE_TURNING_ON);
80                   boolean ret = mAdapterService.enableNative();
81                   if (!ret) {
82                       Log.e(TAG, "Error while turning Bluetooth On");
83                       sendIntent(BluetoothAdapter.STATE_OFF);
84                   } else {
85                       sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
86                       transitionTo(mPendingCommandState);
87                   }
88                   break;
89               case AIRPLANE_MODE_OFF:
90               {
91                   if(mAdapterService.getBluetoothPersistedSetting()) {
92                        Log.i(TAG, "OffState : Turning BT on after Airplane"+
93                            " Mode OFF state");
94                        sendIntent(BluetoothAdapter.STATE_TURNING_ON);
95                        ret = mAdapterService.enableNative();
96                        if (!ret) {
97                            Log.e(TAG, "Error while turning Bluetooth On");
98                            sendIntent(BluetoothAdapter.STATE_OFF);
99                        } else {
100                            sendMessageDelayed(ENABLE_TIMEOUT,
101                                ENABLE_TIMEOUT_DELAY);
102                            transitionTo(mPendingCommandState);
103                        }
104                   }
105                   break;
106               }
107               case USER_TURN_OFF:
108               case AIRPLANE_MODE_ON:
109                   //ignore
110                   break;
111               default:
112                   Log.e(TAG, "Received unhandled state: " + msg.what);
113                   return false;
114            }
115            return true;
116        }
117    }
118
119    private class OnState extends State {
120        @Override
121        public void enter() {
122            infoLog("Entering On State");
123        }
124
125        @Override
126        public boolean processMessage(Message msg) {
127            switch(msg.what) {
128               case USER_TURN_OFF:
129                   int persist = msg.arg1;
130                   if (persist == 1) {
131                          //Persist disable immediately even before disable completes
132                       mAdapterService.persistBluetoothSetting(false);
133                   }
134                   //Fall Through
135               case AIRPLANE_MODE_ON:
136                   sendIntent(BluetoothAdapter.STATE_TURNING_OFF);
137                   // Invoke onBluetoothDisable which shall trigger a
138                   // setScanMode to SCAN_MODE_NONE
139                   mAdapterProperties.onBluetoothDisable();
140                   if (mAdapterProperties.getConnectionState() !=
141                           BluetoothAdapter.STATE_DISCONNECTED) {
142                       sendMessageDelayed(ALL_DEVICES_DISCONNECTED,
143                                   DISCONNECT_TIMEOUT);
144                       break;
145                   }
146                   //Fall Through
147               case ALL_DEVICES_DISCONNECTED:
148                   boolean ret = mAdapterService.disableNative();
149                   if (!ret) {
150                       Log.e(TAG, "Error while turning Bluetooth Off");
151                       sendIntent(BluetoothAdapter.STATE_ON);
152                   } else {
153                       transitionTo(mPendingCommandState);
154                   }
155                   break;
156               case USER_TURN_ON:
157               case AIRPLANE_MODE_OFF:
158                   //ignore
159                   break;
160               default:
161                   Log.e(TAG, "Received unhandled state: " + msg.what);
162                   return false;
163            }
164            return true;
165        }
166    }
167
168    private class PendingCommandState extends State {
169        @Override
170        public void enter() {
171            infoLog("Entering PendingCommandState State");
172        }
173
174        @Override
175        public boolean processMessage(Message msg) {
176            switch (msg.what) {
177                case USER_TURN_ON:
178                case USER_TURN_OFF:
179                case AIRPLANE_MODE_ON:
180                case AIRPLANE_MODE_OFF:
181                    deferMessage(msg);
182                    break;
183                case ENABLED_READY:
184                    removeMessages(ENABLE_TIMEOUT);
185                    //Persist enable state only once enable completes
186                    if (mPendingPersistEnable) {
187                        mAdapterService.persistBluetoothSetting(true);
188                        mPendingPersistEnable=false;
189                    }
190                    mAdapterProperties.onBluetoothReady();
191                    sendIntent(BluetoothAdapter.STATE_ON);
192                    transitionTo(mOnState);
193                    break;
194                case DISABLED:
195                    sendIntent(BluetoothAdapter.STATE_OFF);
196                    transitionTo(mOffState);
197                    break;
198                case ENABLE_TIMEOUT:
199                    errorLog("Error enabling Bluetooth");
200                    sendIntent(BluetoothAdapter.STATE_OFF);
201                    transitionTo(mOffState);
202                    break;
203                default:
204                    Log.e(TAG, "Received unhandled event:" + msg.what);
205                    return false;
206            }
207            return true;
208        }
209    }
210
211
212    private void sendIntent(int newState) {
213        int oldState = mAdapterProperties.getState();
214        Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
215        intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, oldState);
216        intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
217        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
218        mAdapterProperties.setState(newState);
219
220        mContext.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
221        infoLog("Bluetooth State Change Intent: " + oldState + " -> " + newState);
222    }
223
224    void stateChangeCallback(int status) {
225        if (status == AbstractionLayer.BT_STATE_OFF) {
226            sendMessage(DISABLED);
227        } else if (status == AbstractionLayer.BT_STATE_ON) {
228            // We should have got the property change for adapter and remote devices.
229            sendMessage(ENABLED_READY);
230        } else {
231            errorLog("Incorrect status in stateChangeCallback");
232        }
233    }
234
235    private void infoLog(String msg) {
236        if (DBG) Log.i(TAG, msg);
237    }
238
239    private void errorLog(String msg) {
240        Log.e(TAG, msg);
241    }
242}
243