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