Loading services/core/java/com/android/server/media/DeviceRouteController.java 0 → 100644 +193 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.media; import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO; import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK; import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER; import static android.media.MediaRoute2Info.TYPE_DOCK; import static android.media.MediaRoute2Info.TYPE_HDMI; import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.IAudioRoutesObserver; import android.media.IAudioService; import android.media.MediaRoute2Info; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import java.util.Objects; /** * Controls device routes. * * <p>A device route is a system wired route, for example, built-in speaker, wired * headsets and headphones, dock, hdmi, or usb devices. * * <p>Thread safe. * * @see SystemMediaRoute2Provider */ /* package */ final class DeviceRouteController { private static final String TAG = "WiredRoutesController"; private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; @NonNull private final Context mContext; @NonNull private final AudioManager mAudioManager; @NonNull private final IAudioService mAudioService; @NonNull private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; @NonNull private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver(); private int mDeviceVolume; private MediaRoute2Info mDeviceRoute; /* package */ static DeviceRouteController createInstance(@NonNull Context context, @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) { AudioManager audioManager = context.getSystemService(AudioManager.class); IAudioService audioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); return new DeviceRouteController(context, audioManager, audioService, onDeviceRouteChangedListener); } @VisibleForTesting /* package */ DeviceRouteController(@NonNull Context context, @NonNull AudioManager audioManager, @NonNull IAudioService audioService, @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) { Objects.requireNonNull(context); Objects.requireNonNull(audioManager); Objects.requireNonNull(audioService); Objects.requireNonNull(onDeviceRouteChangedListener); mContext = context; mOnDeviceRouteChangedListener = onDeviceRouteChangedListener; mAudioManager = audioManager; mAudioService = audioService; AudioRoutesInfo newAudioRoutes = null; try { newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); } catch (RemoteException e) { Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e); } mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes); } @NonNull /* package */ synchronized MediaRoute2Info getDeviceRoute() { return mDeviceRoute; } /* package */ synchronized boolean updateVolume(int volume) { if (mDeviceVolume == volume) { return false; } mDeviceVolume = volume; mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) .setVolume(volume) .build(); return true; } private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) { int name = R.string.default_audio_route_name; int type = TYPE_BUILTIN_SPEAKER; if (newRoutes != null) { if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) { type = TYPE_WIRED_HEADPHONES; name = com.android.internal.R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { type = TYPE_WIRED_HEADSET; name = com.android.internal.R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { type = TYPE_DOCK; name = com.android.internal.R.string.default_audio_route_name_dock_speakers; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { type = TYPE_HDMI; name = com.android.internal.R.string.default_audio_route_name_external_device; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) { type = TYPE_USB_DEVICE; name = com.android.internal.R.string.default_audio_route_name_usb; } } synchronized (this) { return new MediaRoute2Info.Builder( DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) .setVolumeHandling(mAudioManager.isVolumeFixed() ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) .setVolume(mDeviceVolume) .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) .setType(type) .addFeature(FEATURE_LIVE_AUDIO) .addFeature(FEATURE_LIVE_VIDEO) .addFeature(FEATURE_LOCAL_PLAYBACK) .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) .build(); } } private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) { mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute); } /* package */ interface OnDeviceRouteChangedListener { void onDeviceRouteChanged(@NonNull MediaRoute2Info deviceRoute); } private class AudioRoutesObserver extends IAudioRoutesObserver.Stub { @Override public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) { MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes); synchronized (DeviceRouteController.this) { mDeviceRoute = deviceRoute; } notifyDeviceRouteUpdate(deviceRoute); } } } services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +29 −91 Original line number Diff line number Diff line Loading @@ -16,25 +16,12 @@ package com.android.server.media; import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO; import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK; import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER; import static android.media.MediaRoute2Info.TYPE_DOCK; import static android.media.MediaRoute2Info.TYPE_HDMI; import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.IAudioRoutesObserver; import android.media.IAudioService; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; Loading @@ -43,14 +30,11 @@ import android.media.RoutingSessionInfo; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import java.util.Objects; Loading @@ -68,23 +52,21 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SystemMediaRoute2Provider.class.getName()); static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE"; static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION"; private final AudioManager mAudioManager; private final IAudioService mAudioService; private final Handler mHandler; private final Context mContext; private final UserHandle mUser; private final DeviceRouteController mDeviceRouteController; private final BluetoothRouteController mBtRouteProvider; private String mSelectedRouteId; // For apps without MODIFYING_AUDIO_ROUTING permission. // This should be the currently selected route. MediaRoute2Info mDefaultRoute; MediaRoute2Info mDeviceRoute; RoutingSessionInfo mDefaultSessionInfo; int mDeviceVolume; private final AudioManagerBroadcastReceiver mAudioReceiver = new AudioManagerBroadcastReceiver(); Loading @@ -93,19 +75,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { @GuardedBy("mRequestLock") private volatile SessionCreationRequest mPendingSessionCreationRequest; final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { @Override public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) { mHandler.post(() -> { updateDeviceRoute(newRoutes); notifyProviderState(); if (updateSessionInfosIfNeeded()) { notifySessionInfoUpdated(); } }); } }; SystemMediaRoute2Provider(Context context, UserHandle user) { super(COMPONENT_NAME); mIsSystemRouteProvider = true; Loading @@ -114,8 +83,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mHandler = new Handler(Looper.getMainLooper()); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); mBtRouteProvider = BluetoothRouteController.createInstance(context, (routes) -> { publishProviderState(); Loading @@ -124,15 +91,18 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } }); AudioRoutesInfo newAudioRoutes = null; try { newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); } catch (RemoteException e) { mDeviceRouteController = DeviceRouteController.createInstance(context, (deviceRoute) -> { mHandler.post(() -> { publishProviderState(); if (updateSessionInfosIfNeeded()) { notifySessionInfoUpdated(); } }); }); // The methods below should be called after all fields are initialized, as they // These methods below should be called after all fields are initialized, as they // access the fields inside. updateDeviceRoute(newAudioRoutes); updateProviderState(); updateSessionInfosIfNeeded(); } Loading Loading @@ -216,7 +186,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // The currently selected route is the default route. return; } if (TextUtils.equals(routeId, mDeviceRoute.getId())) { MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); if (TextUtils.equals(routeId, deviceRoute.getId())) { mBtRouteProvider.transferTo(null); } else { mBtRouteProvider.transferTo(routeId); Loading Loading @@ -254,9 +226,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (mSessionInfos.isEmpty()) { return null; } MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, packageName).setSystemSession(true); builder.addSelectedRoute(mDeviceRoute.getId()); builder.addSelectedRoute(deviceRoute.getId()); for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) { builder.addTransferableRoute(route.getId()); } Loading @@ -264,47 +239,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } private void updateDeviceRoute(AudioRoutesInfo newRoutes) { int name = R.string.default_audio_route_name; int type = TYPE_BUILTIN_SPEAKER; if (newRoutes != null) { if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) { type = TYPE_WIRED_HEADPHONES; name = com.android.internal.R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { type = TYPE_WIRED_HEADSET; name = com.android.internal.R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { type = TYPE_DOCK; name = com.android.internal.R.string.default_audio_route_name_dock_speakers; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { type = TYPE_HDMI; name = com.android.internal.R.string.default_audio_route_name_external_device; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) { type = TYPE_USB_DEVICE; name = com.android.internal.R.string.default_audio_route_name_usb; } } mDeviceRoute = new MediaRoute2Info.Builder( DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) .setVolumeHandling(mAudioManager.isVolumeFixed() ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) .setVolume(mDeviceVolume) .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) .setType(type) .addFeature(FEATURE_LIVE_AUDIO) .addFeature(FEATURE_LIVE_VIDEO) .addFeature(FEATURE_LOCAL_PLAYBACK) .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) .build(); updateProviderState(); } private void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); builder.addRoute(mDeviceRoute); // We must have a device route in the provider info. builder.addRoute(mDeviceRouteController.getDeviceRoute()); for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) { builder.addRoute(route); } Loading @@ -327,11 +267,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SYSTEM_SESSION_ID, "" /* clientPackageName */) .setSystemSession(true); MediaRoute2Info selectedRoute = mDeviceRoute; MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); MediaRoute2Info selectedRoute = deviceRoute; MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute(); if (selectedBtRoute != null) { selectedRoute = selectedBtRoute; builder.addTransferableRoute(mDeviceRoute.getId()); builder.addTransferableRoute(deviceRoute.getId()); } mSelectedRouteId = selectedRoute.getId(); mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute) Loading Loading @@ -423,12 +364,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (mBtRouteProvider.updateVolumeForDevices(devices, volume)) { return; } if (mDeviceVolume != volume) { mDeviceVolume = volume; mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) .setVolume(volume) .build(); } mDeviceRouteController.updateVolume(volume); publishProviderState(); } Loading services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java 0 → 100644 +371 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/media/DeviceRouteController.java 0 → 100644 +193 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.media; import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO; import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK; import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER; import static android.media.MediaRoute2Info.TYPE_DOCK; import static android.media.MediaRoute2Info.TYPE_HDMI; import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.IAudioRoutesObserver; import android.media.IAudioService; import android.media.MediaRoute2Info; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import java.util.Objects; /** * Controls device routes. * * <p>A device route is a system wired route, for example, built-in speaker, wired * headsets and headphones, dock, hdmi, or usb devices. * * <p>Thread safe. * * @see SystemMediaRoute2Provider */ /* package */ final class DeviceRouteController { private static final String TAG = "WiredRoutesController"; private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; @NonNull private final Context mContext; @NonNull private final AudioManager mAudioManager; @NonNull private final IAudioService mAudioService; @NonNull private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; @NonNull private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver(); private int mDeviceVolume; private MediaRoute2Info mDeviceRoute; /* package */ static DeviceRouteController createInstance(@NonNull Context context, @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) { AudioManager audioManager = context.getSystemService(AudioManager.class); IAudioService audioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); return new DeviceRouteController(context, audioManager, audioService, onDeviceRouteChangedListener); } @VisibleForTesting /* package */ DeviceRouteController(@NonNull Context context, @NonNull AudioManager audioManager, @NonNull IAudioService audioService, @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) { Objects.requireNonNull(context); Objects.requireNonNull(audioManager); Objects.requireNonNull(audioService); Objects.requireNonNull(onDeviceRouteChangedListener); mContext = context; mOnDeviceRouteChangedListener = onDeviceRouteChangedListener; mAudioManager = audioManager; mAudioService = audioService; AudioRoutesInfo newAudioRoutes = null; try { newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); } catch (RemoteException e) { Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e); } mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes); } @NonNull /* package */ synchronized MediaRoute2Info getDeviceRoute() { return mDeviceRoute; } /* package */ synchronized boolean updateVolume(int volume) { if (mDeviceVolume == volume) { return false; } mDeviceVolume = volume; mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) .setVolume(volume) .build(); return true; } private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) { int name = R.string.default_audio_route_name; int type = TYPE_BUILTIN_SPEAKER; if (newRoutes != null) { if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) { type = TYPE_WIRED_HEADPHONES; name = com.android.internal.R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { type = TYPE_WIRED_HEADSET; name = com.android.internal.R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { type = TYPE_DOCK; name = com.android.internal.R.string.default_audio_route_name_dock_speakers; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { type = TYPE_HDMI; name = com.android.internal.R.string.default_audio_route_name_external_device; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) { type = TYPE_USB_DEVICE; name = com.android.internal.R.string.default_audio_route_name_usb; } } synchronized (this) { return new MediaRoute2Info.Builder( DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) .setVolumeHandling(mAudioManager.isVolumeFixed() ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) .setVolume(mDeviceVolume) .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) .setType(type) .addFeature(FEATURE_LIVE_AUDIO) .addFeature(FEATURE_LIVE_VIDEO) .addFeature(FEATURE_LOCAL_PLAYBACK) .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) .build(); } } private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) { mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute); } /* package */ interface OnDeviceRouteChangedListener { void onDeviceRouteChanged(@NonNull MediaRoute2Info deviceRoute); } private class AudioRoutesObserver extends IAudioRoutesObserver.Stub { @Override public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) { MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes); synchronized (DeviceRouteController.this) { mDeviceRoute = deviceRoute; } notifyDeviceRouteUpdate(deviceRoute); } } }
services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +29 −91 Original line number Diff line number Diff line Loading @@ -16,25 +16,12 @@ package com.android.server.media; import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO; import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK; import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER; import static android.media.MediaRoute2Info.TYPE_DOCK; import static android.media.MediaRoute2Info.TYPE_HDMI; import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.IAudioRoutesObserver; import android.media.IAudioService; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; Loading @@ -43,14 +30,11 @@ import android.media.RoutingSessionInfo; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import java.util.Objects; Loading @@ -68,23 +52,21 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SystemMediaRoute2Provider.class.getName()); static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE"; static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION"; private final AudioManager mAudioManager; private final IAudioService mAudioService; private final Handler mHandler; private final Context mContext; private final UserHandle mUser; private final DeviceRouteController mDeviceRouteController; private final BluetoothRouteController mBtRouteProvider; private String mSelectedRouteId; // For apps without MODIFYING_AUDIO_ROUTING permission. // This should be the currently selected route. MediaRoute2Info mDefaultRoute; MediaRoute2Info mDeviceRoute; RoutingSessionInfo mDefaultSessionInfo; int mDeviceVolume; private final AudioManagerBroadcastReceiver mAudioReceiver = new AudioManagerBroadcastReceiver(); Loading @@ -93,19 +75,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { @GuardedBy("mRequestLock") private volatile SessionCreationRequest mPendingSessionCreationRequest; final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { @Override public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) { mHandler.post(() -> { updateDeviceRoute(newRoutes); notifyProviderState(); if (updateSessionInfosIfNeeded()) { notifySessionInfoUpdated(); } }); } }; SystemMediaRoute2Provider(Context context, UserHandle user) { super(COMPONENT_NAME); mIsSystemRouteProvider = true; Loading @@ -114,8 +83,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mHandler = new Handler(Looper.getMainLooper()); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); mBtRouteProvider = BluetoothRouteController.createInstance(context, (routes) -> { publishProviderState(); Loading @@ -124,15 +91,18 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } }); AudioRoutesInfo newAudioRoutes = null; try { newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); } catch (RemoteException e) { mDeviceRouteController = DeviceRouteController.createInstance(context, (deviceRoute) -> { mHandler.post(() -> { publishProviderState(); if (updateSessionInfosIfNeeded()) { notifySessionInfoUpdated(); } }); }); // The methods below should be called after all fields are initialized, as they // These methods below should be called after all fields are initialized, as they // access the fields inside. updateDeviceRoute(newAudioRoutes); updateProviderState(); updateSessionInfosIfNeeded(); } Loading Loading @@ -216,7 +186,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // The currently selected route is the default route. return; } if (TextUtils.equals(routeId, mDeviceRoute.getId())) { MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); if (TextUtils.equals(routeId, deviceRoute.getId())) { mBtRouteProvider.transferTo(null); } else { mBtRouteProvider.transferTo(routeId); Loading Loading @@ -254,9 +226,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (mSessionInfos.isEmpty()) { return null; } MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, packageName).setSystemSession(true); builder.addSelectedRoute(mDeviceRoute.getId()); builder.addSelectedRoute(deviceRoute.getId()); for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) { builder.addTransferableRoute(route.getId()); } Loading @@ -264,47 +239,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } private void updateDeviceRoute(AudioRoutesInfo newRoutes) { int name = R.string.default_audio_route_name; int type = TYPE_BUILTIN_SPEAKER; if (newRoutes != null) { if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) { type = TYPE_WIRED_HEADPHONES; name = com.android.internal.R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { type = TYPE_WIRED_HEADSET; name = com.android.internal.R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { type = TYPE_DOCK; name = com.android.internal.R.string.default_audio_route_name_dock_speakers; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { type = TYPE_HDMI; name = com.android.internal.R.string.default_audio_route_name_external_device; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) { type = TYPE_USB_DEVICE; name = com.android.internal.R.string.default_audio_route_name_usb; } } mDeviceRoute = new MediaRoute2Info.Builder( DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) .setVolumeHandling(mAudioManager.isVolumeFixed() ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) .setVolume(mDeviceVolume) .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) .setType(type) .addFeature(FEATURE_LIVE_AUDIO) .addFeature(FEATURE_LIVE_VIDEO) .addFeature(FEATURE_LOCAL_PLAYBACK) .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) .build(); updateProviderState(); } private void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); builder.addRoute(mDeviceRoute); // We must have a device route in the provider info. builder.addRoute(mDeviceRouteController.getDeviceRoute()); for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) { builder.addRoute(route); } Loading @@ -327,11 +267,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SYSTEM_SESSION_ID, "" /* clientPackageName */) .setSystemSession(true); MediaRoute2Info selectedRoute = mDeviceRoute; MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); MediaRoute2Info selectedRoute = deviceRoute; MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute(); if (selectedBtRoute != null) { selectedRoute = selectedBtRoute; builder.addTransferableRoute(mDeviceRoute.getId()); builder.addTransferableRoute(deviceRoute.getId()); } mSelectedRouteId = selectedRoute.getId(); mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute) Loading Loading @@ -423,12 +364,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (mBtRouteProvider.updateVolumeForDevices(devices, volume)) { return; } if (mDeviceVolume != volume) { mDeviceVolume = volume; mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) .setVolume(volume) .build(); } mDeviceRouteController.updateVolume(volume); publishProviderState(); } Loading
services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java 0 → 100644 +371 −0 File added.Preview size limit exceeded, changes collapsed. Show changes