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 */ 16package android.bluetooth; 17 18import android.content.BroadcastReceiver; 19import android.content.Context; 20import android.content.Intent; 21import android.content.IntentFilter; 22import android.os.Message; 23import android.util.Log; 24 25import com.android.internal.util.State; 26import 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 42public 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