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.HierarchicalState;
     26 import com.android.internal.util.HierarchicalStateMachine;
     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 HierarchicalStateMachine {
     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 
     49     static final int TRANSITION_TO_STABLE = 100;
     50 
     51     private int mProfile;
     52     private BluetoothDevice mPendingDevice;
     53     private PendingCommandState mPendingCommandState = new PendingCommandState();
     54     private StableState mStableState = new StableState();
     55 
     56     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
     57         @Override
     58         public void onReceive(Context context, Intent intent) {
     59             String action = intent.getAction();
     60             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
     61             if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
     62                 int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
     63                 if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED ||
     64                     newState == BluetoothHeadset.STATE_DISCONNECTED)) {
     65                     sendMessage(TRANSITION_TO_STABLE);
     66                 }
     67             } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
     68                 int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
     69                 if (mProfile == A2DP && (newState == BluetoothA2dp.STATE_CONNECTED ||
     70                     newState == BluetoothA2dp.STATE_DISCONNECTED)) {
     71                     sendMessage(TRANSITION_TO_STABLE);
     72                 }
     73             } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
     74                 if (device.equals(mPendingDevice)) {
     75                     sendMessage(TRANSITION_TO_STABLE);
     76                 }
     77             }
     78         }
     79     };
     80 
     81     public BluetoothProfileState(Context context, int profile) {
     82         super("BluetoothProfileState:" + profile);
     83         mProfile = profile;
     84         addState(mStableState);
     85         addState(mPendingCommandState);
     86         setInitialState(mStableState);
     87 
     88         IntentFilter filter = new IntentFilter();
     89         filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
     90         filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
     91         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
     92         context.registerReceiver(mBroadcastReceiver, filter);
     93     }
     94 
     95     private class StableState extends HierarchicalState {
     96         @Override
     97         protected void enter() {
     98             log("Entering Stable State");
     99             mPendingDevice = null;
    100         }
    101 
    102         @Override
    103         protected boolean processMessage(Message msg) {
    104             if (msg.what != TRANSITION_TO_STABLE) {
    105                 transitionTo(mPendingCommandState);
    106             }
    107             return true;
    108         }
    109     }
    110 
    111     private class PendingCommandState extends HierarchicalState {
    112         @Override
    113         protected void enter() {
    114             log("Entering PendingCommandState State");
    115             dispatchMessage(getCurrentMessage());
    116         }
    117 
    118         @Override
    119         protected boolean processMessage(Message msg) {
    120             if (msg.what == TRANSITION_TO_STABLE) {
    121                 transitionTo(mStableState);
    122             } else {
    123                 dispatchMessage(msg);
    124             }
    125             return true;
    126         }
    127 
    128         private void dispatchMessage(Message msg) {
    129             BluetoothDeviceProfileState deviceProfileMgr =
    130               (BluetoothDeviceProfileState)msg.obj;
    131             int cmd = msg.arg1;
    132             if (mPendingDevice == null || mPendingDevice.equals(deviceProfileMgr.getDevice())) {
    133                 mPendingDevice = deviceProfileMgr.getDevice();
    134                 deviceProfileMgr.sendMessage(cmd);
    135             } else {
    136                 Message deferMsg = new Message();
    137                 deferMsg.arg1 = cmd;
    138                 deferMsg.obj = deviceProfileMgr;
    139                 deferMessage(deferMsg);
    140             }
    141         }
    142     }
    143 
    144     private void log(String message) {
    145         if (DBG) {
    146             Log.i(TAG, "Message:" + message);
    147         }
    148     }
    149 }
    150