AdapterState.java revision 0708fe3087b690439dd9745b2cf1a96f29f570b0
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 final AdapterService mAdapterService;
43    private final Context mContext;
44    private final AdapterProperties mAdapterProperties;
45
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        setInitialState(mOffState);
57        mAdapterService = service;
58        mContext = context;
59        mAdapterProperties = adapterProperties;
60    }
61
62    private class OffState extends State {
63        @Override
64        public void enter() {
65            infoLog("Entering Off State");
66        }
67
68        @Override
69        public boolean processMessage(Message msg) {
70            switch(msg.what) {
71               case USER_TURN_ON:
72                   int persist = msg.arg1;
73                   if (persist == 1) mAdapterService.persistBluetoothSetting(true);
74                   sendIntent(BluetoothAdapter.STATE_TURNING_ON);
75                   boolean ret = mAdapterService.enableNative();
76                   if (!ret) {
77                       Log.e(TAG, "Error while turning Bluetooth On");
78                       sendIntent(BluetoothAdapter.STATE_OFF);
79                   } else {
80                       sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
81                       transitionTo(mPendingCommandState);
82                   }
83                   break;
84               case AIRPLANE_MODE_OFF:
85               {
86                   if(mAdapterService.getBluetoothPersistedSetting()) {
87                        Log.i(TAG, "OffState : Turning BT on after Airplane"+
88                            " Mode OFF state");
89                        sendIntent(BluetoothAdapter.STATE_TURNING_ON);
90                        ret = mAdapterService.enableNative();
91                        if (!ret) {
92                            Log.e(TAG, "Error while turning Bluetooth On");
93                            sendIntent(BluetoothAdapter.STATE_OFF);
94                        } else {
95                            sendMessageDelayed(ENABLE_TIMEOUT,
96                                ENABLE_TIMEOUT_DELAY);
97                            transitionTo(mPendingCommandState);
98                        }
99                   }
100                   break;
101               }
102               case USER_TURN_OFF:
103               case AIRPLANE_MODE_ON:
104                   //ignore
105                   break;
106               default:
107                   Log.e(TAG, "Received unhandled state: " + msg.what);
108                   return false;
109            }
110            return true;
111        }
112    }
113
114    private class OnState extends State {
115        @Override
116        public void enter() {
117            infoLog("Entering On State");
118        }
119
120        @Override
121        public boolean processMessage(Message msg) {
122            switch(msg.what) {
123               case USER_TURN_OFF:
124                   int persist = msg.arg1;
125                   if (persist == 1) {
126                           mAdapterService.persistBluetoothSetting(false);
127                   }
128                   //Fall Through
129               case AIRPLANE_MODE_ON:
130                   sendIntent(BluetoothAdapter.STATE_TURNING_OFF);
131                   if (mAdapterProperties.getConnectionState() !=
132                           BluetoothAdapter.STATE_DISCONNECTED) {
133                       sendMessageDelayed(ALL_DEVICES_DISCONNECTED,
134                                   DISCONNECT_TIMEOUT);
135                       break;
136                   }
137                   //Fall Through
138               case ALL_DEVICES_DISCONNECTED:
139                   boolean ret = mAdapterService.disableNative();
140                   if (!ret) {
141                       Log.e(TAG, "Error while turning Bluetooth Off");
142                       sendIntent(BluetoothAdapter.STATE_ON);
143                   } else {
144                       transitionTo(mPendingCommandState);
145                   }
146                   break;
147               case USER_TURN_ON:
148               case AIRPLANE_MODE_OFF:
149                   //ignore
150                   break;
151               default:
152                   Log.e(TAG, "Received unhandled state: " + msg.what);
153                   return false;
154            }
155            return true;
156        }
157    }
158
159    private class PendingCommandState extends State {
160        @Override
161        public void enter() {
162            infoLog("Entering PendingCommandState State");
163        }
164
165        @Override
166        public boolean processMessage(Message msg) {
167            switch (msg.what) {
168                case USER_TURN_ON:
169                case USER_TURN_OFF:
170                case AIRPLANE_MODE_ON:
171                case AIRPLANE_MODE_OFF:
172                    deferMessage(msg);
173                    break;
174                case ENABLED_READY:
175                    removeMessages(ENABLE_TIMEOUT);
176                    mAdapterProperties.onBluetoothReady();
177                    sendIntent(BluetoothAdapter.STATE_ON);
178                    transitionTo(mOnState);
179                    break;
180                case DISABLED:
181                    sendIntent(BluetoothAdapter.STATE_OFF);
182                    transitionTo(mOffState);
183                    break;
184                case ENABLE_TIMEOUT:
185                    errorLog("Error enabling Bluetooth");
186                    sendIntent(BluetoothAdapter.STATE_OFF);
187                    transitionTo(mOffState);
188                    break;
189                default:
190                    Log.e(TAG, "Received unhandled event:" + msg.what);
191                    return false;
192            }
193            return true;
194        }
195    }
196
197
198    private void sendIntent(int newState) {
199        int oldState = mAdapterProperties.getState();
200        Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
201        intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, oldState);
202        intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
203        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
204        mAdapterProperties.setState(newState);
205
206        mContext.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
207        infoLog("Bluetooth State Change Intent: " + oldState + " -> " + newState);
208    }
209
210    void stateChangeCallback(int status) {
211        if (status == AbstractionLayer.BT_STATE_OFF) {
212            sendMessage(DISABLED);
213        } else if (status == AbstractionLayer.BT_STATE_ON) {
214            // We should have got the property change for adapter and remote devices.
215            sendMessage(ENABLED_READY);
216        } else {
217            errorLog("Incorrect status in stateChangeCallback");
218        }
219    }
220
221    private void infoLog(String msg) {
222        if (DBG) Log.i(TAG, msg);
223    }
224
225    private void errorLog(String msg) {
226        Log.e(TAG, msg);
227    }
228}
229