Loading src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java +28 −38 Original line number Diff line number Diff line Loading @@ -46,45 +46,35 @@ public class CallAudioRoutePeripheralAdapter implements WiredHeadsetManager.List } @Override public void onBluetoothStateChange(int oldState, int newState) { switch (oldState) { case BluetoothRouteManager.BLUETOOTH_DISCONNECTED: case BluetoothRouteManager.BLUETOOTH_UNINITIALIZED: switch (newState) { case BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED: case BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED: public void onBluetoothDeviceListChanged() { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.CONNECT_BLUETOOTH); break; CallAudioRouteStateMachine.BLUETOOTH_DEVICE_LIST_CHANGED); } break; case BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED: switch (newState) { case BluetoothRouteManager.BLUETOOTH_DISCONNECTED: mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH); break; case BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED: @Override public void onBluetoothDeviceAvailable() { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.SWITCH_BLUETOOTH); break; CallAudioRouteStateMachine.CONNECT_BLUETOOTH); } break; case BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED: case BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING: switch (newState) { case BluetoothRouteManager.BLUETOOTH_DISCONNECTED: @Override public void onBluetoothDeviceUnavailable() { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH); break; case BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED: mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.BT_AUDIO_DISCONNECT); break; } break; @Override public void onBluetoothAudioConnected() { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.BT_AUDIO_CONNECTED); } @Override public void onBluetoothAudioDisconnected() { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.BT_AUDIO_DISCONNECTED); } /** * Updates the audio route when the headset plugged in state changes. For example, if audio is * being routed over speakerphone and a headset is plugged in then switch to wired headset. Loading src/com/android/server/telecom/CallAudioRouteStateMachine.java +123 −40 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.server.telecom; import android.app.ActivityManager; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.content.Context; import android.content.pm.UserInfo; import android.media.AudioManager; Loading @@ -39,6 +41,7 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.server.telecom.bluetooth.BluetoothRouteManager; import java.util.Collection; import java.util.HashMap; /** Loading @@ -63,9 +66,6 @@ import java.util.HashMap; * mIsMuted: a boolean indicating whether the audio is muted */ public class CallAudioRouteStateMachine extends StateMachine { private static final String TELECOM_PACKAGE = CallAudioRouteStateMachine.class.getPackage().getName(); /** Direct the audio stream through the device's earpiece. */ public static final int ROUTE_EARPIECE = CallAudioState.ROUTE_EARPIECE; Loading @@ -85,6 +85,7 @@ public class CallAudioRouteStateMachine extends StateMachine { public static final int DISCONNECT_BLUETOOTH = 4; public static final int CONNECT_DOCK = 5; public static final int DISCONNECT_DOCK = 6; public static final int BLUETOOTH_DEVICE_LIST_CHANGED = 7; public static final int SWITCH_EARPIECE = 1001; public static final int SWITCH_BLUETOOTH = 1002; Loading @@ -92,7 +93,6 @@ public class CallAudioRouteStateMachine extends StateMachine { public static final int SWITCH_SPEAKER = 1004; // Wired headset, earpiece, or speakerphone, in that order of precedence. public static final int SWITCH_BASELINE_ROUTE = 1005; public static final int BT_AUDIO_DISCONNECT = 1006; public static final int USER_SWITCH_EARPIECE = 1101; public static final int USER_SWITCH_BLUETOOTH = 1102; Loading @@ -102,6 +102,15 @@ public class CallAudioRouteStateMachine extends StateMachine { public static final int UPDATE_SYSTEM_AUDIO_ROUTE = 1201; // These three messages indicate state changes that come from BluetoothRouteManager. // They may be triggered by the BT stack doing something on its own or they may be sent after // we request that the BT stack do something. Any logic for these messages should take into // account the possibility that the event indicated has already been processed (i.e. handling // should be idempotent). public static final int BT_AUDIO_DISCONNECTED = 1301; public static final int BT_AUDIO_CONNECTED = 1302; public static final int BT_AUDIO_PENDING = 1303; public static final int MUTE_ON = 3001; public static final int MUTE_OFF = 3002; public static final int TOGGLE_MUTE = 3003; Loading Loading @@ -135,13 +144,13 @@ public class CallAudioRouteStateMachine extends StateMachine { put(DISCONNECT_BLUETOOTH, "DISCONNECT_BLUETOOTH"); put(CONNECT_DOCK, "CONNECT_DOCK"); put(DISCONNECT_DOCK, "DISCONNECT_DOCK"); put(BLUETOOTH_DEVICE_LIST_CHANGED, "BLUETOOTH_DEVICE_LIST_CHANGED"); put(SWITCH_EARPIECE, "SWITCH_EARPIECE"); put(SWITCH_BLUETOOTH, "SWITCH_BLUETOOTH"); put(SWITCH_HEADSET, "SWITCH_HEADSET"); put(SWITCH_SPEAKER, "SWITCH_SPEAKER"); put(SWITCH_BASELINE_ROUTE, "SWITCH_BASELINE_ROUTE"); put(BT_AUDIO_DISCONNECT, "BT_AUDIO_DISCONNECT"); put(USER_SWITCH_EARPIECE, "USER_SWITCH_EARPIECE"); put(USER_SWITCH_BLUETOOTH, "USER_SWITCH_BLUETOOTH"); Loading @@ -151,6 +160,10 @@ public class CallAudioRouteStateMachine extends StateMachine { put(UPDATE_SYSTEM_AUDIO_ROUTE, "UPDATE_SYSTEM_AUDIO_ROUTE"); put(BT_AUDIO_DISCONNECTED, "BT_AUDIO_DISCONNECTED"); put(BT_AUDIO_CONNECTED, "BT_AUDIO_CONNECTED"); put(BT_AUDIO_PENDING, "BT_AUDIO_PENDING"); put(MUTE_ON, "MUTE_ON"); put(MUTE_OFF, "MUTE_OFF"); put(TOGGLE_MUTE, "TOGGLE_MUTE"); Loading Loading @@ -209,6 +222,7 @@ public class CallAudioRouteStateMachine extends StateMachine { public boolean processMessage(Message msg) { int addedRoutes = 0; int removedRoutes = 0; boolean isHandled = NOT_HANDLED; switch (msg.what) { case CONNECT_WIRED_HEADSET: Loading @@ -235,6 +249,19 @@ public class CallAudioRouteStateMachine extends StateMachine { "Bluetooth disconnected"); removedRoutes |= ROUTE_BLUETOOTH; break; case BLUETOOTH_DEVICE_LIST_CHANGED: Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, "Bluetooth device list changed"); Collection<BluetoothDevice> connectedDevices = mBluetoothRouteManager.getConnectedDevices(); if (connectedDevices.size() > 0) { addedRoutes |= ROUTE_BLUETOOTH; } else { removedRoutes |= ROUTE_BLUETOOTH; } // TODO: update in-call app on the list of BT devices. isHandled = HANDLED; break; case SWITCH_BASELINE_ROUTE: sendInternalMessage(calculateBaselineRouteMessage(false, msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE)); Loading @@ -256,7 +283,7 @@ public class CallAudioRouteStateMachine extends StateMachine { addedRoutes, false); } return NOT_HANDLED; return isHandled; } // Behavior will depend on whether the state is an active one or a quiescent one. Loading @@ -280,7 +307,7 @@ public class CallAudioRouteStateMachine extends StateMachine { public void enter() { super.enter(); setSpeakerphoneOn(false); setBluetoothOn(false); setBluetoothOff(); CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE, mAvailableRoutes); setSystemAudioState(newState, true); Loading @@ -303,11 +330,18 @@ public class CallAudioRouteStateMachine extends StateMachine { case USER_SWITCH_EARPIECE: // Nothing to do here return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { transitionTo(mAudioFocusType == ACTIVE_FOCUS ? mActiveBluetoothRoute : mRingingBluetoothRoute); if (mAudioFocusType == ACTIVE_FOCUS || mIsInbandRingSupported) { // Omit transition to ActiveBluetoothRoute setBluetoothOn(); } else { transitionTo(mRingingBluetoothRoute); } } else { Log.w(this, "Ignoring switch to bluetooth command. Not available."); } Loading Loading @@ -368,6 +402,10 @@ public class CallAudioRouteStateMachine extends StateMachine { case USER_SWITCH_EARPIECE: // Nothing to do here return HANDLED; case BT_AUDIO_CONNECTED: Log.w(this, "BT Audio came on in quiescent earpiece route."); transitionTo(mActiveBluetoothRoute); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { Loading Loading @@ -433,7 +471,7 @@ public class CallAudioRouteStateMachine extends StateMachine { "earpiece"); updateSystemAudioState(); return HANDLED; case BT_AUDIO_DISCONNECT: case BT_AUDIO_DISCONNECTED: // This may be sent as a confirmation by the BT stack after switch off BT. return HANDLED; case CONNECT_DOCK: Loading Loading @@ -463,7 +501,7 @@ public class CallAudioRouteStateMachine extends StateMachine { public void enter() { super.enter(); setSpeakerphoneOn(false); setBluetoothOn(false); setBluetoothOff(); CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET, mAvailableRoutes); setSystemAudioState(newState, true); Loading @@ -490,11 +528,18 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { transitionTo(mAudioFocusType == ACTIVE_FOCUS ? mActiveBluetoothRoute : mRingingBluetoothRoute); if (mAudioFocusType == ACTIVE_FOCUS || mIsInbandRingSupported) { // Omit transition to ActiveBluetoothRoute until actual connection. setBluetoothOn(); } else { transitionTo(mRingingBluetoothRoute); } } else { Log.w(this, "Ignoring switch to bluetooth command. Not available."); } Loading Loading @@ -555,6 +600,10 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); Log.w(this, "BT Audio came on in quiescent headset route."); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { Loading Loading @@ -620,7 +669,7 @@ public class CallAudioRouteStateMachine extends StateMachine { sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); } return HANDLED; case BT_AUDIO_DISCONNECT: case BT_AUDIO_DISCONNECTED: // This may be sent as a confirmation by the BT stack after switch off BT. return HANDLED; case CONNECT_DOCK: Loading @@ -635,6 +684,10 @@ public class CallAudioRouteStateMachine extends StateMachine { } } // Note: transitions to/from this class work a bit differently -- we delegate to // BluetoothRouteManager to manage all Bluetooth state, so instead of transitioning to one of // the bluetooth states immediately when there's an request to do so, we wait for // BluetoothRouteManager to report its state before we go into this state. class ActiveBluetoothRoute extends BluetoothRoute { @Override public String getName() { Loading @@ -650,7 +703,6 @@ public class CallAudioRouteStateMachine extends StateMachine { public void enter() { super.enter(); setSpeakerphoneOn(false); setBluetoothOn(true); CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH, mAvailableRoutes); setSystemAudioState(newState, true); Loading Loading @@ -679,6 +731,7 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: // Nothing to do Loading @@ -701,12 +754,14 @@ public class CallAudioRouteStateMachine extends StateMachine { return HANDLED; case SWITCH_FOCUS: if (msg.arg1 == NO_FOCUS) { setBluetoothOff(); reinitialize(); } else if (msg.arg1 == RINGING_FOCUS) { } else if (msg.arg1 == RINGING_FOCUS && !mIsInbandRingSupported) { setBluetoothOff(); transitionTo(mRingingBluetoothRoute); } return HANDLED; case BT_AUDIO_DISCONNECT: case BT_AUDIO_DISCONNECTED: sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE); return HANDLED; default: Loading @@ -715,6 +770,8 @@ public class CallAudioRouteStateMachine extends StateMachine { } } // This state is only used when the device doesn't support in-band ring. If it does, // ActiveBluetoothRoute is used instead. class RingingBluetoothRoute extends BluetoothRoute { @Override public String getName() { Loading Loading @@ -759,6 +816,9 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: // Nothing to do Loading @@ -783,12 +843,12 @@ public class CallAudioRouteStateMachine extends StateMachine { if (msg.arg1 == NO_FOCUS) { reinitialize(); } else if (msg.arg1 == ACTIVE_FOCUS) { transitionTo(mActiveBluetoothRoute); setBluetoothOn(); } return HANDLED; case BT_AUDIO_DISCONNECT: // BT SCO might be connected when in-band ringing is enabled sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE); case BT_AUDIO_DISCONNECTED: // Ignore this -- audio disconnecting while ringing w/o in-band should not // cause a route switch, since the device is still connected. return HANDLED; default: return NOT_HANDLED; Loading @@ -812,7 +872,6 @@ public class CallAudioRouteStateMachine extends StateMachine { super.enter(); mHasUserExplicitlyLeftBluetooth = false; updateInternalCallAudioState(); setBluetoothOn(false); } @Override Loading @@ -834,6 +893,9 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: // Nothing to do Loading @@ -852,12 +914,16 @@ public class CallAudioRouteStateMachine extends StateMachine { return HANDLED; case SWITCH_FOCUS: if (msg.arg1 == ACTIVE_FOCUS) { transitionTo(mActiveBluetoothRoute); setBluetoothOn(); } else if (msg.arg1 == RINGING_FOCUS) { if (mIsInbandRingSupported) { setBluetoothOn(); } else { transitionTo(mRingingBluetoothRoute); } } return HANDLED; case BT_AUDIO_DISCONNECT: case BT_AUDIO_DISCONNECTED: // Ignore this -- audio disconnecting while quiescent should not cause a // route switch, since the device is still connected. return HANDLED; Loading Loading @@ -923,7 +989,7 @@ public class CallAudioRouteStateMachine extends StateMachine { super.enter(); mWasOnSpeaker = true; setSpeakerphoneOn(true); setBluetoothOn(false); setBluetoothOff(); CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER, mAvailableRoutes); setSystemAudioState(newState); Loading Loading @@ -952,13 +1018,20 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); return HANDLED; case USER_SWITCH_BLUETOOTH: mWasOnSpeaker = false; // fall through case SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { transitionTo(mAudioFocusType == ACTIVE_FOCUS ? mActiveBluetoothRoute : mRingingBluetoothRoute); if (mAudioFocusType == ACTIVE_FOCUS || mIsInbandRingSupported) { // Omit transition to ActiveBluetoothRoute setBluetoothOn(); } else { transitionTo(mRingingBluetoothRoute); } } else { Log.w(this, "Ignoring switch to bluetooth command. Not available."); } Loading Loading @@ -1027,6 +1100,10 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); Log.w(this, "BT audio reported as connected while in quiescent speaker"); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { Loading Loading @@ -1090,7 +1167,7 @@ public class CallAudioRouteStateMachine extends StateMachine { updateSystemAudioState(); // No change in audio route required return HANDLED; case BT_AUDIO_DISCONNECT: case BT_AUDIO_DISCONNECTED: // This may be sent as a confirmation by the BT stack after switch off BT. return HANDLED; case CONNECT_DOCK: Loading Loading @@ -1144,6 +1221,8 @@ public class CallAudioRouteStateMachine extends StateMachine { private CallAudioState mCurrentCallAudioState; private CallAudioState mLastKnownCallAudioState; private final boolean mIsInbandRingSupported; public CallAudioRouteStateMachine( Context context, CallsManager callsManager, Loading Loading @@ -1171,6 +1250,7 @@ public class CallAudioRouteStateMachine extends StateMachine { mStatusBarNotifier = statusBarNotifier; mAudioServiceFactory = audioServiceFactory; mDoesDeviceSupportEarpieceRoute = doesDeviceSupportEarpieceRoute; mIsInbandRingSupported = BluetoothHeadset.isInbandRingingSupported(mContext); mLock = callsManager.getLock(); mStateNameToRouteCode = new HashMap<>(8); Loading Loading @@ -1275,8 +1355,7 @@ public class CallAudioRouteStateMachine extends StateMachine { r.run(); return; default: Log.e(this, new IllegalStateException(), "Unexpected message code %d", msg.what); Log.e(this, new IllegalStateException(), "Unexpected message code %d", msg.what); } } Loading @@ -1300,17 +1379,22 @@ public class CallAudioRouteStateMachine extends StateMachine { } } private void setBluetoothOn(boolean on) { private void setBluetoothOn() { if (mBluetoothRouteManager.isBluetoothAvailable()) { if (on != mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { Log.i(this, "connecting bluetooth %s", on); if (on) { if (!mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { Log.i(this, "connecting bluetooth audio"); mBluetoothRouteManager.connectBluetoothAudio(null /*TODO: add real address*/); } else { mBluetoothRouteManager.disconnectBluetoothAudio(); } } } private void setBluetoothOff() { if (mBluetoothRouteManager.isBluetoothAvailable()) { if (mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { Log.i(this, "disconnecting bluetooth audio"); mBluetoothRouteManager.disconnectBluetoothAudio(); } } } private void setMuteOn(boolean mute) { Loading @@ -1331,7 +1415,6 @@ public class CallAudioRouteStateMachine extends StateMachine { audio.setMicrophoneMute( mute, mContext.getOpPackageName(), getCurrentUserId()); mStatusBarNotifier.notifyMute(mute); } catch (RemoteException e) { Log.e(this, e, "Remote exception while toggling mute."); } Loading src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java +7 −3 Original line number Diff line number Diff line Loading @@ -71,7 +71,7 @@ public class BluetoothDeviceManager { mConnectedDevicesByAddress.values()); mConnectedDevicesByAddress.clear(); for (BluetoothDevice device : devicesToRemove) { mBluetoothRouteManager.onDeviceLost(device); mBluetoothRouteManager.onDeviceLost(device.getAddress()); } } } finally { Loading Loading @@ -106,13 +106,13 @@ public class BluetoothDeviceManager { if (bluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED) { if (!mConnectedDevicesByAddress.containsKey(device.getAddress())) { mConnectedDevicesByAddress.put(device.getAddress(), device); mBluetoothRouteManager.onDeviceAdded(device); mBluetoothRouteManager.onDeviceAdded(device.getAddress()); } } else if (bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTED || bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTING) { if (mConnectedDevicesByAddress.containsKey(device.getAddress())) { mConnectedDevicesByAddress.remove(device.getAddress()); mBluetoothRouteManager.onDeviceLost(device); mBluetoothRouteManager.onDeviceLost(device.getAddress()); } } } Loading Loading @@ -151,6 +151,10 @@ public class BluetoothDeviceManager { return mConnectedDevicesByAddress.size(); } public Collection<BluetoothDevice> getConnectedDevices() { return mConnectedDevicesByAddress.values(); } public String getMostRecentlyConnectedDevice(String excludeAddress) { String result = null; synchronized (mLock) { Loading Loading
src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java +28 −38 Original line number Diff line number Diff line Loading @@ -46,45 +46,35 @@ public class CallAudioRoutePeripheralAdapter implements WiredHeadsetManager.List } @Override public void onBluetoothStateChange(int oldState, int newState) { switch (oldState) { case BluetoothRouteManager.BLUETOOTH_DISCONNECTED: case BluetoothRouteManager.BLUETOOTH_UNINITIALIZED: switch (newState) { case BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED: case BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED: public void onBluetoothDeviceListChanged() { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.CONNECT_BLUETOOTH); break; CallAudioRouteStateMachine.BLUETOOTH_DEVICE_LIST_CHANGED); } break; case BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED: switch (newState) { case BluetoothRouteManager.BLUETOOTH_DISCONNECTED: mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH); break; case BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED: @Override public void onBluetoothDeviceAvailable() { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.SWITCH_BLUETOOTH); break; CallAudioRouteStateMachine.CONNECT_BLUETOOTH); } break; case BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED: case BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING: switch (newState) { case BluetoothRouteManager.BLUETOOTH_DISCONNECTED: @Override public void onBluetoothDeviceUnavailable() { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH); break; case BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED: mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.BT_AUDIO_DISCONNECT); break; } break; @Override public void onBluetoothAudioConnected() { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.BT_AUDIO_CONNECTED); } @Override public void onBluetoothAudioDisconnected() { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.BT_AUDIO_DISCONNECTED); } /** * Updates the audio route when the headset plugged in state changes. For example, if audio is * being routed over speakerphone and a headset is plugged in then switch to wired headset. Loading
src/com/android/server/telecom/CallAudioRouteStateMachine.java +123 −40 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.server.telecom; import android.app.ActivityManager; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.content.Context; import android.content.pm.UserInfo; import android.media.AudioManager; Loading @@ -39,6 +41,7 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.server.telecom.bluetooth.BluetoothRouteManager; import java.util.Collection; import java.util.HashMap; /** Loading @@ -63,9 +66,6 @@ import java.util.HashMap; * mIsMuted: a boolean indicating whether the audio is muted */ public class CallAudioRouteStateMachine extends StateMachine { private static final String TELECOM_PACKAGE = CallAudioRouteStateMachine.class.getPackage().getName(); /** Direct the audio stream through the device's earpiece. */ public static final int ROUTE_EARPIECE = CallAudioState.ROUTE_EARPIECE; Loading @@ -85,6 +85,7 @@ public class CallAudioRouteStateMachine extends StateMachine { public static final int DISCONNECT_BLUETOOTH = 4; public static final int CONNECT_DOCK = 5; public static final int DISCONNECT_DOCK = 6; public static final int BLUETOOTH_DEVICE_LIST_CHANGED = 7; public static final int SWITCH_EARPIECE = 1001; public static final int SWITCH_BLUETOOTH = 1002; Loading @@ -92,7 +93,6 @@ public class CallAudioRouteStateMachine extends StateMachine { public static final int SWITCH_SPEAKER = 1004; // Wired headset, earpiece, or speakerphone, in that order of precedence. public static final int SWITCH_BASELINE_ROUTE = 1005; public static final int BT_AUDIO_DISCONNECT = 1006; public static final int USER_SWITCH_EARPIECE = 1101; public static final int USER_SWITCH_BLUETOOTH = 1102; Loading @@ -102,6 +102,15 @@ public class CallAudioRouteStateMachine extends StateMachine { public static final int UPDATE_SYSTEM_AUDIO_ROUTE = 1201; // These three messages indicate state changes that come from BluetoothRouteManager. // They may be triggered by the BT stack doing something on its own or they may be sent after // we request that the BT stack do something. Any logic for these messages should take into // account the possibility that the event indicated has already been processed (i.e. handling // should be idempotent). public static final int BT_AUDIO_DISCONNECTED = 1301; public static final int BT_AUDIO_CONNECTED = 1302; public static final int BT_AUDIO_PENDING = 1303; public static final int MUTE_ON = 3001; public static final int MUTE_OFF = 3002; public static final int TOGGLE_MUTE = 3003; Loading Loading @@ -135,13 +144,13 @@ public class CallAudioRouteStateMachine extends StateMachine { put(DISCONNECT_BLUETOOTH, "DISCONNECT_BLUETOOTH"); put(CONNECT_DOCK, "CONNECT_DOCK"); put(DISCONNECT_DOCK, "DISCONNECT_DOCK"); put(BLUETOOTH_DEVICE_LIST_CHANGED, "BLUETOOTH_DEVICE_LIST_CHANGED"); put(SWITCH_EARPIECE, "SWITCH_EARPIECE"); put(SWITCH_BLUETOOTH, "SWITCH_BLUETOOTH"); put(SWITCH_HEADSET, "SWITCH_HEADSET"); put(SWITCH_SPEAKER, "SWITCH_SPEAKER"); put(SWITCH_BASELINE_ROUTE, "SWITCH_BASELINE_ROUTE"); put(BT_AUDIO_DISCONNECT, "BT_AUDIO_DISCONNECT"); put(USER_SWITCH_EARPIECE, "USER_SWITCH_EARPIECE"); put(USER_SWITCH_BLUETOOTH, "USER_SWITCH_BLUETOOTH"); Loading @@ -151,6 +160,10 @@ public class CallAudioRouteStateMachine extends StateMachine { put(UPDATE_SYSTEM_AUDIO_ROUTE, "UPDATE_SYSTEM_AUDIO_ROUTE"); put(BT_AUDIO_DISCONNECTED, "BT_AUDIO_DISCONNECTED"); put(BT_AUDIO_CONNECTED, "BT_AUDIO_CONNECTED"); put(BT_AUDIO_PENDING, "BT_AUDIO_PENDING"); put(MUTE_ON, "MUTE_ON"); put(MUTE_OFF, "MUTE_OFF"); put(TOGGLE_MUTE, "TOGGLE_MUTE"); Loading Loading @@ -209,6 +222,7 @@ public class CallAudioRouteStateMachine extends StateMachine { public boolean processMessage(Message msg) { int addedRoutes = 0; int removedRoutes = 0; boolean isHandled = NOT_HANDLED; switch (msg.what) { case CONNECT_WIRED_HEADSET: Loading @@ -235,6 +249,19 @@ public class CallAudioRouteStateMachine extends StateMachine { "Bluetooth disconnected"); removedRoutes |= ROUTE_BLUETOOTH; break; case BLUETOOTH_DEVICE_LIST_CHANGED: Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, "Bluetooth device list changed"); Collection<BluetoothDevice> connectedDevices = mBluetoothRouteManager.getConnectedDevices(); if (connectedDevices.size() > 0) { addedRoutes |= ROUTE_BLUETOOTH; } else { removedRoutes |= ROUTE_BLUETOOTH; } // TODO: update in-call app on the list of BT devices. isHandled = HANDLED; break; case SWITCH_BASELINE_ROUTE: sendInternalMessage(calculateBaselineRouteMessage(false, msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE)); Loading @@ -256,7 +283,7 @@ public class CallAudioRouteStateMachine extends StateMachine { addedRoutes, false); } return NOT_HANDLED; return isHandled; } // Behavior will depend on whether the state is an active one or a quiescent one. Loading @@ -280,7 +307,7 @@ public class CallAudioRouteStateMachine extends StateMachine { public void enter() { super.enter(); setSpeakerphoneOn(false); setBluetoothOn(false); setBluetoothOff(); CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE, mAvailableRoutes); setSystemAudioState(newState, true); Loading @@ -303,11 +330,18 @@ public class CallAudioRouteStateMachine extends StateMachine { case USER_SWITCH_EARPIECE: // Nothing to do here return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { transitionTo(mAudioFocusType == ACTIVE_FOCUS ? mActiveBluetoothRoute : mRingingBluetoothRoute); if (mAudioFocusType == ACTIVE_FOCUS || mIsInbandRingSupported) { // Omit transition to ActiveBluetoothRoute setBluetoothOn(); } else { transitionTo(mRingingBluetoothRoute); } } else { Log.w(this, "Ignoring switch to bluetooth command. Not available."); } Loading Loading @@ -368,6 +402,10 @@ public class CallAudioRouteStateMachine extends StateMachine { case USER_SWITCH_EARPIECE: // Nothing to do here return HANDLED; case BT_AUDIO_CONNECTED: Log.w(this, "BT Audio came on in quiescent earpiece route."); transitionTo(mActiveBluetoothRoute); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { Loading Loading @@ -433,7 +471,7 @@ public class CallAudioRouteStateMachine extends StateMachine { "earpiece"); updateSystemAudioState(); return HANDLED; case BT_AUDIO_DISCONNECT: case BT_AUDIO_DISCONNECTED: // This may be sent as a confirmation by the BT stack after switch off BT. return HANDLED; case CONNECT_DOCK: Loading Loading @@ -463,7 +501,7 @@ public class CallAudioRouteStateMachine extends StateMachine { public void enter() { super.enter(); setSpeakerphoneOn(false); setBluetoothOn(false); setBluetoothOff(); CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET, mAvailableRoutes); setSystemAudioState(newState, true); Loading @@ -490,11 +528,18 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { transitionTo(mAudioFocusType == ACTIVE_FOCUS ? mActiveBluetoothRoute : mRingingBluetoothRoute); if (mAudioFocusType == ACTIVE_FOCUS || mIsInbandRingSupported) { // Omit transition to ActiveBluetoothRoute until actual connection. setBluetoothOn(); } else { transitionTo(mRingingBluetoothRoute); } } else { Log.w(this, "Ignoring switch to bluetooth command. Not available."); } Loading Loading @@ -555,6 +600,10 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); Log.w(this, "BT Audio came on in quiescent headset route."); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { Loading Loading @@ -620,7 +669,7 @@ public class CallAudioRouteStateMachine extends StateMachine { sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); } return HANDLED; case BT_AUDIO_DISCONNECT: case BT_AUDIO_DISCONNECTED: // This may be sent as a confirmation by the BT stack after switch off BT. return HANDLED; case CONNECT_DOCK: Loading @@ -635,6 +684,10 @@ public class CallAudioRouteStateMachine extends StateMachine { } } // Note: transitions to/from this class work a bit differently -- we delegate to // BluetoothRouteManager to manage all Bluetooth state, so instead of transitioning to one of // the bluetooth states immediately when there's an request to do so, we wait for // BluetoothRouteManager to report its state before we go into this state. class ActiveBluetoothRoute extends BluetoothRoute { @Override public String getName() { Loading @@ -650,7 +703,6 @@ public class CallAudioRouteStateMachine extends StateMachine { public void enter() { super.enter(); setSpeakerphoneOn(false); setBluetoothOn(true); CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH, mAvailableRoutes); setSystemAudioState(newState, true); Loading Loading @@ -679,6 +731,7 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: // Nothing to do Loading @@ -701,12 +754,14 @@ public class CallAudioRouteStateMachine extends StateMachine { return HANDLED; case SWITCH_FOCUS: if (msg.arg1 == NO_FOCUS) { setBluetoothOff(); reinitialize(); } else if (msg.arg1 == RINGING_FOCUS) { } else if (msg.arg1 == RINGING_FOCUS && !mIsInbandRingSupported) { setBluetoothOff(); transitionTo(mRingingBluetoothRoute); } return HANDLED; case BT_AUDIO_DISCONNECT: case BT_AUDIO_DISCONNECTED: sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE); return HANDLED; default: Loading @@ -715,6 +770,8 @@ public class CallAudioRouteStateMachine extends StateMachine { } } // This state is only used when the device doesn't support in-band ring. If it does, // ActiveBluetoothRoute is used instead. class RingingBluetoothRoute extends BluetoothRoute { @Override public String getName() { Loading Loading @@ -759,6 +816,9 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: // Nothing to do Loading @@ -783,12 +843,12 @@ public class CallAudioRouteStateMachine extends StateMachine { if (msg.arg1 == NO_FOCUS) { reinitialize(); } else if (msg.arg1 == ACTIVE_FOCUS) { transitionTo(mActiveBluetoothRoute); setBluetoothOn(); } return HANDLED; case BT_AUDIO_DISCONNECT: // BT SCO might be connected when in-band ringing is enabled sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE); case BT_AUDIO_DISCONNECTED: // Ignore this -- audio disconnecting while ringing w/o in-band should not // cause a route switch, since the device is still connected. return HANDLED; default: return NOT_HANDLED; Loading @@ -812,7 +872,6 @@ public class CallAudioRouteStateMachine extends StateMachine { super.enter(); mHasUserExplicitlyLeftBluetooth = false; updateInternalCallAudioState(); setBluetoothOn(false); } @Override Loading @@ -834,6 +893,9 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: // Nothing to do Loading @@ -852,12 +914,16 @@ public class CallAudioRouteStateMachine extends StateMachine { return HANDLED; case SWITCH_FOCUS: if (msg.arg1 == ACTIVE_FOCUS) { transitionTo(mActiveBluetoothRoute); setBluetoothOn(); } else if (msg.arg1 == RINGING_FOCUS) { if (mIsInbandRingSupported) { setBluetoothOn(); } else { transitionTo(mRingingBluetoothRoute); } } return HANDLED; case BT_AUDIO_DISCONNECT: case BT_AUDIO_DISCONNECTED: // Ignore this -- audio disconnecting while quiescent should not cause a // route switch, since the device is still connected. return HANDLED; Loading Loading @@ -923,7 +989,7 @@ public class CallAudioRouteStateMachine extends StateMachine { super.enter(); mWasOnSpeaker = true; setSpeakerphoneOn(true); setBluetoothOn(false); setBluetoothOff(); CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER, mAvailableRoutes); setSystemAudioState(newState); Loading Loading @@ -952,13 +1018,20 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); return HANDLED; case USER_SWITCH_BLUETOOTH: mWasOnSpeaker = false; // fall through case SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { transitionTo(mAudioFocusType == ACTIVE_FOCUS ? mActiveBluetoothRoute : mRingingBluetoothRoute); if (mAudioFocusType == ACTIVE_FOCUS || mIsInbandRingSupported) { // Omit transition to ActiveBluetoothRoute setBluetoothOn(); } else { transitionTo(mRingingBluetoothRoute); } } else { Log.w(this, "Ignoring switch to bluetooth command. Not available."); } Loading Loading @@ -1027,6 +1100,10 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to earpiece command. Not available."); } return HANDLED; case BT_AUDIO_CONNECTED: transitionTo(mActiveBluetoothRoute); Log.w(this, "BT audio reported as connected while in quiescent speaker"); return HANDLED; case SWITCH_BLUETOOTH: case USER_SWITCH_BLUETOOTH: if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { Loading Loading @@ -1090,7 +1167,7 @@ public class CallAudioRouteStateMachine extends StateMachine { updateSystemAudioState(); // No change in audio route required return HANDLED; case BT_AUDIO_DISCONNECT: case BT_AUDIO_DISCONNECTED: // This may be sent as a confirmation by the BT stack after switch off BT. return HANDLED; case CONNECT_DOCK: Loading Loading @@ -1144,6 +1221,8 @@ public class CallAudioRouteStateMachine extends StateMachine { private CallAudioState mCurrentCallAudioState; private CallAudioState mLastKnownCallAudioState; private final boolean mIsInbandRingSupported; public CallAudioRouteStateMachine( Context context, CallsManager callsManager, Loading Loading @@ -1171,6 +1250,7 @@ public class CallAudioRouteStateMachine extends StateMachine { mStatusBarNotifier = statusBarNotifier; mAudioServiceFactory = audioServiceFactory; mDoesDeviceSupportEarpieceRoute = doesDeviceSupportEarpieceRoute; mIsInbandRingSupported = BluetoothHeadset.isInbandRingingSupported(mContext); mLock = callsManager.getLock(); mStateNameToRouteCode = new HashMap<>(8); Loading Loading @@ -1275,8 +1355,7 @@ public class CallAudioRouteStateMachine extends StateMachine { r.run(); return; default: Log.e(this, new IllegalStateException(), "Unexpected message code %d", msg.what); Log.e(this, new IllegalStateException(), "Unexpected message code %d", msg.what); } } Loading @@ -1300,17 +1379,22 @@ public class CallAudioRouteStateMachine extends StateMachine { } } private void setBluetoothOn(boolean on) { private void setBluetoothOn() { if (mBluetoothRouteManager.isBluetoothAvailable()) { if (on != mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { Log.i(this, "connecting bluetooth %s", on); if (on) { if (!mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { Log.i(this, "connecting bluetooth audio"); mBluetoothRouteManager.connectBluetoothAudio(null /*TODO: add real address*/); } else { mBluetoothRouteManager.disconnectBluetoothAudio(); } } } private void setBluetoothOff() { if (mBluetoothRouteManager.isBluetoothAvailable()) { if (mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { Log.i(this, "disconnecting bluetooth audio"); mBluetoothRouteManager.disconnectBluetoothAudio(); } } } private void setMuteOn(boolean mute) { Loading @@ -1331,7 +1415,6 @@ public class CallAudioRouteStateMachine extends StateMachine { audio.setMicrophoneMute( mute, mContext.getOpPackageName(), getCurrentUserId()); mStatusBarNotifier.notifyMute(mute); } catch (RemoteException e) { Log.e(this, e, "Remote exception while toggling mute."); } Loading
src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java +7 −3 Original line number Diff line number Diff line Loading @@ -71,7 +71,7 @@ public class BluetoothDeviceManager { mConnectedDevicesByAddress.values()); mConnectedDevicesByAddress.clear(); for (BluetoothDevice device : devicesToRemove) { mBluetoothRouteManager.onDeviceLost(device); mBluetoothRouteManager.onDeviceLost(device.getAddress()); } } } finally { Loading Loading @@ -106,13 +106,13 @@ public class BluetoothDeviceManager { if (bluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED) { if (!mConnectedDevicesByAddress.containsKey(device.getAddress())) { mConnectedDevicesByAddress.put(device.getAddress(), device); mBluetoothRouteManager.onDeviceAdded(device); mBluetoothRouteManager.onDeviceAdded(device.getAddress()); } } else if (bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTED || bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTING) { if (mConnectedDevicesByAddress.containsKey(device.getAddress())) { mConnectedDevicesByAddress.remove(device.getAddress()); mBluetoothRouteManager.onDeviceLost(device); mBluetoothRouteManager.onDeviceLost(device.getAddress()); } } } Loading Loading @@ -151,6 +151,10 @@ public class BluetoothDeviceManager { return mConnectedDevicesByAddress.size(); } public Collection<BluetoothDevice> getConnectedDevices() { return mConnectedDevicesByAddress.values(); } public String getMostRecentlyConnectedDevice(String excludeAddress) { String result = null; synchronized (mLock) { Loading