Loading src/com/android/server/telecom/AudioRoute.java +15 −15 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.bluetooth.BluetoothStatusCodes; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.telecom.Log; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.server.telecom.bluetooth.BluetoothRouteManager; Loading Loading @@ -226,7 +227,7 @@ public class AudioRoute { AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_LE, bluetoothLeDeviceInfoTypes); } int getType() { public int getType() { return mAudioRouteType; } Loading @@ -237,7 +238,7 @@ public class AudioRoute { // Invoked when entered pending route whose dest route is this route void onDestRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute, BluetoothDevice device, AudioManager audioManager, BluetoothRouteManager bluetoothRouteManager) { BluetoothRouteManager bluetoothRouteManager, boolean isScoAudioConnected) { Log.i(this, "onDestRouteAsPendingRoute: active (%b), type (%d)", active, mAudioRouteType); if (pendingAudioRoute.isActive() && !active) { clearCommunicationDevice(pendingAudioRoute, bluetoothRouteManager, audioManager); Loading @@ -251,20 +252,19 @@ public class AudioRoute { // Check if the communication device was set for the device, even if // BluetoothHeadset#connectAudio reports that the SCO connection wasn't // successfully established. boolean scoConnected = audioManager.getCommunicationDevice().equals(mInfo); if (connectedBtAudio || scoConnected) { if (connectedBtAudio || isScoAudioConnected) { pendingAudioRoute.setCommunicationDeviceType(mAudioRouteType); if (!isScoAudioConnected) { pendingAudioRoute.addMessage(BT_AUDIO_CONNECTED, mBluetoothAddress); } if (connectedBtAudio) { pendingAudioRoute.addMessage(BT_AUDIO_CONNECTED); } else if (!scoConnected) { pendingAudioRoute.onMessageReceived( PENDING_ROUTE_FAILED, mBluetoothAddress); } else { pendingAudioRoute.onMessageReceived(new Pair<>(PENDING_ROUTE_FAILED, mBluetoothAddress), mBluetoothAddress); } return; } } else if (mAudioRouteType == TYPE_SPEAKER) { pendingAudioRoute.addMessage(SPEAKER_ON); pendingAudioRoute.addMessage(SPEAKER_ON, null); } boolean result = false; Loading @@ -291,7 +291,7 @@ public class AudioRoute { // before being able to successfully set the communication device. Refrain from sending // pending route failed message for BT route until the second attempt fails. if (!result && !BT_AUDIO_ROUTE_TYPES.contains(mAudioRouteType)) { pendingAudioRoute.onMessageReceived(PENDING_ROUTE_FAILED, null); pendingAudioRoute.onMessageReceived(new Pair<>(PENDING_ROUTE_FAILED, null), null); } } } Loading @@ -303,13 +303,13 @@ public class AudioRoute { Log.i(this, "onOrigRouteAsPendingRoute: active (%b), type (%d)", active, mAudioRouteType); if (active) { if (mAudioRouteType == TYPE_SPEAKER) { pendingAudioRoute.addMessage(SPEAKER_OFF); pendingAudioRoute.addMessage(SPEAKER_OFF, null); } int result = clearCommunicationDevice(pendingAudioRoute, bluetoothRouteManager, audioManager); // Only send BT_AUDIO_DISCONNECTED for SCO if disconnect was successful. if (mAudioRouteType == TYPE_BLUETOOTH_SCO && result == BluetoothStatusCodes.SUCCESS) { pendingAudioRoute.addMessage(BT_AUDIO_DISCONNECTED); pendingAudioRoute.addMessage(BT_AUDIO_DISCONNECTED, mBluetoothAddress); } } } Loading Loading @@ -370,7 +370,7 @@ public class AudioRoute { return success; } private int clearCommunicationDevice(PendingAudioRoute pendingAudioRoute, int clearCommunicationDevice(PendingAudioRoute pendingAudioRoute, BluetoothRouteManager bluetoothRouteManager, AudioManager audioManager) { // Try to see if there's a previously set device for communication that should be cleared. // This only serves to help in the SCO case to ensure that we disconnect the headset. Loading src/com/android/server/telecom/CallAudioRouteController.java +72 −24 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.telecom; import static com.android.server.telecom.AudioRoute.BT_AUDIO_ROUTE_TYPES; import static com.android.server.telecom.AudioRoute.TYPE_INVALID; import static com.android.server.telecom.AudioRoute.TYPE_SPEAKER; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; Loading @@ -40,7 +41,9 @@ import android.os.RemoteException; import android.telecom.CallAudioState; import android.telecom.Log; import android.telecom.Logging.Session; import android.telecom.VideoProfile; import android.util.ArrayMap; import android.util.Pair; import androidx.annotation.NonNull; Loading Loading @@ -94,7 +97,9 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { private StatusBarNotifier mStatusBarNotifier; private FeatureFlags mFeatureFlags; private int mFocusType; private boolean mIsScoAudioConnected; private final Object mLock = new Object(); private final TelecomSystem.SyncRoot mTelecomLock; private final BroadcastReceiver mSpeakerPhoneChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading @@ -105,7 +110,9 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { AudioDeviceInfo info = mAudioManager.getCommunicationDevice(); if ((info != null) && (info.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)) { if (mCurrentRoute.getType() != AudioRoute.TYPE_SPEAKER) { sendMessageWithSessionInfo(SPEAKER_ON); } } else { sendMessageWithSessionInfo(SPEAKER_OFF); } Loading Loading @@ -171,6 +178,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { mStatusBarNotifier = statusBarNotifier; mFeatureFlags = featureFlags; mFocusType = NO_FOCUS; mIsScoAudioConnected = false; mTelecomLock = callsManager.getLock(); HandlerThread handlerThread = new HandlerThread(this.getClass().getSimpleName()); handlerThread.start(); Loading Loading @@ -282,7 +291,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { handleMuteChanged(false); break; case MUTE_EXTERNALLY_CHANGED: handleMuteChanged(mAudioManager.isMasterMute()); handleMuteChanged(mAudioManager.isMicrophoneMute()); break; case SWITCH_FOCUS: focus = msg.arg1; Loading Loading @@ -455,11 +464,13 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { Log.i(this, "Override current pending route destination from %s(active=%b) to " + "%s(active=%b)", mPendingAudioRoute.getDestRoute(), mIsActive, destRoute, active); // Ensure we don't keep waiting for SPEAKER_ON if dest route gets overridden. if (active && mPendingAudioRoute.getDestRoute().getType() == TYPE_SPEAKER) { mPendingAudioRoute.clearPendingMessage(new Pair<>(SPEAKER_ON, null)); } // override pending route while keep waiting for still pending messages for the // previous pending route mPendingAudioRoute.setOrigRoute(mIsActive, mPendingAudioRoute.getDestRoute()); mPendingAudioRoute.setDestRoute(active, destRoute, mBluetoothRoutes.get(destRoute)); mIsActive = active; } else { if (mCurrentRoute.equals(destRoute) && (mIsActive == active)) { return; Loading @@ -473,10 +484,11 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { // Avoid waiting for pending messages for an unavailable route mPendingAudioRoute.setOrigRoute(mIsActive, DUMMY_ROUTE); } mPendingAudioRoute.setDestRoute(active, destRoute, mBluetoothRoutes.get(destRoute)); mIsActive = active; mIsPending = true; } mPendingAudioRoute.setDestRoute(active, destRoute, mBluetoothRoutes.get(destRoute), mIsScoAudioConnected); mIsActive = active; mPendingAudioRoute.evaluatePendingState(); postTimeoutMessage(); } Loading Loading @@ -599,7 +611,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { Log.i(this, "handleBtAudioActive: is pending path"); if (Objects.equals(mPendingAudioRoute.getDestRoute().getBluetoothAddress(), bluetoothDevice.getAddress())) { mPendingAudioRoute.onMessageReceived(BT_AUDIO_CONNECTED, null); mPendingAudioRoute.onMessageReceived(new Pair<>(BT_AUDIO_CONNECTED, bluetoothDevice.getAddress()), null); } } else { // ignore, not triggered by telecom Loading @@ -620,7 +633,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { Log.i(this, "handleBtAudioInactive: is pending path"); if (Objects.equals(mPendingAudioRoute.getOrigRoute().getBluetoothAddress(), bluetoothDevice.getAddress())) { mPendingAudioRoute.onMessageReceived(BT_AUDIO_DISCONNECTED, null); mPendingAudioRoute.onMessageReceived(new Pair<>(BT_AUDIO_DISCONNECTED, bluetoothDevice.getAddress()), null); } } else { // ignore, not triggered by telecom Loading Loading @@ -719,7 +733,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { private void handleMuteChanged(boolean mute) { mIsMute = mute; if (mIsMute != mAudioManager.isMasterMute() && mIsActive) { if (mIsMute != mAudioManager.isMicrophoneMute() && mIsActive) { IAudioService audioService = mAudioServiceFactory.getAudioService(); Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]", mute, audioService == null); Loading Loading @@ -752,11 +766,10 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { } } case ACTIVE_FOCUS -> { // Route to active baseline route, otherwise ignore if route is already active. if (!mIsActive) { // Route to active baseline route (we may need to change audio route in the case // when a video call is put on hold). routeTo(true, getBaseRoute(true, null)); } } case RINGING_FOCUS -> { if (!mIsActive) { AudioRoute route = getBaseRoute(true, null); Loading Loading @@ -836,7 +849,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { private void handleSpeakerOn() { if (isPending()) { Log.i(this, "handleSpeakerOn: sending SPEAKER_ON to pending audio route"); mPendingAudioRoute.onMessageReceived(SPEAKER_ON, null); mPendingAudioRoute.onMessageReceived(new Pair<>(SPEAKER_ON, null), null); // Update status bar notification if we are in a call. mStatusBarNotifier.notifySpeakerphone(mCallsManager.hasAnyCalls()); } else { Loading @@ -854,7 +867,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { private void handleSpeakerOff() { if (isPending()) { Log.i(this, "handleSpeakerOff - sending SPEAKER_OFF to pending audio route"); mPendingAudioRoute.onMessageReceived(SPEAKER_OFF, null); mPendingAudioRoute.onMessageReceived(new Pair<>(SPEAKER_OFF, null), null); // Update status bar notification mStatusBarNotifier.notifySpeakerphone(false); } else if (mCurrentRoute.getType() == AudioRoute.TYPE_SPEAKER) { Loading @@ -878,6 +891,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, "Entering audio route: " + mCurrentRoute + " (active=" + mIsActive + ")"); mIsPending = false; mPendingAudioRoute.clearPendingMessages(); onCurrentRouteChanged(); } } Loading Loading @@ -909,7 +923,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { BluetoothDevice deviceToAdd = mBluetoothRoutes.get(route); // Only include the lead device for LE audio (otherwise, the routes will show // two separate devices in the UI). if (route.getType() == AudioRoute.TYPE_BLUETOOTH_LE) { if (route.getType() == AudioRoute.TYPE_BLUETOOTH_LE && getLeAudioService() != null) { int groupId = getLeAudioService().getGroupId(deviceToAdd); if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) { deviceToAdd = getLeAudioService().getConnectedGroupLeadDevice(groupId); Loading Loading @@ -988,6 +1003,12 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { private AudioRoute getPreferredAudioRouteFromDefault(boolean includeBluetooth, String btAddressToExclude) { boolean skipEarpiece; Call foregroundCall = mCallAudioManager.getForegroundCall(); synchronized (mTelecomLock) { skipEarpiece = foregroundCall != null && VideoProfile.isVideo(foregroundCall.getVideoState()); } // Route to earpiece, wired, or speaker route if there are not bluetooth routes or if there // are only wearables available. AudioRoute activeWatchOrNonWatchDeviceRoute = Loading @@ -996,7 +1017,17 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { || activeWatchOrNonWatchDeviceRoute == null) { Log.i(this, "getPreferredAudioRouteFromDefault: Audio routing defaulting to " + "available non-BT route."); return mEarpieceWiredRoute != null ? mEarpieceWiredRoute : mSpeakerDockRoute; AudioRoute defaultRoute = mEarpieceWiredRoute != null ? mEarpieceWiredRoute : mSpeakerDockRoute; // Ensure that we default to speaker route if we're in a video call, but disregard it if // a wired headset is plugged in. if (skipEarpiece && defaultRoute.getType() == AudioRoute.TYPE_EARPIECE) { Log.i(this, "getPreferredAudioRouteFromDefault: Audio routing defaulting to " + "speaker route for video call."); defaultRoute = mSpeakerDockRoute; } return defaultRoute; } else { // Most recent active route will always be the last in the array (ensure that we don't // auto route to a wearable device unless it's already active). Loading Loading @@ -1042,7 +1073,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { return mCurrentRoute; } private AudioRoute getBluetoothRoute(@AudioRoute.AudioRouteType int audioRouteType, public AudioRoute getBluetoothRoute(@AudioRoute.AudioRouteType int audioRouteType, String address) { for (AudioRoute route : mBluetoothRoutes.keySet()) { if (route.getType() == audioRouteType && route.getBluetoothAddress().equals(address)) { Loading Loading @@ -1129,7 +1160,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { BluetoothDevice device = mBluetoothRoutes.get(route); // Skip excluded BT address and LE audio if it's not the lead device. if (route.getBluetoothAddress().equals(btAddressToExclude) || isLeAudioNonLeadDevice(route.getType(), device)) { || isLeAudioNonLeadDeviceOrServiceUnavailable(route.getType(), device)) { continue; } // Check if the most recently active device is a watch device. Loading Loading @@ -1158,7 +1189,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { for (int i = bluetoothRoutes.size() - 1; i >= 0; i--) { AudioRoute route = bluetoothRoutes.get(i); // Skip LE route if it's not the lead device. if (isLeAudioNonLeadDevice(route.getType(), mBluetoothRoutes.get(route))) { if (isLeAudioNonLeadDeviceOrServiceUnavailable( route.getType(), mBluetoothRoutes.get(route))) { continue; } if (!route.getBluetoothAddress().equals(btAddressToExclude)) { Loading @@ -1168,15 +1200,19 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { return null; } private boolean isLeAudioNonLeadDevice(@AudioRoute.AudioRouteType int type, private boolean isLeAudioNonLeadDeviceOrServiceUnavailable(@AudioRoute.AudioRouteType int type, BluetoothDevice device) { if (type != AudioRoute.TYPE_BLUETOOTH_LE) { return false; } else if (getLeAudioService() == null) { return true; } int groupId = getLeAudioService().getGroupId(device); if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) { return !device.getAddress().equals( getLeAudioService().getConnectedGroupLeadDevice(groupId).getAddress()); BluetoothDevice leadDevice = getLeAudioService().getConnectedGroupLeadDevice(groupId); Log.i(this, "Lead device for device (%s) is %s.", device, leadDevice); return leadDevice == null || !device.getAddress().equals(leadDevice.getAddress()); } return false; } Loading @@ -1195,6 +1231,18 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { mAudioRouteFactory = audioRouteFactory; } public Map<AudioRoute, BluetoothDevice> getBluetoothRoutes() { return mBluetoothRoutes; } public void overrideIsPending(boolean isPending) { mIsPending = isPending; } public void setIsScoAudioConnected(boolean value) { mIsScoAudioConnected = value; } @VisibleForTesting public void setActive(boolean active) { if (active) { Loading src/com/android/server/telecom/CallsManager.java +1 −0 Original line number Diff line number Diff line Loading @@ -659,6 +659,7 @@ public class CallsManager extends Call.ListenerBase } callAudioRouteAdapter.initialize(); bluetoothStateReceiver.setCallAudioRouteAdapter(callAudioRouteAdapter); bluetoothDeviceManager.setCallAudioRouteAdapter(callAudioRouteAdapter); CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter = new CallAudioRoutePeripheralAdapter( Loading src/com/android/server/telecom/PendingAudioRoute.java +23 −14 Original line number Diff line number Diff line Loading @@ -22,10 +22,12 @@ import static com.android.server.telecom.CallAudioRouteAdapter.SWITCH_BASELINE_R import android.bluetooth.BluetoothDevice; import android.media.AudioManager; import android.telecom.Log; import android.util.ArraySet; import android.util.Pair; import com.android.server.telecom.bluetooth.BluetoothRouteManager; import java.util.ArrayList; import java.util.Set; /** * Used to represent the intermediate state during audio route switching. Loading @@ -47,7 +49,7 @@ public class PendingAudioRoute { * by new switching request during the ongoing switching */ private AudioRoute mDestRoute; private ArrayList<Integer> mPendingMessages; private Set<Pair<Integer, String>> mPendingMessages; private boolean mActive; /** * The device that has been set for communication by Telecom Loading @@ -59,7 +61,7 @@ public class PendingAudioRoute { mCallAudioRouteController = controller; mAudioManager = audioManager; mBluetoothRouteManager = bluetoothRouteManager; mPendingMessages = new ArrayList<>(); mPendingMessages = new ArraySet<>(); mActive = false; mCommunicationDeviceType = AudioRoute.TYPE_INVALID; } Loading @@ -73,24 +75,25 @@ public class PendingAudioRoute { return mOrigRoute; } void setDestRoute(boolean active, AudioRoute destRoute, BluetoothDevice device) { void setDestRoute(boolean active, AudioRoute destRoute, BluetoothDevice device, boolean isScoAudioConnected) { destRoute.onDestRouteAsPendingRoute(active, this, device, mAudioManager, mBluetoothRouteManager); mAudioManager, mBluetoothRouteManager, isScoAudioConnected); mActive = active; mDestRoute = destRoute; } AudioRoute getDestRoute() { public AudioRoute getDestRoute() { return mDestRoute; } public void addMessage(int message) { mPendingMessages.add(message); public void addMessage(int message, String bluetoothDevice) { mPendingMessages.add(new Pair<>(message, bluetoothDevice)); } public void onMessageReceived(int message, String btAddressToExclude) { public void onMessageReceived(Pair<Integer, String> message, String btAddressToExclude) { Log.i(this, "onMessageReceived: message - %s", message); if (message == PENDING_ROUTE_FAILED) { if (message.first == PENDING_ROUTE_FAILED) { // Fallback to base route mCallAudioRouteController.sendMessageWithSessionInfo( SWITCH_BASELINE_ROUTE, 0, btAddressToExclude); Loading @@ -98,7 +101,7 @@ public class PendingAudioRoute { } // Removes the first occurrence of the specified message from this list, if it is present. mPendingMessages.remove((Object) message); mPendingMessages.remove(message); evaluatePendingState(); } Loading @@ -107,9 +110,7 @@ public class PendingAudioRoute { mCallAudioRouteController.sendMessageWithSessionInfo( CallAudioRouteAdapter.EXIT_PENDING_ROUTE); } else { for(Integer i: mPendingMessages) { Log.d(this, "evaluatePendingState: pending Messages - %d", i); } Log.i(this, "evaluatePendingState: mPendingMessages - %s", mPendingMessages); } } Loading @@ -117,6 +118,10 @@ public class PendingAudioRoute { mPendingMessages.clear(); } public void clearPendingMessage(Pair<Integer, String> message) { mPendingMessages.remove(message); } public boolean isActive() { return mActive; } Loading @@ -129,4 +134,8 @@ public class PendingAudioRoute { @AudioRoute.AudioRouteType int communicationDeviceType) { mCommunicationDeviceType = communicationDeviceType; } public void overrideDestRoute(AudioRoute route) { mDestRoute = route; } } src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java +59 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
src/com/android/server/telecom/AudioRoute.java +15 −15 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.bluetooth.BluetoothStatusCodes; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.telecom.Log; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.server.telecom.bluetooth.BluetoothRouteManager; Loading Loading @@ -226,7 +227,7 @@ public class AudioRoute { AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_LE, bluetoothLeDeviceInfoTypes); } int getType() { public int getType() { return mAudioRouteType; } Loading @@ -237,7 +238,7 @@ public class AudioRoute { // Invoked when entered pending route whose dest route is this route void onDestRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute, BluetoothDevice device, AudioManager audioManager, BluetoothRouteManager bluetoothRouteManager) { BluetoothRouteManager bluetoothRouteManager, boolean isScoAudioConnected) { Log.i(this, "onDestRouteAsPendingRoute: active (%b), type (%d)", active, mAudioRouteType); if (pendingAudioRoute.isActive() && !active) { clearCommunicationDevice(pendingAudioRoute, bluetoothRouteManager, audioManager); Loading @@ -251,20 +252,19 @@ public class AudioRoute { // Check if the communication device was set for the device, even if // BluetoothHeadset#connectAudio reports that the SCO connection wasn't // successfully established. boolean scoConnected = audioManager.getCommunicationDevice().equals(mInfo); if (connectedBtAudio || scoConnected) { if (connectedBtAudio || isScoAudioConnected) { pendingAudioRoute.setCommunicationDeviceType(mAudioRouteType); if (!isScoAudioConnected) { pendingAudioRoute.addMessage(BT_AUDIO_CONNECTED, mBluetoothAddress); } if (connectedBtAudio) { pendingAudioRoute.addMessage(BT_AUDIO_CONNECTED); } else if (!scoConnected) { pendingAudioRoute.onMessageReceived( PENDING_ROUTE_FAILED, mBluetoothAddress); } else { pendingAudioRoute.onMessageReceived(new Pair<>(PENDING_ROUTE_FAILED, mBluetoothAddress), mBluetoothAddress); } return; } } else if (mAudioRouteType == TYPE_SPEAKER) { pendingAudioRoute.addMessage(SPEAKER_ON); pendingAudioRoute.addMessage(SPEAKER_ON, null); } boolean result = false; Loading @@ -291,7 +291,7 @@ public class AudioRoute { // before being able to successfully set the communication device. Refrain from sending // pending route failed message for BT route until the second attempt fails. if (!result && !BT_AUDIO_ROUTE_TYPES.contains(mAudioRouteType)) { pendingAudioRoute.onMessageReceived(PENDING_ROUTE_FAILED, null); pendingAudioRoute.onMessageReceived(new Pair<>(PENDING_ROUTE_FAILED, null), null); } } } Loading @@ -303,13 +303,13 @@ public class AudioRoute { Log.i(this, "onOrigRouteAsPendingRoute: active (%b), type (%d)", active, mAudioRouteType); if (active) { if (mAudioRouteType == TYPE_SPEAKER) { pendingAudioRoute.addMessage(SPEAKER_OFF); pendingAudioRoute.addMessage(SPEAKER_OFF, null); } int result = clearCommunicationDevice(pendingAudioRoute, bluetoothRouteManager, audioManager); // Only send BT_AUDIO_DISCONNECTED for SCO if disconnect was successful. if (mAudioRouteType == TYPE_BLUETOOTH_SCO && result == BluetoothStatusCodes.SUCCESS) { pendingAudioRoute.addMessage(BT_AUDIO_DISCONNECTED); pendingAudioRoute.addMessage(BT_AUDIO_DISCONNECTED, mBluetoothAddress); } } } Loading Loading @@ -370,7 +370,7 @@ public class AudioRoute { return success; } private int clearCommunicationDevice(PendingAudioRoute pendingAudioRoute, int clearCommunicationDevice(PendingAudioRoute pendingAudioRoute, BluetoothRouteManager bluetoothRouteManager, AudioManager audioManager) { // Try to see if there's a previously set device for communication that should be cleared. // This only serves to help in the SCO case to ensure that we disconnect the headset. Loading
src/com/android/server/telecom/CallAudioRouteController.java +72 −24 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.telecom; import static com.android.server.telecom.AudioRoute.BT_AUDIO_ROUTE_TYPES; import static com.android.server.telecom.AudioRoute.TYPE_INVALID; import static com.android.server.telecom.AudioRoute.TYPE_SPEAKER; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; Loading @@ -40,7 +41,9 @@ import android.os.RemoteException; import android.telecom.CallAudioState; import android.telecom.Log; import android.telecom.Logging.Session; import android.telecom.VideoProfile; import android.util.ArrayMap; import android.util.Pair; import androidx.annotation.NonNull; Loading Loading @@ -94,7 +97,9 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { private StatusBarNotifier mStatusBarNotifier; private FeatureFlags mFeatureFlags; private int mFocusType; private boolean mIsScoAudioConnected; private final Object mLock = new Object(); private final TelecomSystem.SyncRoot mTelecomLock; private final BroadcastReceiver mSpeakerPhoneChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading @@ -105,7 +110,9 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { AudioDeviceInfo info = mAudioManager.getCommunicationDevice(); if ((info != null) && (info.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)) { if (mCurrentRoute.getType() != AudioRoute.TYPE_SPEAKER) { sendMessageWithSessionInfo(SPEAKER_ON); } } else { sendMessageWithSessionInfo(SPEAKER_OFF); } Loading Loading @@ -171,6 +178,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { mStatusBarNotifier = statusBarNotifier; mFeatureFlags = featureFlags; mFocusType = NO_FOCUS; mIsScoAudioConnected = false; mTelecomLock = callsManager.getLock(); HandlerThread handlerThread = new HandlerThread(this.getClass().getSimpleName()); handlerThread.start(); Loading Loading @@ -282,7 +291,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { handleMuteChanged(false); break; case MUTE_EXTERNALLY_CHANGED: handleMuteChanged(mAudioManager.isMasterMute()); handleMuteChanged(mAudioManager.isMicrophoneMute()); break; case SWITCH_FOCUS: focus = msg.arg1; Loading Loading @@ -455,11 +464,13 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { Log.i(this, "Override current pending route destination from %s(active=%b) to " + "%s(active=%b)", mPendingAudioRoute.getDestRoute(), mIsActive, destRoute, active); // Ensure we don't keep waiting for SPEAKER_ON if dest route gets overridden. if (active && mPendingAudioRoute.getDestRoute().getType() == TYPE_SPEAKER) { mPendingAudioRoute.clearPendingMessage(new Pair<>(SPEAKER_ON, null)); } // override pending route while keep waiting for still pending messages for the // previous pending route mPendingAudioRoute.setOrigRoute(mIsActive, mPendingAudioRoute.getDestRoute()); mPendingAudioRoute.setDestRoute(active, destRoute, mBluetoothRoutes.get(destRoute)); mIsActive = active; } else { if (mCurrentRoute.equals(destRoute) && (mIsActive == active)) { return; Loading @@ -473,10 +484,11 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { // Avoid waiting for pending messages for an unavailable route mPendingAudioRoute.setOrigRoute(mIsActive, DUMMY_ROUTE); } mPendingAudioRoute.setDestRoute(active, destRoute, mBluetoothRoutes.get(destRoute)); mIsActive = active; mIsPending = true; } mPendingAudioRoute.setDestRoute(active, destRoute, mBluetoothRoutes.get(destRoute), mIsScoAudioConnected); mIsActive = active; mPendingAudioRoute.evaluatePendingState(); postTimeoutMessage(); } Loading Loading @@ -599,7 +611,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { Log.i(this, "handleBtAudioActive: is pending path"); if (Objects.equals(mPendingAudioRoute.getDestRoute().getBluetoothAddress(), bluetoothDevice.getAddress())) { mPendingAudioRoute.onMessageReceived(BT_AUDIO_CONNECTED, null); mPendingAudioRoute.onMessageReceived(new Pair<>(BT_AUDIO_CONNECTED, bluetoothDevice.getAddress()), null); } } else { // ignore, not triggered by telecom Loading @@ -620,7 +633,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { Log.i(this, "handleBtAudioInactive: is pending path"); if (Objects.equals(mPendingAudioRoute.getOrigRoute().getBluetoothAddress(), bluetoothDevice.getAddress())) { mPendingAudioRoute.onMessageReceived(BT_AUDIO_DISCONNECTED, null); mPendingAudioRoute.onMessageReceived(new Pair<>(BT_AUDIO_DISCONNECTED, bluetoothDevice.getAddress()), null); } } else { // ignore, not triggered by telecom Loading Loading @@ -719,7 +733,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { private void handleMuteChanged(boolean mute) { mIsMute = mute; if (mIsMute != mAudioManager.isMasterMute() && mIsActive) { if (mIsMute != mAudioManager.isMicrophoneMute() && mIsActive) { IAudioService audioService = mAudioServiceFactory.getAudioService(); Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]", mute, audioService == null); Loading Loading @@ -752,11 +766,10 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { } } case ACTIVE_FOCUS -> { // Route to active baseline route, otherwise ignore if route is already active. if (!mIsActive) { // Route to active baseline route (we may need to change audio route in the case // when a video call is put on hold). routeTo(true, getBaseRoute(true, null)); } } case RINGING_FOCUS -> { if (!mIsActive) { AudioRoute route = getBaseRoute(true, null); Loading Loading @@ -836,7 +849,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { private void handleSpeakerOn() { if (isPending()) { Log.i(this, "handleSpeakerOn: sending SPEAKER_ON to pending audio route"); mPendingAudioRoute.onMessageReceived(SPEAKER_ON, null); mPendingAudioRoute.onMessageReceived(new Pair<>(SPEAKER_ON, null), null); // Update status bar notification if we are in a call. mStatusBarNotifier.notifySpeakerphone(mCallsManager.hasAnyCalls()); } else { Loading @@ -854,7 +867,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { private void handleSpeakerOff() { if (isPending()) { Log.i(this, "handleSpeakerOff - sending SPEAKER_OFF to pending audio route"); mPendingAudioRoute.onMessageReceived(SPEAKER_OFF, null); mPendingAudioRoute.onMessageReceived(new Pair<>(SPEAKER_OFF, null), null); // Update status bar notification mStatusBarNotifier.notifySpeakerphone(false); } else if (mCurrentRoute.getType() == AudioRoute.TYPE_SPEAKER) { Loading @@ -878,6 +891,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, "Entering audio route: " + mCurrentRoute + " (active=" + mIsActive + ")"); mIsPending = false; mPendingAudioRoute.clearPendingMessages(); onCurrentRouteChanged(); } } Loading Loading @@ -909,7 +923,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { BluetoothDevice deviceToAdd = mBluetoothRoutes.get(route); // Only include the lead device for LE audio (otherwise, the routes will show // two separate devices in the UI). if (route.getType() == AudioRoute.TYPE_BLUETOOTH_LE) { if (route.getType() == AudioRoute.TYPE_BLUETOOTH_LE && getLeAudioService() != null) { int groupId = getLeAudioService().getGroupId(deviceToAdd); if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) { deviceToAdd = getLeAudioService().getConnectedGroupLeadDevice(groupId); Loading Loading @@ -988,6 +1003,12 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { private AudioRoute getPreferredAudioRouteFromDefault(boolean includeBluetooth, String btAddressToExclude) { boolean skipEarpiece; Call foregroundCall = mCallAudioManager.getForegroundCall(); synchronized (mTelecomLock) { skipEarpiece = foregroundCall != null && VideoProfile.isVideo(foregroundCall.getVideoState()); } // Route to earpiece, wired, or speaker route if there are not bluetooth routes or if there // are only wearables available. AudioRoute activeWatchOrNonWatchDeviceRoute = Loading @@ -996,7 +1017,17 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { || activeWatchOrNonWatchDeviceRoute == null) { Log.i(this, "getPreferredAudioRouteFromDefault: Audio routing defaulting to " + "available non-BT route."); return mEarpieceWiredRoute != null ? mEarpieceWiredRoute : mSpeakerDockRoute; AudioRoute defaultRoute = mEarpieceWiredRoute != null ? mEarpieceWiredRoute : mSpeakerDockRoute; // Ensure that we default to speaker route if we're in a video call, but disregard it if // a wired headset is plugged in. if (skipEarpiece && defaultRoute.getType() == AudioRoute.TYPE_EARPIECE) { Log.i(this, "getPreferredAudioRouteFromDefault: Audio routing defaulting to " + "speaker route for video call."); defaultRoute = mSpeakerDockRoute; } return defaultRoute; } else { // Most recent active route will always be the last in the array (ensure that we don't // auto route to a wearable device unless it's already active). Loading Loading @@ -1042,7 +1073,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { return mCurrentRoute; } private AudioRoute getBluetoothRoute(@AudioRoute.AudioRouteType int audioRouteType, public AudioRoute getBluetoothRoute(@AudioRoute.AudioRouteType int audioRouteType, String address) { for (AudioRoute route : mBluetoothRoutes.keySet()) { if (route.getType() == audioRouteType && route.getBluetoothAddress().equals(address)) { Loading Loading @@ -1129,7 +1160,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { BluetoothDevice device = mBluetoothRoutes.get(route); // Skip excluded BT address and LE audio if it's not the lead device. if (route.getBluetoothAddress().equals(btAddressToExclude) || isLeAudioNonLeadDevice(route.getType(), device)) { || isLeAudioNonLeadDeviceOrServiceUnavailable(route.getType(), device)) { continue; } // Check if the most recently active device is a watch device. Loading Loading @@ -1158,7 +1189,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { for (int i = bluetoothRoutes.size() - 1; i >= 0; i--) { AudioRoute route = bluetoothRoutes.get(i); // Skip LE route if it's not the lead device. if (isLeAudioNonLeadDevice(route.getType(), mBluetoothRoutes.get(route))) { if (isLeAudioNonLeadDeviceOrServiceUnavailable( route.getType(), mBluetoothRoutes.get(route))) { continue; } if (!route.getBluetoothAddress().equals(btAddressToExclude)) { Loading @@ -1168,15 +1200,19 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { return null; } private boolean isLeAudioNonLeadDevice(@AudioRoute.AudioRouteType int type, private boolean isLeAudioNonLeadDeviceOrServiceUnavailable(@AudioRoute.AudioRouteType int type, BluetoothDevice device) { if (type != AudioRoute.TYPE_BLUETOOTH_LE) { return false; } else if (getLeAudioService() == null) { return true; } int groupId = getLeAudioService().getGroupId(device); if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) { return !device.getAddress().equals( getLeAudioService().getConnectedGroupLeadDevice(groupId).getAddress()); BluetoothDevice leadDevice = getLeAudioService().getConnectedGroupLeadDevice(groupId); Log.i(this, "Lead device for device (%s) is %s.", device, leadDevice); return leadDevice == null || !device.getAddress().equals(leadDevice.getAddress()); } return false; } Loading @@ -1195,6 +1231,18 @@ public class CallAudioRouteController implements CallAudioRouteAdapter { mAudioRouteFactory = audioRouteFactory; } public Map<AudioRoute, BluetoothDevice> getBluetoothRoutes() { return mBluetoothRoutes; } public void overrideIsPending(boolean isPending) { mIsPending = isPending; } public void setIsScoAudioConnected(boolean value) { mIsScoAudioConnected = value; } @VisibleForTesting public void setActive(boolean active) { if (active) { Loading
src/com/android/server/telecom/CallsManager.java +1 −0 Original line number Diff line number Diff line Loading @@ -659,6 +659,7 @@ public class CallsManager extends Call.ListenerBase } callAudioRouteAdapter.initialize(); bluetoothStateReceiver.setCallAudioRouteAdapter(callAudioRouteAdapter); bluetoothDeviceManager.setCallAudioRouteAdapter(callAudioRouteAdapter); CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter = new CallAudioRoutePeripheralAdapter( Loading
src/com/android/server/telecom/PendingAudioRoute.java +23 −14 Original line number Diff line number Diff line Loading @@ -22,10 +22,12 @@ import static com.android.server.telecom.CallAudioRouteAdapter.SWITCH_BASELINE_R import android.bluetooth.BluetoothDevice; import android.media.AudioManager; import android.telecom.Log; import android.util.ArraySet; import android.util.Pair; import com.android.server.telecom.bluetooth.BluetoothRouteManager; import java.util.ArrayList; import java.util.Set; /** * Used to represent the intermediate state during audio route switching. Loading @@ -47,7 +49,7 @@ public class PendingAudioRoute { * by new switching request during the ongoing switching */ private AudioRoute mDestRoute; private ArrayList<Integer> mPendingMessages; private Set<Pair<Integer, String>> mPendingMessages; private boolean mActive; /** * The device that has been set for communication by Telecom Loading @@ -59,7 +61,7 @@ public class PendingAudioRoute { mCallAudioRouteController = controller; mAudioManager = audioManager; mBluetoothRouteManager = bluetoothRouteManager; mPendingMessages = new ArrayList<>(); mPendingMessages = new ArraySet<>(); mActive = false; mCommunicationDeviceType = AudioRoute.TYPE_INVALID; } Loading @@ -73,24 +75,25 @@ public class PendingAudioRoute { return mOrigRoute; } void setDestRoute(boolean active, AudioRoute destRoute, BluetoothDevice device) { void setDestRoute(boolean active, AudioRoute destRoute, BluetoothDevice device, boolean isScoAudioConnected) { destRoute.onDestRouteAsPendingRoute(active, this, device, mAudioManager, mBluetoothRouteManager); mAudioManager, mBluetoothRouteManager, isScoAudioConnected); mActive = active; mDestRoute = destRoute; } AudioRoute getDestRoute() { public AudioRoute getDestRoute() { return mDestRoute; } public void addMessage(int message) { mPendingMessages.add(message); public void addMessage(int message, String bluetoothDevice) { mPendingMessages.add(new Pair<>(message, bluetoothDevice)); } public void onMessageReceived(int message, String btAddressToExclude) { public void onMessageReceived(Pair<Integer, String> message, String btAddressToExclude) { Log.i(this, "onMessageReceived: message - %s", message); if (message == PENDING_ROUTE_FAILED) { if (message.first == PENDING_ROUTE_FAILED) { // Fallback to base route mCallAudioRouteController.sendMessageWithSessionInfo( SWITCH_BASELINE_ROUTE, 0, btAddressToExclude); Loading @@ -98,7 +101,7 @@ public class PendingAudioRoute { } // Removes the first occurrence of the specified message from this list, if it is present. mPendingMessages.remove((Object) message); mPendingMessages.remove(message); evaluatePendingState(); } Loading @@ -107,9 +110,7 @@ public class PendingAudioRoute { mCallAudioRouteController.sendMessageWithSessionInfo( CallAudioRouteAdapter.EXIT_PENDING_ROUTE); } else { for(Integer i: mPendingMessages) { Log.d(this, "evaluatePendingState: pending Messages - %d", i); } Log.i(this, "evaluatePendingState: mPendingMessages - %s", mPendingMessages); } } Loading @@ -117,6 +118,10 @@ public class PendingAudioRoute { mPendingMessages.clear(); } public void clearPendingMessage(Pair<Integer, String> message) { mPendingMessages.remove(message); } public boolean isActive() { return mActive; } Loading @@ -129,4 +134,8 @@ public class PendingAudioRoute { @AudioRoute.AudioRouteType int communicationDeviceType) { mCommunicationDeviceType = communicationDeviceType; } public void overrideDestRoute(AudioRoute route) { mDestRoute = route; } }
src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java +59 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes