Loading services/core/java/com/android/server/media/AudioAttributesUtils.java 0 → 100644 +112 −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 android.annotation.NonNull; import android.annotation.Nullable; import android.media.AudioAttributes; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.media.MediaRoute2Info; /* package */ final class AudioAttributesUtils { /* package */ static final AudioAttributes ATTRIBUTES_MEDIA = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .build(); private AudioAttributesUtils() { // no-op to prevent instantiation. } @MediaRoute2Info.Type /* package */ static int mapToMediaRouteType( @NonNull AudioDeviceAttributes audioDeviceAttributes) { switch (audioDeviceAttributes.getType()) { case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE: case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER: return MediaRoute2Info.TYPE_BUILTIN_SPEAKER; case AudioDeviceInfo.TYPE_WIRED_HEADSET: return MediaRoute2Info.TYPE_WIRED_HEADSET; case AudioDeviceInfo.TYPE_WIRED_HEADPHONES: return MediaRoute2Info.TYPE_WIRED_HEADPHONES; case AudioDeviceInfo.TYPE_DOCK: case AudioDeviceInfo.TYPE_DOCK_ANALOG: return MediaRoute2Info.TYPE_DOCK; case AudioDeviceInfo.TYPE_HDMI: return MediaRoute2Info.TYPE_HDMI; case AudioDeviceInfo.TYPE_USB_DEVICE: return MediaRoute2Info.TYPE_USB_DEVICE; case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP: return MediaRoute2Info.TYPE_BLUETOOTH_A2DP; case AudioDeviceInfo.TYPE_BLE_HEADSET: return MediaRoute2Info.TYPE_BLE_HEADSET; case AudioDeviceInfo.TYPE_HEARING_AID: return MediaRoute2Info.TYPE_HEARING_AID; default: return MediaRoute2Info.TYPE_UNKNOWN; } } /* package */ static boolean isDeviceOutputAttributes( @Nullable AudioDeviceAttributes audioDeviceAttributes) { if (audioDeviceAttributes == null) { return false; } if (audioDeviceAttributes.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) { return false; } switch (audioDeviceAttributes.getType()) { case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE: case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER: case AudioDeviceInfo.TYPE_WIRED_HEADSET: case AudioDeviceInfo.TYPE_WIRED_HEADPHONES: case AudioDeviceInfo.TYPE_DOCK: case AudioDeviceInfo.TYPE_DOCK_ANALOG: case AudioDeviceInfo.TYPE_HDMI: case AudioDeviceInfo.TYPE_USB_DEVICE: return true; default: return false; } } /* package */ static boolean isBluetoothOutputAttributes( @Nullable AudioDeviceAttributes audioDeviceAttributes) { if (audioDeviceAttributes == null) { return false; } if (audioDeviceAttributes.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) { return false; } switch (audioDeviceAttributes.getType()) { case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP: case AudioDeviceInfo.TYPE_BLE_HEADSET: case AudioDeviceInfo.TYPE_BLE_SPEAKER: case AudioDeviceInfo.TYPE_HEARING_AID: return true; default: return false; } } } services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +51 −0 Original line number Diff line number Diff line Loading @@ -16,11 +16,14 @@ package com.android.server.media; import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioAttributes; import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; Loading @@ -37,6 +40,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import java.util.List; import java.util.Objects; /** Loading Loading @@ -71,6 +75,26 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private final AudioManagerBroadcastReceiver mAudioReceiver = new AudioManagerBroadcastReceiver(); private final AudioManager.OnDevicesForAttributesChangedListener mOnDevicesForAttributesChangedListener = new AudioManager.OnDevicesForAttributesChangedListener() { @Override public void onDevicesForAttributesChanged(@NonNull AudioAttributes attributes, @NonNull List<AudioDeviceAttributes> devices) { if (attributes.getUsage() != AudioAttributes.USAGE_MEDIA) { return; } mHandler.post(() -> { updateSelectedAudioDevice(devices); notifyProviderState(); if (updateSessionInfosIfNeeded()) { notifySessionInfoUpdated(); } }); } }; private final Object mRequestLock = new Object(); @GuardedBy("mRequestLock") private volatile SessionCreationRequest mPendingSessionCreationRequest; Loading Loading @@ -100,8 +124,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { }); }); mAudioManager.addOnDevicesForAttributesChangedListener( AudioAttributesUtils.ATTRIBUTES_MEDIA, mContext.getMainExecutor(), mOnDevicesForAttributesChangedListener); // These methods below should be called after all fields are initialized, as they // access the fields inside. List<AudioDeviceAttributes> devices = mAudioManager.getDevicesForAttributes(AudioAttributesUtils.ATTRIBUTES_MEDIA); updateSelectedAudioDevice(devices); updateProviderState(); updateSessionInfosIfNeeded(); } Loading Loading @@ -239,6 +270,26 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } private void updateSelectedAudioDevice(@NonNull List<AudioDeviceAttributes> devices) { if (devices.isEmpty()) { Slog.w(TAG, "The list of preferred devices was empty."); return; } AudioDeviceAttributes audioDeviceAttributes = devices.get(0); if (AudioAttributesUtils.isDeviceOutputAttributes(audioDeviceAttributes)) { mDeviceRouteController.selectRoute( AudioAttributesUtils.mapToMediaRouteType(audioDeviceAttributes)); mBluetoothRouteController.selectRoute(null); } else if (AudioAttributesUtils.isBluetoothOutputAttributes(audioDeviceAttributes)) { mDeviceRouteController.selectRoute(null); mBluetoothRouteController.selectRoute(audioDeviceAttributes.getAddress()); } else { Slog.w(TAG, "Unknown audio attributes: " + audioDeviceAttributes); } } private void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); Loading Loading
services/core/java/com/android/server/media/AudioAttributesUtils.java 0 → 100644 +112 −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 android.annotation.NonNull; import android.annotation.Nullable; import android.media.AudioAttributes; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.media.MediaRoute2Info; /* package */ final class AudioAttributesUtils { /* package */ static final AudioAttributes ATTRIBUTES_MEDIA = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .build(); private AudioAttributesUtils() { // no-op to prevent instantiation. } @MediaRoute2Info.Type /* package */ static int mapToMediaRouteType( @NonNull AudioDeviceAttributes audioDeviceAttributes) { switch (audioDeviceAttributes.getType()) { case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE: case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER: return MediaRoute2Info.TYPE_BUILTIN_SPEAKER; case AudioDeviceInfo.TYPE_WIRED_HEADSET: return MediaRoute2Info.TYPE_WIRED_HEADSET; case AudioDeviceInfo.TYPE_WIRED_HEADPHONES: return MediaRoute2Info.TYPE_WIRED_HEADPHONES; case AudioDeviceInfo.TYPE_DOCK: case AudioDeviceInfo.TYPE_DOCK_ANALOG: return MediaRoute2Info.TYPE_DOCK; case AudioDeviceInfo.TYPE_HDMI: return MediaRoute2Info.TYPE_HDMI; case AudioDeviceInfo.TYPE_USB_DEVICE: return MediaRoute2Info.TYPE_USB_DEVICE; case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP: return MediaRoute2Info.TYPE_BLUETOOTH_A2DP; case AudioDeviceInfo.TYPE_BLE_HEADSET: return MediaRoute2Info.TYPE_BLE_HEADSET; case AudioDeviceInfo.TYPE_HEARING_AID: return MediaRoute2Info.TYPE_HEARING_AID; default: return MediaRoute2Info.TYPE_UNKNOWN; } } /* package */ static boolean isDeviceOutputAttributes( @Nullable AudioDeviceAttributes audioDeviceAttributes) { if (audioDeviceAttributes == null) { return false; } if (audioDeviceAttributes.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) { return false; } switch (audioDeviceAttributes.getType()) { case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE: case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER: case AudioDeviceInfo.TYPE_WIRED_HEADSET: case AudioDeviceInfo.TYPE_WIRED_HEADPHONES: case AudioDeviceInfo.TYPE_DOCK: case AudioDeviceInfo.TYPE_DOCK_ANALOG: case AudioDeviceInfo.TYPE_HDMI: case AudioDeviceInfo.TYPE_USB_DEVICE: return true; default: return false; } } /* package */ static boolean isBluetoothOutputAttributes( @Nullable AudioDeviceAttributes audioDeviceAttributes) { if (audioDeviceAttributes == null) { return false; } if (audioDeviceAttributes.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) { return false; } switch (audioDeviceAttributes.getType()) { case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP: case AudioDeviceInfo.TYPE_BLE_HEADSET: case AudioDeviceInfo.TYPE_BLE_SPEAKER: case AudioDeviceInfo.TYPE_HEARING_AID: return true; default: return false; } } }
services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +51 −0 Original line number Diff line number Diff line Loading @@ -16,11 +16,14 @@ package com.android.server.media; import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioAttributes; import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; Loading @@ -37,6 +40,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import java.util.List; import java.util.Objects; /** Loading Loading @@ -71,6 +75,26 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private final AudioManagerBroadcastReceiver mAudioReceiver = new AudioManagerBroadcastReceiver(); private final AudioManager.OnDevicesForAttributesChangedListener mOnDevicesForAttributesChangedListener = new AudioManager.OnDevicesForAttributesChangedListener() { @Override public void onDevicesForAttributesChanged(@NonNull AudioAttributes attributes, @NonNull List<AudioDeviceAttributes> devices) { if (attributes.getUsage() != AudioAttributes.USAGE_MEDIA) { return; } mHandler.post(() -> { updateSelectedAudioDevice(devices); notifyProviderState(); if (updateSessionInfosIfNeeded()) { notifySessionInfoUpdated(); } }); } }; private final Object mRequestLock = new Object(); @GuardedBy("mRequestLock") private volatile SessionCreationRequest mPendingSessionCreationRequest; Loading Loading @@ -100,8 +124,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { }); }); mAudioManager.addOnDevicesForAttributesChangedListener( AudioAttributesUtils.ATTRIBUTES_MEDIA, mContext.getMainExecutor(), mOnDevicesForAttributesChangedListener); // These methods below should be called after all fields are initialized, as they // access the fields inside. List<AudioDeviceAttributes> devices = mAudioManager.getDevicesForAttributes(AudioAttributesUtils.ATTRIBUTES_MEDIA); updateSelectedAudioDevice(devices); updateProviderState(); updateSessionInfosIfNeeded(); } Loading Loading @@ -239,6 +270,26 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } private void updateSelectedAudioDevice(@NonNull List<AudioDeviceAttributes> devices) { if (devices.isEmpty()) { Slog.w(TAG, "The list of preferred devices was empty."); return; } AudioDeviceAttributes audioDeviceAttributes = devices.get(0); if (AudioAttributesUtils.isDeviceOutputAttributes(audioDeviceAttributes)) { mDeviceRouteController.selectRoute( AudioAttributesUtils.mapToMediaRouteType(audioDeviceAttributes)); mBluetoothRouteController.selectRoute(null); } else if (AudioAttributesUtils.isBluetoothOutputAttributes(audioDeviceAttributes)) { mDeviceRouteController.selectRoute(null); mBluetoothRouteController.selectRoute(audioDeviceAttributes.getAddress()); } else { Slog.w(TAG, "Unknown audio attributes: " + audioDeviceAttributes); } } private void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); Loading