diff options
7 files changed, 175 insertions, 2 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 52eae43f7db9..aa3dbda374be 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -21,6 +21,7 @@ import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO; import static android.content.Context.DEVICE_ID_DEFAULT; import static android.media.audio.Flags.autoPublicVolumeApiHardening; import static android.media.audio.Flags.automaticBtDeviceType; +import static android.media.audio.Flags.cacheGetStreamMinMaxVolume; import static android.media.audio.Flags.FLAG_DEPRECATE_STREAM_BT_SCO; import static android.media.audio.Flags.FLAG_FOCUS_EXCLUSIVE_WITH_RECORDING; import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API; @@ -58,7 +59,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.media.AudioAttributes.AttributeSystemUsage; -import android.media.AudioDeviceInfo; import android.media.CallbackUtil.ListenerInfo; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener; @@ -75,6 +75,7 @@ import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.IBinder; +import android.os.IpcDataCache; import android.os.Looper; import android.os.Message; import android.os.RemoteException; @@ -1231,6 +1232,84 @@ public class AudioManager { } /** + * API string for caching the min volume for each stream + * @hide + **/ + public static final String VOLUME_MIN_CACHING_API = "getStreamMinVolume"; + /** + * API string for caching the max volume for each stream + * @hide + **/ + public static final String VOLUME_MAX_CACHING_API = "getStreamMaxVolume"; + private static final int VOLUME_MIN_MAX_CACHING_SIZE = 16; + + private final IpcDataCache.QueryHandler<VolumeCacheQuery, Integer> mVolQuery = + new IpcDataCache.QueryHandler<>() { + @Override + public Integer apply(VolumeCacheQuery query) { + final IAudioService service = getService(); + try { + return switch (query.queryCommand) { + case QUERY_VOL_MIN -> service.getStreamMinVolume(query.stream); + case QUERY_VOL_MAX -> service.getStreamMaxVolume(query.stream); + default -> { + Log.w(TAG, "Not a valid volume cache query: " + query); + yield null; + } + }; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + }; + + private final IpcDataCache<VolumeCacheQuery, Integer> mVolMinCache = + new IpcDataCache<>(VOLUME_MIN_MAX_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM, + VOLUME_MIN_CACHING_API, VOLUME_MIN_CACHING_API, mVolQuery); + + private final IpcDataCache<VolumeCacheQuery, Integer> mVolMaxCache = + new IpcDataCache<>(VOLUME_MIN_MAX_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM, + VOLUME_MAX_CACHING_API, VOLUME_MAX_CACHING_API, mVolQuery); + + /** + * Used to invalidate the cache for the given API + * @hide + **/ + public static void clearVolumeCache(String api) { + if (cacheGetStreamMinMaxVolume()) { + IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, api); + } + } + + private static final int QUERY_VOL_MIN = 1; + private static final int QUERY_VOL_MAX = 2; + /** @hide */ + @IntDef(prefix = "QUERY_VOL", value = { + QUERY_VOL_MIN, + QUERY_VOL_MAX} + ) + @Retention(RetentionPolicy.SOURCE) + private @interface QueryVolCommand {} + + private record VolumeCacheQuery(int stream, @QueryVolCommand int queryCommand) { + private String queryVolCommandToString() { + return switch (queryCommand) { + case QUERY_VOL_MIN -> "getStreamMinVolume"; + case QUERY_VOL_MAX -> "getStreamMaxVolume"; + default -> "invalid command"; + }; + } + + @NonNull + @Override + public String toString() { + return TextUtils.formatSimple("VolumeCacheQuery(stream=%d, queryCommand=%s)", stream, + queryVolCommandToString()); + } + } + + /** * Returns the maximum volume index for a particular stream. * * @param streamType The stream type whose maximum volume index is returned. @@ -1238,6 +1317,9 @@ public class AudioManager { * @see #getStreamVolume(int) */ public int getStreamMaxVolume(int streamType) { + if (cacheGetStreamMinMaxVolume()) { + return mVolMaxCache.query(new VolumeCacheQuery(streamType, QUERY_VOL_MAX)); + } final IAudioService service = getService(); try { return service.getStreamMaxVolume(streamType); @@ -1271,6 +1353,9 @@ public class AudioManager { */ @TestApi public int getStreamMinVolumeInt(int streamType) { + if (cacheGetStreamMinMaxVolume()) { + return mVolMinCache.query(new VolumeCacheQuery(streamType, QUERY_VOL_MIN)); + } final IAudioService service = getService(); try { return service.getStreamMinVolume(streamType); diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java index e9a0d3eceba3..017a1029d35c 100644 --- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java @@ -16,6 +16,15 @@ package com.android.audiopolicytest; +import static android.media.AudioManager.STREAM_ACCESSIBILITY; +import static android.media.AudioManager.STREAM_ALARM; +import static android.media.AudioManager.STREAM_DTMF; +import static android.media.AudioManager.STREAM_MUSIC; +import static android.media.AudioManager.STREAM_NOTIFICATION; +import static android.media.AudioManager.STREAM_RING; +import static android.media.AudioManager.STREAM_SYSTEM; +import static android.media.AudioManager.STREAM_VOICE_CALL; + import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.android.audiopolicytest.AudioVolumeTestUtil.DEFAULT_ATTRIBUTES; @@ -28,11 +37,15 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import android.content.Context; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.IAudioService; import android.media.audiopolicy.AudioProductStrategy; import android.media.audiopolicy.AudioVolumeGroup; +import android.os.IBinder; +import android.os.ServiceManager; import android.platform.test.annotations.Presubmit; import android.util.Log; @@ -207,6 +220,32 @@ public class AudioManagerTest { } //----------------------------------------------------------------- + // Test getStreamVolume consistency with AudioService + //----------------------------------------------------------------- + @Test + public void getStreamMinMaxVolume_consistentWithAs() throws Exception { + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + IAudioService service = IAudioService.Stub.asInterface(b); + final int[] streamTypes = { + STREAM_VOICE_CALL, + STREAM_SYSTEM, + STREAM_RING, + STREAM_MUSIC, + STREAM_ALARM, + STREAM_NOTIFICATION, + STREAM_DTMF, + STREAM_ACCESSIBILITY, + }; + + for (int streamType : streamTypes) { + assertEquals(service.getStreamMinVolume(streamType), + mAudioManager.getStreamMinVolume(streamType)); + assertEquals(service.getStreamMaxVolume(streamType), + mAudioManager.getStreamMaxVolume(streamType)); + } + } + + //----------------------------------------------------------------- // Test Volume per Attributes setter/getters //----------------------------------------------------------------- @Test diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java index 030ce12f5063..c35f4fca6edd 100644 --- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java +++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java @@ -65,6 +65,10 @@ class AudioManagerShellCommand extends ShellCommand { return setRingerMode(); case "set-volume": return setVolume(); + case "get-min-volume": + return getMinVolume(); + case "get-max-volume": + return getMaxVolume(); case "set-device-volume": return setDeviceVolume(); case "adj-mute": @@ -106,6 +110,10 @@ class AudioManagerShellCommand extends ShellCommand { pw.println(" Sets the Ringer mode to one of NORMAL|SILENT|VIBRATE"); pw.println(" set-volume STREAM_TYPE VOLUME_INDEX"); pw.println(" Sets the volume for STREAM_TYPE to VOLUME_INDEX"); + pw.println(" get-min-volume STREAM_TYPE"); + pw.println(" Gets the min volume for STREAM_TYPE"); + pw.println(" get-max-volume STREAM_TYPE"); + pw.println(" Gets the max volume for STREAM_TYPE"); pw.println(" set-device-volume STREAM_TYPE VOLUME_INDEX NATIVE_DEVICE_TYPE"); pw.println(" Sets for NATIVE_DEVICE_TYPE the STREAM_TYPE volume to VOLUME_INDEX"); pw.println(" adj-mute STREAM_TYPE"); @@ -296,6 +304,24 @@ class AudioManagerShellCommand extends ShellCommand { return 0; } + private int getMinVolume() { + final Context context = mService.mContext; + final AudioManager am = context.getSystemService(AudioManager.class); + final int stream = readIntArg(); + final int result = am.getStreamMinVolume(stream); + getOutPrintWriter().println("AudioManager.getStreamMinVolume(" + stream + ") -> " + result); + return 0; + } + + private int getMaxVolume() { + final Context context = mService.mContext; + final AudioManager am = context.getSystemService(AudioManager.class); + final int stream = readIntArg(); + final int result = am.getStreamMaxVolume(stream); + getOutPrintWriter().println("AudioManager.getStreamMaxVolume(" + stream + ") -> " + result); + return 0; + } + private int setDeviceVolume() { final Context context = mService.mContext; final AudioDeviceVolumeManager advm = (AudioDeviceVolumeManager) context.getSystemService( diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index c125d2d2e8cd..1baded86705e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -47,9 +47,9 @@ import static android.media.AudioManager.RINGER_MODE_NORMAL; import static android.media.AudioManager.RINGER_MODE_SILENT; import static android.media.AudioManager.RINGER_MODE_VIBRATE; import static android.media.AudioManager.STREAM_SYSTEM; -import static android.media.IAudioManagerNative.HardeningType; import static android.media.audio.Flags.autoPublicVolumeApiHardening; import static android.media.audio.Flags.automaticBtDeviceType; +import static android.media.audio.Flags.cacheGetStreamMinMaxVolume; import static android.media.audio.Flags.concurrentAudioRecordBypassPermission; import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency; import static android.media.audio.Flags.focusFreezeTestApi; @@ -4966,6 +4966,8 @@ public class AudioService extends IAudioService.Stub + ringMyCar()); pw.println("\tandroid.media.audio.Flags.concurrentAudioRecordBypassPermission:" + concurrentAudioRecordBypassPermission()); + pw.println("\tandroid.media.audio.Flags.cacheGetStreamMinMaxVolume:" + + cacheGetStreamMinMaxVolume()); } private void dumpAudioMode(PrintWriter pw) { @@ -9356,6 +9358,12 @@ public class AudioService extends IAudioService.Stub mIndexMinNoPerm = mIndexMin; } } + if (cacheGetStreamMinMaxVolume() && mStreamType == AudioSystem.STREAM_VOICE_CALL) { + if (DEBUG_VOL) { + Log.d(TAG, "Clear min volume cache from updateIndexFactors"); + } + AudioManager.clearVolumeCache(AudioManager.VOLUME_MIN_CACHING_API); + } final int status = AudioSystem.initStreamVolume( mStreamType, indexMinVolCurve, indexMaxVolCurve); @@ -9393,11 +9401,19 @@ public class AudioService extends IAudioService.Stub * @param index minimum index expressed in "UI units", i.e. no 10x factor */ public void updateNoPermMinIndex(int index) { + boolean changedNoPermMinIndex = + cacheGetStreamMinMaxVolume() && (index * 10) != mIndexMinNoPerm; mIndexMinNoPerm = index * 10; if (mIndexMinNoPerm < mIndexMin) { Log.e(TAG, "Invalid mIndexMinNoPerm for stream " + mStreamType); mIndexMinNoPerm = mIndexMin; } + if (changedNoPermMinIndex) { + if (DEBUG_VOL) { + Log.d(TAG, "Clear min volume cache from updateNoPermMinIndex"); + } + AudioManager.clearVolumeCache(AudioManager.VOLUME_MIN_CACHING_API); + } } /** diff --git a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java index ef9580c54de6..8d3eef4a3168 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java @@ -38,6 +38,7 @@ import android.media.AudioManager; import android.media.AudioSystem; import android.media.IAudioDeviceVolumeDispatcher; import android.media.VolumeInfo; +import android.os.IpcDataCache; import android.os.PermissionEnforcer; import android.os.RemoteException; import android.os.test.TestLooper; @@ -83,6 +84,7 @@ public class AbsoluteVolumeBehaviorTest { @Before public void setUp() throws Exception { + IpcDataCache.disableForTestMode(); mTestLooper = new TestLooper(); mContext = spy(ApplicationProvider.getApplicationContext()); diff --git a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java index 746645a8c585..541dbba67957 100644 --- a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java @@ -28,6 +28,7 @@ import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.IDeviceVolumeBehaviorDispatcher; +import android.os.IpcDataCache; import android.os.PermissionEnforcer; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; @@ -69,6 +70,7 @@ public class DeviceVolumeBehaviorTest { @Before public void setUp() throws Exception { + IpcDataCache.disableForTestMode(); mTestLooper = new TestLooper(); mContext = InstrumentationRegistry.getTargetContext(); mAudioSystem = new NoOpAudioSystemAdapter(); diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java index 6b41c434b80f..0bbae247d8bb 100644 --- a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java @@ -80,6 +80,7 @@ import android.media.AudioSystem; import android.media.IDeviceVolumeBehaviorDispatcher; import android.media.VolumeInfo; import android.media.audiopolicy.AudioVolumeGroup; +import android.os.IpcDataCache; import android.os.Looper; import android.os.PermissionEnforcer; import android.os.test.TestLooper; @@ -210,6 +211,8 @@ public class VolumeHelperTest { @Before public void setUp() throws Exception { + IpcDataCache.disableForTestMode(); + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); mTestLooper = new TestLooper(); |