Loading services/core/java/com/android/server/media/AudioManagerRouteController.java +40 −17 Original line number Diff line number Diff line Loading @@ -204,23 +204,24 @@ import java.util.Objects; Slog.w(TAG, "transferTo: Ignoring transfer request to unknown route id : " + routeId); return; } // TODO: b/329929065 - Push audio manager and bluetooth operations to the handler, so that // they don't run on a binder thread, so as to prevent possible deadlocks (these operations // may need system_server binder threads to complete). if (mediaRoute2InfoHolder.mCorrespondsToInactiveBluetoothRoute) { // By default, the last connected device is the active route so we don't need to apply a // routing audio policy. mBluetoothRouteController.activateBluetoothDeviceWithAddress( mediaRoute2InfoHolder.mMediaRoute2Info.getAddress()); mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia); } else { AudioDeviceAttributes attr = new AudioDeviceAttributes( AudioDeviceAttributes.ROLE_OUTPUT, mediaRoute2InfoHolder.mAudioDeviceInfoType, /* address= */ ""); // This is not a BT device, hence no address needed. mAudioManager.setPreferredDeviceForStrategy(mStrategyForMedia, attr); Runnable transferAction = getTransferActionForRoute(mediaRoute2InfoHolder); Runnable guardedTransferAction = () -> { try { transferAction.run(); } catch (Throwable throwable) { // We swallow the exception to avoid crashing system_server, since this // doesn't run on a binder thread. Slog.e( TAG, "Unexpected exception while transferring to route id: " + routeId, throwable); mHandler.post(this::rebuildAvailableRoutesAndNotify); } }; // We post the transfer operation to the handler to avoid making these calls on a binder // thread. See class javadoc for details. mHandler.post(guardedTransferAction); } @RequiresPermission( Loading @@ -236,6 +237,28 @@ import java.util.Objects; return true; } private Runnable getTransferActionForRoute(MediaRoute2InfoHolder mediaRoute2InfoHolder) { if (mediaRoute2InfoHolder.mCorrespondsToInactiveBluetoothRoute) { String deviceAddress = mediaRoute2InfoHolder.mMediaRoute2Info.getAddress(); return () -> { // By default, the last connected device is the active route so we don't // need to apply a routing audio policy. mBluetoothRouteController.activateBluetoothDeviceWithAddress(deviceAddress); mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia); }; } else { AudioDeviceAttributes deviceAttributes = new AudioDeviceAttributes( AudioDeviceAttributes.ROLE_OUTPUT, mediaRoute2InfoHolder.mAudioDeviceInfoType, /* address= */ ""); // This is not a BT device, hence no address needed. return () -> mAudioManager.setPreferredDeviceForStrategy( mStrategyForMedia, deviceAttributes); } } @RequiresPermission( anyOf = { Manifest.permission.MODIFY_AUDIO_ROUTING, Loading services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java +9 −2 Original line number Diff line number Diff line Loading @@ -69,6 +69,13 @@ import java.util.Set; public class AudioManagerRouteControllerTest { private static final String FAKE_ROUTE_NAME = "fake name"; /** * The number of milliseconds to wait for an asynchronous operation before failing an associated * assertion. */ private static final int ASYNC_CALL_TIMEOUTS_MS = 1000; private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER = createAudioDeviceInfo( AudioSystem.DEVICE_OUT_SPEAKER, "name_builtin", /* address= */ null); Loading Loading @@ -231,7 +238,7 @@ public class AudioManagerRouteControllerTest { MediaRoute2Info builtInSpeakerRoute = getAvailableRouteWithType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); mControllerUnderTest.transferTo(builtInSpeakerRoute.getId()); verify(mMockAudioManager) verify(mMockAudioManager, Mockito.timeout(ASYNC_CALL_TIMEOUTS_MS)) .setPreferredDeviceForStrategy( mMediaAudioProductStrategy, createAudioDeviceAttribute(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)); Loading @@ -239,7 +246,7 @@ public class AudioManagerRouteControllerTest { MediaRoute2Info wiredHeadsetRoute = getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET); mControllerUnderTest.transferTo(wiredHeadsetRoute.getId()); verify(mMockAudioManager) verify(mMockAudioManager, Mockito.timeout(ASYNC_CALL_TIMEOUTS_MS)) .setPreferredDeviceForStrategy( mMediaAudioProductStrategy, createAudioDeviceAttribute(AudioDeviceInfo.TYPE_WIRED_HEADSET)); Loading Loading
services/core/java/com/android/server/media/AudioManagerRouteController.java +40 −17 Original line number Diff line number Diff line Loading @@ -204,23 +204,24 @@ import java.util.Objects; Slog.w(TAG, "transferTo: Ignoring transfer request to unknown route id : " + routeId); return; } // TODO: b/329929065 - Push audio manager and bluetooth operations to the handler, so that // they don't run on a binder thread, so as to prevent possible deadlocks (these operations // may need system_server binder threads to complete). if (mediaRoute2InfoHolder.mCorrespondsToInactiveBluetoothRoute) { // By default, the last connected device is the active route so we don't need to apply a // routing audio policy. mBluetoothRouteController.activateBluetoothDeviceWithAddress( mediaRoute2InfoHolder.mMediaRoute2Info.getAddress()); mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia); } else { AudioDeviceAttributes attr = new AudioDeviceAttributes( AudioDeviceAttributes.ROLE_OUTPUT, mediaRoute2InfoHolder.mAudioDeviceInfoType, /* address= */ ""); // This is not a BT device, hence no address needed. mAudioManager.setPreferredDeviceForStrategy(mStrategyForMedia, attr); Runnable transferAction = getTransferActionForRoute(mediaRoute2InfoHolder); Runnable guardedTransferAction = () -> { try { transferAction.run(); } catch (Throwable throwable) { // We swallow the exception to avoid crashing system_server, since this // doesn't run on a binder thread. Slog.e( TAG, "Unexpected exception while transferring to route id: " + routeId, throwable); mHandler.post(this::rebuildAvailableRoutesAndNotify); } }; // We post the transfer operation to the handler to avoid making these calls on a binder // thread. See class javadoc for details. mHandler.post(guardedTransferAction); } @RequiresPermission( Loading @@ -236,6 +237,28 @@ import java.util.Objects; return true; } private Runnable getTransferActionForRoute(MediaRoute2InfoHolder mediaRoute2InfoHolder) { if (mediaRoute2InfoHolder.mCorrespondsToInactiveBluetoothRoute) { String deviceAddress = mediaRoute2InfoHolder.mMediaRoute2Info.getAddress(); return () -> { // By default, the last connected device is the active route so we don't // need to apply a routing audio policy. mBluetoothRouteController.activateBluetoothDeviceWithAddress(deviceAddress); mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia); }; } else { AudioDeviceAttributes deviceAttributes = new AudioDeviceAttributes( AudioDeviceAttributes.ROLE_OUTPUT, mediaRoute2InfoHolder.mAudioDeviceInfoType, /* address= */ ""); // This is not a BT device, hence no address needed. return () -> mAudioManager.setPreferredDeviceForStrategy( mStrategyForMedia, deviceAttributes); } } @RequiresPermission( anyOf = { Manifest.permission.MODIFY_AUDIO_ROUTING, Loading
services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java +9 −2 Original line number Diff line number Diff line Loading @@ -69,6 +69,13 @@ import java.util.Set; public class AudioManagerRouteControllerTest { private static final String FAKE_ROUTE_NAME = "fake name"; /** * The number of milliseconds to wait for an asynchronous operation before failing an associated * assertion. */ private static final int ASYNC_CALL_TIMEOUTS_MS = 1000; private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER = createAudioDeviceInfo( AudioSystem.DEVICE_OUT_SPEAKER, "name_builtin", /* address= */ null); Loading Loading @@ -231,7 +238,7 @@ public class AudioManagerRouteControllerTest { MediaRoute2Info builtInSpeakerRoute = getAvailableRouteWithType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); mControllerUnderTest.transferTo(builtInSpeakerRoute.getId()); verify(mMockAudioManager) verify(mMockAudioManager, Mockito.timeout(ASYNC_CALL_TIMEOUTS_MS)) .setPreferredDeviceForStrategy( mMediaAudioProductStrategy, createAudioDeviceAttribute(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)); Loading @@ -239,7 +246,7 @@ public class AudioManagerRouteControllerTest { MediaRoute2Info wiredHeadsetRoute = getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET); mControllerUnderTest.transferTo(wiredHeadsetRoute.getId()); verify(mMockAudioManager) verify(mMockAudioManager, Mockito.timeout(ASYNC_CALL_TIMEOUTS_MS)) .setPreferredDeviceForStrategy( mMediaAudioProductStrategy, createAudioDeviceAttribute(AudioDeviceInfo.TYPE_WIRED_HEADSET)); Loading