Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.bluetooth;
     17 
     18 import android.content.BroadcastReceiver;
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.content.IntentFilter;
     22 import android.os.Message;
     23 import android.util.Log;
     24 
     25 import com.android.internal.util.State;
     26 import com.android.internal.util.StateMachine;
     27 
     28 /**
     29  * This state machine is used to serialize the connections
     30  * to a particular profile. Currently, we only allow one device
     31  * to be connected to a particular profile.
     32  * States:
     33  *      {@link StableState} : No pending commands. Send the
     34  *      command to the appropriate remote device specific state machine.
     35  *
     36  *      {@link PendingCommandState} : A profile connection / disconnection
     37  *      command is being executed. This will result in a profile state
     38  *      change. Defer all commands.
     39  * @hide
     40  */
     41 
     42 public class BluetoothProfileState extends StateMachine {
     43     private static final boolean DBG = true;
     44     private static final String TAG = "BluetoothProfileState";
     45 
     46     public static final int HFP = 0;
     47     public static final int A2DP = 1;
     48     public static final int HID = 2;
     49 
     50     static final int TRANSITION_TO_STABLE = 100;
     51 
     52     private int mProfile;
     53     private BluetoothDevice mPendingDevice;
     54     private PendingCommandState mPendingCommandState = new PendingCommandState();
     55     private StableState mStableState = new StableState();
     56 
     57     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
     58         @Override
     59         public void onReceive(Context context, Intent intent) {
     60             String action = intent.getAction();
     61             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
     62             if (device == null) {
     63                 return;
     64             }
     65             if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
     66                 int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
     67                 if (mProfile == HFP && (newState == BluetoothProfile.STATE_CONNECTED ||
     68                     newState == BluetoothProfile.STATE_DISCONNECTED)) {
     69                     sendMessage(TRANSITION_TO_STABLE);
     70                 }
     71             } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
     72                 int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
     73                 if (mProfile == A2DP && (newState == BluetoothProfile.STATE_CONNECTED ||
     74                     newState == BluetoothProfile.STATE_DISCONNECTED)) {
     75                     sendMessage(TRANSITION_TO_STABLE);
     76                 }
     77             } else if (action.equals(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED)) {
     78                 int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
     79                 if (mProfile == HID && (newState == BluetoothProfile.STATE_CONNECTED ||
     80                     newState == BluetoothProfile.STATE_DISCONNECTED)) {
     81                     sendMessage(TRANSITION_TO_STABLE);
     82                 }
     83             } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
     84                 if (device.equals(mPendingDevice)) {
     85                     sendMessage(TRANSITION_TO_STABLE);
     86                 }
     87             }
     88         }
     89     };
     90 
     91     public BluetoothProfileState(Context context, int profile) {
     92         super("BluetoothProfileState:" + profile);
     93         mProfile = profile;
     94         addState(mStableState);
     95         addState(mPendingCommandState);
     96         setInitialState(mStableState);
     97 
     98         IntentFilter filter = new IntentFilter();
     99         filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
    100         filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
    101         filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
    102         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    103         context.registerReceiver(mBroadcastReceiver, filter);
    104     }
    105 
    106     private class StableState extends State {
    107         @Override
    108         public void enter() {
    109             log("Entering Stable State");
    110             mPendingDevice = null;
    111         }
    112 
    113         @Override
    114         public boolean processMessage(Message msg) {
    115             if (msg.what != TRANSITION_TO_STABLE) {
    116                 transitionTo(mPendingCommandState);
    117             }
    118             return true;
    119         }
    120     }
    121 
    122     private class PendingCommandState extends State {
    123         @Override
    124         public void enter() {
    125             log("Entering PendingCommandState State");
    126             dispatchMessage(getCurrentMessage());
    127         }
    128 
    129         @Override
    130         public boolean processMessage(Message msg) {
    131             if (msg.what == TRANSITION_TO_STABLE) {
    132                 transitionTo(mStableState);
    133             } else {
    134                 dispatchMessage(msg);
    135             }
    136             return true;
    137         }
    138 
    139         private void dispatchMessage(Message msg) {
    140             BluetoothDeviceProfileState deviceProfileMgr =
    141               (BluetoothDeviceProfileState)msg.obj;
    142             int cmd = msg.arg1;
    143             if (mPendingDevice == null || mPendingDevice.equals(deviceProfileMgr.getDevice())) {
    144                 mPendingDevice = deviceProfileMgr.getDevice();
    145                 deviceProfileMgr.sendMessage(cmd);
    146             } else {
    147                 Message deferMsg = new Message();
    148                 deferMsg.arg1 = cmd;
    149                 deferMsg.obj = deviceProfileMgr;
    150                 deferMessage(deferMsg);
    151             }
    152         }
    153     }
    154 
    155     private void log(String message) {
    156         if (DBG) {
    157             Log.i(TAG, "Message:" + message);
    158         }
    159     }
    160 }
    161