Handle audio route restrictions set on calls
When a call restricts audio routes, it will move the audio route to a
supported route if available. The supported routes are determined initially
and re-evaluated when the call becomes the foreground call.
This is a cherry-pick of abandoned ag/1520954
Bug: 32958838
Change-Id: Ie30847c995839516507df0c70178a591c39f6cdd
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index b9b2fda..2e77654 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -26,6 +26,7 @@
import android.os.RemoteException;
import android.os.Trace;
import android.provider.ContactsContract.Contacts;
+import android.telecom.CallAudioState;
import android.telecom.DisconnectCause;
import android.telecom.Connection;
import android.telecom.GatewayInfo;
@@ -322,6 +323,8 @@
private int mConnectionProperties;
+ private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL;
+
private boolean mIsConference = false;
private final boolean mShouldAttachToExistingConnection;
@@ -1080,6 +1083,16 @@
}
}
+ int getSupportedAudioRoutes() {
+ return mSupportedAudioRoutes;
+ }
+
+ void setSupportedAudioRoutes(int audioRoutes) {
+ if (mSupportedAudioRoutes != audioRoutes) {
+ mSupportedAudioRoutes = audioRoutes;
+ }
+ }
+
@VisibleForTesting
public Call getParentCall() {
return mParentCall;
@@ -1176,6 +1189,7 @@
setConnectionCapabilities(connection.getConnectionCapabilities());
setConnectionProperties(connection.getConnectionProperties());
+ setSupportedAudioRoutes(connection.getSupportedAudioRoutes());
setVideoProvider(connection.getVideoProvider());
setVideoState(connection.getVideoState());
setRingbackRequested(connection.isRingbackRequested());
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index 28b3ec5..9014dbe 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -207,6 +207,16 @@
Log.endSession();
}
+ private int getCurrentCallSupportedRoutes() {
+ int supportedRoutes = CallAudioState.ROUTE_ALL;
+
+ if (mCallsManager.getForegroundCall() != null) {
+ supportedRoutes &= mCallsManager.getForegroundCall().getSupportedAudioRoutes();
+ }
+
+ return supportedRoutes;
+ }
+
abstract class AudioState extends State {
@Override
public void enter() {
@@ -224,31 +234,34 @@
@Override
public boolean processMessage(Message msg) {
+ int addedRoutes = 0;
+ int removedRoutes = 0;
+
switch (msg.what) {
case CONNECT_WIRED_HEADSET:
Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
"Wired headset connected");
- mAvailableRoutes &= ~ROUTE_EARPIECE;
- mAvailableRoutes |= ROUTE_WIRED_HEADSET;
- return NOT_HANDLED;
+ removedRoutes |= ROUTE_EARPIECE;
+ addedRoutes |= ROUTE_WIRED_HEADSET;
+ break;
case CONNECT_BLUETOOTH:
Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
"Bluetooth connected");
- mAvailableRoutes |= ROUTE_BLUETOOTH;
- return NOT_HANDLED;
+ addedRoutes |= ROUTE_BLUETOOTH;
+ break;
case DISCONNECT_WIRED_HEADSET:
Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
"Wired headset disconnected");
- mAvailableRoutes &= ~ROUTE_WIRED_HEADSET;
+ removedRoutes |= ROUTE_WIRED_HEADSET;
if (mDoesDeviceSupportEarpieceRoute) {
- mAvailableRoutes |= ROUTE_EARPIECE;
+ addedRoutes |= ROUTE_EARPIECE;
}
- return NOT_HANDLED;
+ break;
case DISCONNECT_BLUETOOTH:
Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
"Bluetooth disconnected");
- mAvailableRoutes &= ~ROUTE_BLUETOOTH;
- return NOT_HANDLED;
+ removedRoutes |= ROUTE_BLUETOOTH;
+ break;
case SWITCH_BASELINE_ROUTE:
sendInternalMessage(calculateBaselineRouteMessage(false));
return HANDLED;
@@ -261,6 +274,14 @@
default:
return NOT_HANDLED;
}
+
+ if (addedRoutes != 0 || removedRoutes != 0) {
+ mAvailableRoutes = modifyRoutes(mAvailableRoutes, removedRoutes, addedRoutes, true);
+ mDeviceSupportedRoutes = modifyRoutes(mDeviceSupportedRoutes, removedRoutes,
+ addedRoutes, false);
+ }
+
+ return NOT_HANDLED;
}
// Behavior will depend on whether the state is an active one or a quiescent one.
@@ -268,6 +289,18 @@
abstract public boolean isActive();
}
+ private int modifyRoutes(int base, int remove, int add, boolean considerCurrentCall) {
+ base &= ~remove;
+
+ if (considerCurrentCall) {
+ add &= getCurrentCallSupportedRoutes();
+ }
+
+ base |= add;
+
+ return base;
+ }
+
class ActiveEarpieceRoute extends EarpieceRoute {
@Override
public String getName() {
@@ -1114,6 +1147,7 @@
* A few pieces of hidden state. Used to avoid exponential explosion of number of explicit
* states
*/
+ private int mDeviceSupportedRoutes;
private int mAvailableRoutes;
private int mAudioFocusType;
private boolean mWasOnSpeaker;
@@ -1203,9 +1237,15 @@
}
public void initialize(CallAudioState initState) {
+ if ((initState.getRoute() & getCurrentCallSupportedRoutes()) == 0) {
+ Log.e(this, new IllegalArgumentException(), "Route " + initState.getRoute()
+ + "specified when supported call routes are:" + getCurrentCallSupportedRoutes());
+ }
+
mCurrentCallAudioState = initState;
mLastKnownCallAudioState = initState;
- mAvailableRoutes = initState.getSupportedRouteMask();
+ mDeviceSupportedRoutes = initState.getSupportedRouteMask();
+ mAvailableRoutes = mDeviceSupportedRoutes;
mIsMuted = initState.isMuted();
mWasOnSpeaker = false;
@@ -1263,6 +1303,7 @@
}
return;
case UPDATE_SYSTEM_AUDIO_ROUTE:
+ updateRouteForForegroundCall();
resendSystemAudioState();
return;
case RUN_RUNNABLE:
@@ -1460,7 +1501,7 @@
}
private CallAudioState getInitialAudioState() {
- int supportedRouteMask = calculateSupportedRoutes();
+ int supportedRouteMask = calculateSupportedRoutes() & getCurrentCallSupportedRoutes();
final int route;
if ((supportedRouteMask & ROUTE_BLUETOOTH) != 0) {
@@ -1525,7 +1566,8 @@
private void reinitialize() {
CallAudioState initState = getInitialAudioState();
- mAvailableRoutes = initState.getSupportedRouteMask();
+ mDeviceSupportedRoutes = initState.getSupportedRouteMask();
+ mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes();
mIsMuted = initState.isMuted();
setMuteOn(mIsMuted);
mWasOnSpeaker = false;
@@ -1533,4 +1575,15 @@
mLastKnownCallAudioState = initState;
transitionTo(mRouteCodeToQuiescentState.get(initState.getRoute()));
}
+
+ private void updateRouteForForegroundCall() {
+ mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes();
+
+ CallAudioState currentState = getCurrentCallAudioState();
+
+ // Move to baseline route in the case the current route is no longer available.
+ if ((mAvailableRoutes & currentState.getRoute()) == 0) {
+ sendInternalMessage(calculateBaselineRouteMessage(false));
+ }
+ }
}
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index 9cc61b3..c4ea9cf 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -88,6 +88,8 @@
}
int capabilities = convertConnectionToCallCapabilities(call.getConnectionCapabilities());
int properties = convertConnectionToCallProperties(call.getConnectionProperties());
+ int supportedAudioRoutes = call.getSupportedAudioRoutes();
+
if (call.isConference()) {
properties |= android.telecom.Call.Details.PROPERTY_CONFERENCE;
}
@@ -157,6 +159,7 @@
call.getCannedSmsResponses(),
capabilities,
properties,
+ supportedAudioRoutes,
connectTimeMillis,
handle,
call.getHandlePresentation(),
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 143ea53..29f6253 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -35,6 +35,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.telecom.CallAudioState;
import android.telecom.ConnectionService;
import android.telecom.DefaultDialerManager;
import android.telecom.Log;
@@ -1508,6 +1509,7 @@
private static final String ADDRESS = "handle";
private static final String SUBSCRIPTION_ADDRESS = "subscription_number";
private static final String CAPABILITIES = "capabilities";
+ private static final String SUPPORTED_AUDIO_ROUTES = "supported_audio_routes";
private static final String ICON_RES_ID = "icon_res_id";
private static final String ICON_PACKAGE_NAME = "icon_package_name";
private static final String ICON_BITMAP = "icon_bitmap";
@@ -1543,6 +1545,8 @@
writeStringList(SUPPORTED_URI_SCHEMES, o.getSupportedUriSchemes(), serializer);
writeBundle(EXTRAS, o.getExtras(), serializer);
writeTextIfNonNull(ENABLED, o.isEnabled() ? "true" : "false" , serializer);
+ writeTextIfNonNull(SUPPORTED_AUDIO_ROUTES, Integer.toString(
+ o.getSupportedAudioRoutes()), serializer);
serializer.endTag(null, CLASS_PHONE_ACCOUNT);
}
@@ -1556,6 +1560,7 @@
Uri address = null;
Uri subscriptionAddress = null;
int capabilities = 0;
+ int supportedAudioRoutes = 0;
int iconResId = PhoneAccount.NO_RESOURCE_ID;
String iconPackageName = null;
Bitmap iconBitmap = null;
@@ -1614,6 +1619,9 @@
enabled = "true".equalsIgnoreCase(parser.getText());
} else if (parser.getName().equals(EXTRAS)) {
extras = readBundle(parser);
+ } else if (parser.getName().equals(SUPPORTED_AUDIO_ROUTES)) {
+ parser.next();
+ supportedAudioRoutes = Integer.parseInt(parser.getText());
}
}
@@ -1672,10 +1680,16 @@
}
}
+ if (version < 9) {
+ // Set supported audio routes to all by default
+ supportedAudioRoutes = CallAudioState.ROUTE_ALL;
+ }
+
PhoneAccount.Builder builder = PhoneAccount.builder(accountHandle, label)
.setAddress(address)
.setSubscriptionAddress(subscriptionAddress)
.setCapabilities(capabilities)
+ .setSupportedAudioRoutes(supportedAudioRoutes)
.setShortDescription(shortDescription)
.setSupportedUriSchemes(supportedUriSchemes)
.setHighlightColor(highlightColor)