diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 5581b2ce78fda..ba5a78998a020 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -6860,6 +6860,7 @@ package android.media { method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException; method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException; method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException; + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void addOnStreamAliasingChangedListener(@NonNull java.util.concurrent.Executor, @NonNull Runnable); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void cancelMuteAwaitConnection(@NonNull android.media.AudioDeviceAttributes) throws java.lang.IllegalStateException; method public void clearAudioServerStateCallback(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int); @@ -6873,6 +6874,7 @@ package android.media { method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioTrack getCallUplinkInjectionAudioTrack(@NonNull android.media.AudioFormat); method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE", android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public java.util.List getDevicesForAttributes(@NonNull android.media.AudioAttributes); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public java.util.List getIndependentStreamTypes(); method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int getLastAudibleStreamVolume(int); method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int getLastAudibleVolumeGroupVolume(int); method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); @@ -6883,6 +6885,7 @@ package android.media { method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List getPreferredDevicesForCapturePreset(int); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public int getStreamTypeAlias(int); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages(); method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int getVolumeGroupMaxVolumeIndex(int); method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int getVolumeGroupMinVolumeIndex(int); @@ -6905,6 +6908,7 @@ package android.media { method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void removeOnStreamAliasingChangedListener(@NonNull Runnable); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 5deef1af463e1..28febbd1a3e9d 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1756,8 +1756,10 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public boolean isCsdEnabled(); method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE, android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS}) public boolean isFullVolumeDevice(); method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public boolean isVolumeControlUsingVolumeGroups(); method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int requestAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String, int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void setCsd(float); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void setNotifAliasRingForTest(boolean); method public void setRampingRingerEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void setRs2Value(float); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setTestDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, boolean); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 0a37eedf4fa14..d854ed331c151 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -9465,6 +9465,140 @@ public class AudioManager { } } + //==================================================================== + // Stream aliasing changed listener, getter for stream alias or independent streams + + /** + * manages the stream aliasing listeners and StreamAliasingDispatcherStub + */ + private final CallbackUtil.LazyListenerManager mStreamAliasingListenerMgr = + new CallbackUtil.LazyListenerManager(); + + + final class StreamAliasingDispatcherStub extends IStreamAliasingDispatcher.Stub + implements CallbackUtil.DispatcherStub { + + @Override + public void register(boolean register) { + try { + getService().registerStreamAliasingDispatcher(this, register); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + @Override + public void dispatchStreamAliasingChanged() { + mStreamAliasingListenerMgr.callListeners((listener) -> listener.run()); + } + } + + /** + * @hide + * Adds a listener to be notified of changes to volume stream type aliasing. + * See {@link #getIndependentStreamTypes()} and {@link #getStreamTypeAlias(int)} + * @param executor the Executor running the listener + * @param onStreamAliasingChangedListener the listener to add for the aliasing changes + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void addOnStreamAliasingChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull Runnable onStreamAliasingChangedListener) { + mStreamAliasingListenerMgr.addListener(executor, onStreamAliasingChangedListener, + "addOnStreamAliasingChangedListener", + () -> new StreamAliasingDispatcherStub()); + } + + /** + * @hide + * Removes a previously added listener for changes to stream aliasing. + * See {@link #getIndependentStreamTypes()} and {@link #getStreamTypeAlias(int)} + * @see #addOnStreamAliasingChangedListener(Executor, Runnable) + * @param onStreamAliasingChangedListener the previously added listener of aliasing changes + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void removeOnStreamAliasingChangedListener( + @NonNull Runnable onStreamAliasingChangedListener) { + mStreamAliasingListenerMgr.removeListener(onStreamAliasingChangedListener, + "removeOnStreamAliasingChangedListener"); + } + + /** + * @hide + * Test method to temporarily override whether STREAM_NOTIFICATION is aliased to STREAM_RING, + * volumes will be updated in case of a change. + * @param isAliased if true, STREAM_NOTIFICATION is aliased to STREAM_RING + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void setNotifAliasRingForTest(boolean isAliased) { + final IAudioService service = getService(); + try { + service.setNotifAliasRingForTest(isAliased); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Return the list of independent stream types for volume control. + * A stream type is considered independent when the volume changes of that type do not + * affect any other independent volume control stream type. + * An independent stream type is its own alias when using {@link #getStreamTypeAlias(int)}. + * @return list of independent stream types. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public @NonNull List getIndependentStreamTypes() { + final IAudioService service = getService(); + try { + return service.getIndependentStreamTypes(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Return the stream type that a given stream is aliased to. + * A stream alias means that any change to the source stream will also be applied to the alias, + * and vice-versa. + * If a stream is independent (i.e. part of the stream types returned by + * {@link #getIndependentStreamTypes()}), its alias is itself. + * @param sourceStreamType the stream type to query for the alias. + * @return the stream type the source type is aliased to. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public @PublicStreamTypes int getStreamTypeAlias(@PublicStreamTypes int sourceStreamType) { + final IAudioService service = getService(); + try { + return service.getStreamTypeAlias(sourceStreamType); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Returns whether the system uses {@link AudioVolumeGroup} for volume control + * @return true when volume control is performed through volume groups, false if it uses + * stream types. + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public boolean isVolumeControlUsingVolumeGroups() { + final IAudioService service = getService(); + try { + return service.isVolumeControlUsingVolumeGroups(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + //==================================================================== // Mute await connection diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 1c517e7872d52..324e81a50ac95 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -49,6 +49,7 @@ import android.media.ISpatializerHeadTrackerAvailableCallback; import android.media.ISpatializerHeadTrackingModeCallback; import android.media.ISpatializerHeadToSoundStagePoseCallback; import android.media.ISpatializerOutputCallback; +import android.media.IStreamAliasingDispatcher; import android.media.IVolumeController; import android.media.PlayerBase; import android.media.VolumeInfo; @@ -257,6 +258,21 @@ interface IAudioService { IRingtonePlayer getRingtonePlayer(); int getUiSoundsStreamType(); + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + List getIndependentStreamTypes(); + + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + int getStreamTypeAlias(int streamType); + + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + boolean isVolumeControlUsingVolumeGroups(); + + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + oneway void registerStreamAliasingDispatcher(IStreamAliasingDispatcher isad, boolean register); + + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + void setNotifAliasRingForTest(boolean alias); + @EnforcePermission("MODIFY_AUDIO_ROUTING") void setWiredDeviceConnectionState(in AudioDeviceAttributes aa, int state, String caller); diff --git a/media/java/android/media/IStreamAliasingDispatcher.aidl b/media/java/android/media/IStreamAliasingDispatcher.aidl new file mode 100644 index 0000000000000..1ef63c3b5f845 --- /dev/null +++ b/media/java/android/media/IStreamAliasingDispatcher.aidl @@ -0,0 +1,27 @@ +/* + * 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 android.media; + +/** + * AIDL for AudioService to signal stream aliasing update. + * + * {@hide} + */ +oneway interface IStreamAliasingDispatcher { + + void dispatchStreamAliasingChanged(); +} diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index e48c5385cbeb9..dd497306f136c 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -119,6 +119,7 @@ import android.media.ISpatializerHeadTrackingModeCallback; import android.media.ISpatializerOutputCallback; import android.media.IStrategyNonDefaultDevicesDispatcher; import android.media.IStrategyPreferredDevicesDispatcher; +import android.media.IStreamAliasingDispatcher; import android.media.IVolumeController; import android.media.MediaMetrics; import android.media.MediaRecorder.AudioSource; @@ -310,7 +311,9 @@ public class AudioService extends IAudioService.Stub * volumes will be updated in case of a change. * @param alias if true, STREAM_NOTIFICATION is aliased to STREAM_RING */ - /*package*/ void setNotifAliasRingForTest(boolean alias) { + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void setNotifAliasRingForTest(boolean alias) { + super.setNotifAliasRingForTest_enforcePermission(); boolean update = (mNotifAliasRing != alias); mNotifAliasRing = alias; if (update) { @@ -2282,6 +2285,7 @@ public class AudioService extends IAudioService.Stub 0, mStreamStates[AudioSystem.STREAM_ACCESSIBILITY], 0); } + dispatchStreamAliasingUpdate(); } private void readDockAudioSettings(ContentResolver cr) @@ -5018,6 +5022,91 @@ public class AudioService extends IAudioService.Stub return sDefaultVolumeInfo; } + /** + * list of callback dispatchers for stream aliasing updates + */ + final RemoteCallbackList mStreamAliasingDispatchers = + new RemoteCallbackList(); + + /** + * Register/unregister a callback for stream aliasing updates + * @param isad the callback dispatcher + * @param register whether this for a registration or unregistration + * @see AudioManager#addOnStreamAliasingChangedListener(Executor, Runnable) + * @see AudioManager#removeOnStreamAliasingChangedListener(Runnable) + */ + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void registerStreamAliasingDispatcher(IStreamAliasingDispatcher isad, boolean register) { + super.registerStreamAliasingDispatcher_enforcePermission(); + Objects.requireNonNull(isad); + + if (register) { + mStreamAliasingDispatchers.register(isad); + } else { + mStreamAliasingDispatchers.unregister(isad); + } + } + + protected void dispatchStreamAliasingUpdate() { + final int nbDispatchers = mStreamAliasingDispatchers.beginBroadcast(); + for (int i = 0; i < nbDispatchers; i++) { + try { + mStreamAliasingDispatchers.getBroadcastItem(i).dispatchStreamAliasingChanged(); + } catch (RemoteException e) { + Log.e(TAG, "Error on stream alias update dispatch", e); + } + } + mStreamAliasingDispatchers.finishBroadcast(); + } + + /** + * @see AudioManager#getIndependentStreamTypes() + * @return the list of non-aliased stream types + */ + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public ArrayList getIndependentStreamTypes() { + super.getIndependentStreamTypes_enforcePermission(); + + if (mUseVolumeGroupAliases) { + return new ArrayList<>(Arrays.stream(AudioManager.getPublicStreamTypes()) + .boxed().toList()); + } + ArrayList res = new ArrayList(1); + for (int stream : mStreamVolumeAlias) { + if (!res.contains(stream)) { + res.add(stream); + } + } + return res; + } + + /** + * @see AudioManager#getStreamTypeAlias(int) + * @param sourceStreamType the stream type for which the alias is queried + * @return the stream alias + */ + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public @AudioManager.PublicStreamTypes + int getStreamTypeAlias(@AudioManager.PublicStreamTypes int sourceStreamType) { + super.getStreamTypeAlias_enforcePermission(); + // verify parameters + ensureValidStreamType(sourceStreamType); + + return mStreamVolumeAlias[sourceStreamType]; + } + + /** + * @see AudioManager#isVolumeControlUsingVolumeGroups() + * @return true when volume control is performed through volume groups, false if it uses + * stream types. + */ + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public boolean isVolumeControlUsingVolumeGroups() { + super.isVolumeControlUsingVolumeGroups_enforcePermission(); + + return mUseVolumeGroupAliases; + } + /** @see AudioManager#getUiSoundsStreamType() * TODO(b/181140246): when using VolumeGroup alias, we are lacking configurability for * UI Sounds identification. @@ -10723,6 +10812,7 @@ public class AudioService extends IAudioService.Stub pw.print(" mBluetoothName="); pw.println(mDeviceBroker.getCurAudioRoutes().bluetoothName); pw.println("\nOther state:"); + pw.print(" mUseVolumeGroupAliases="); pw.println(mUseVolumeGroupAliases); pw.print(" mVolumeController="); pw.println(mVolumeController); mSoundDoseHelper.dump(pw); pw.print(" sIndependentA11yVolume="); pw.println(sIndependentA11yVolume);