diff options
author | 2025-01-17 17:58:14 -0800 | |
---|---|---|
committer | 2025-01-24 10:30:21 -0800 | |
commit | 4f416c50a7cb590336be31180bf8e3deee2afb4e (patch) | |
tree | 75f4b3b7fbbb0cce424f7e2ab08c01e76cf24b28 | |
parent | 515b50095b02ca1dcc652ae4f120bf116278050c (diff) |
Use cache for volume getter
Using the IpcDataCache to reduce the number of binder calls between
AudioManager and AudioService. From the b/383667500 we see that there
are a lot of calls that can be optimized. We need to investigate whether
the IpcDataCache mechanism is more efficient in this case since the
volumes might get invalidated very often depending on the routing and
mute state.
Flag: android.media.audio.cache_get_stream_volume
Test: atest AudioManagerTest
Bug: 383667500
Change-Id: I8a07d0f728149e2a9bfcaeee81fff60b17ceedb5
4 files changed, 103 insertions, 19 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index aa3dbda374be..71013f7f4e34 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -22,6 +22,7 @@ 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.cacheGetStreamVolume; 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; @@ -1241,7 +1242,12 @@ public class AudioManager { * @hide **/ public static final String VOLUME_MAX_CACHING_API = "getStreamMaxVolume"; - private static final int VOLUME_MIN_MAX_CACHING_SIZE = 16; + /** + * API string for caching the volume for each stream + * @hide + **/ + public static final String VOLUME_CACHING_API = "getStreamVolume"; + private static final int VOLUME_CACHING_SIZE = 16; private final IpcDataCache.QueryHandler<VolumeCacheQuery, Integer> mVolQuery = new IpcDataCache.QueryHandler<>() { @@ -1252,6 +1258,7 @@ public class AudioManager { return switch (query.queryCommand) { case QUERY_VOL_MIN -> service.getStreamMinVolume(query.stream); case QUERY_VOL_MAX -> service.getStreamMaxVolume(query.stream); + case QUERY_VOL -> service.getStreamVolume(query.stream); default -> { Log.w(TAG, "Not a valid volume cache query: " + query); yield null; @@ -1265,29 +1272,40 @@ public class AudioManager { }; private final IpcDataCache<VolumeCacheQuery, Integer> mVolMinCache = - new IpcDataCache<>(VOLUME_MIN_MAX_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM, + new IpcDataCache<>(VOLUME_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, + new IpcDataCache<>(VOLUME_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM, VOLUME_MAX_CACHING_API, VOLUME_MAX_CACHING_API, mVolQuery); + private final IpcDataCache<VolumeCacheQuery, Integer> mVolCache = + new IpcDataCache<>(VOLUME_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM, + VOLUME_CACHING_API, VOLUME_CACHING_API, mVolQuery); + /** * Used to invalidate the cache for the given API * @hide **/ public static void clearVolumeCache(String api) { - if (cacheGetStreamMinMaxVolume()) { + if (cacheGetStreamMinMaxVolume() && (VOLUME_MAX_CACHING_API.equals(api) + || VOLUME_MIN_CACHING_API.equals(api))) { + IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, api); + } else if (cacheGetStreamVolume() && VOLUME_CACHING_API.equals(api)) { IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, api); + } else { + Log.w(TAG, "invalid clearVolumeCache for api " + api); } } private static final int QUERY_VOL_MIN = 1; private static final int QUERY_VOL_MAX = 2; + private static final int QUERY_VOL = 3; /** @hide */ @IntDef(prefix = "QUERY_VOL", value = { QUERY_VOL_MIN, - QUERY_VOL_MAX} + QUERY_VOL_MAX, + QUERY_VOL} ) @Retention(RetentionPolicy.SOURCE) private @interface QueryVolCommand {} @@ -1297,6 +1315,7 @@ public class AudioManager { return switch (queryCommand) { case QUERY_VOL_MIN -> "getStreamMinVolume"; case QUERY_VOL_MAX -> "getStreamMaxVolume"; + case QUERY_VOL -> "getStreamVolume"; default -> "invalid command"; }; } @@ -1373,6 +1392,9 @@ public class AudioManager { * @see #setStreamVolume(int, int, int) */ public int getStreamVolume(int streamType) { + if (cacheGetStreamVolume()) { + return mVolCache.query(new VolumeCacheQuery(streamType, QUERY_VOL)); + } final IAudioService service = getService(); try { return service.getStreamVolume(streamType); diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java index 017a1029d35c..209734ca4a53 100644 --- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java @@ -67,6 +67,17 @@ public class AudioManagerTest { private AudioManager mAudioManager; + private static final int[] PUBLIC_STREAM_TYPES = { + STREAM_VOICE_CALL, + STREAM_SYSTEM, + STREAM_RING, + STREAM_MUSIC, + STREAM_ALARM, + STREAM_NOTIFICATION, + STREAM_DTMF, + STREAM_ACCESSIBILITY, + }; + @Rule public final AudioVolumesTestRule rule = new AudioVolumesTestRule(); @@ -226,18 +237,8 @@ public class AudioManagerTest { 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) { + + for (int streamType : PUBLIC_STREAM_TYPES) { assertEquals(service.getStreamMinVolume(streamType), mAudioManager.getStreamMinVolume(streamType)); assertEquals(service.getStreamMaxVolume(streamType), @@ -245,6 +246,17 @@ public class AudioManagerTest { } } + @Test + public void getStreamVolume_consistentWithAs() throws Exception { + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + IAudioService service = IAudioService.Stub.asInterface(b); + + for (int streamType : PUBLIC_STREAM_TYPES) { + assertEquals(service.getStreamVolume(streamType), + mAudioManager.getStreamVolume(streamType)); + } + } + //----------------------------------------------------------------- // Test Volume per Attributes setter/getters //----------------------------------------------------------------- diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java index c35f4fca6edd..fece7a899f0a 100644 --- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java +++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java @@ -69,6 +69,8 @@ class AudioManagerShellCommand extends ShellCommand { return getMinVolume(); case "get-max-volume": return getMaxVolume(); + case "get-stream-volume": + return getStreamVolume(); case "set-device-volume": return setDeviceVolume(); case "adj-mute": @@ -114,6 +116,8 @@ class AudioManagerShellCommand extends ShellCommand { 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(" get-stream-volume STREAM_TYPE"); + pw.println(" Gets the 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"); @@ -322,6 +326,15 @@ class AudioManagerShellCommand extends ShellCommand { return 0; } + private int getStreamVolume() { + final Context context = mService.mContext; + final AudioManager am = context.getSystemService(AudioManager.class); + final int stream = readIntArg(); + final int result = am.getStreamVolume(stream); + getOutPrintWriter().println("AudioManager.getStreamVolume(" + 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 1baded86705e..ddce8c7051ea 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -50,6 +50,7 @@ import static android.media.AudioManager.STREAM_SYSTEM; 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.cacheGetStreamVolume; import static android.media.audio.Flags.concurrentAudioRecordBypassPermission; import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency; import static android.media.audio.Flags.focusFreezeTestApi; @@ -1890,6 +1891,12 @@ public class AudioService extends IAudioService.Stub mSpatializerHelper.onRoutingUpdated(); } checkMuteAwaitConnection(); + if (cacheGetStreamVolume()) { + if (DEBUG_VOL) { + Log.d(TAG, "Clear volume cache after routing update"); + } + AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API); + } } //----------------------------------------------------------------- @@ -4968,6 +4975,8 @@ public class AudioService extends IAudioService.Stub + concurrentAudioRecordBypassPermission()); pw.println("\tandroid.media.audio.Flags.cacheGetStreamMinMaxVolume:" + cacheGetStreamMinMaxVolume()); + pw.println("\tandroid.media.audio.Flags.cacheGetStreamVolume:" + + cacheGetStreamVolume()); } private void dumpAudioMode(PrintWriter pw) { @@ -7034,6 +7043,13 @@ public class AudioService extends IAudioService.Stub streamState.mIsMuted = false; } } + if (cacheGetStreamVolume()) { + if (DEBUG_VOL) { + Log.d(TAG, + "Clear volume cache after possibly changing mute in readAudioSettings"); + } + AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API); + } } readVolumeGroupsSettings(userSwitch); @@ -9254,11 +9270,23 @@ public class AudioService extends IAudioService.Stub public void put(int key, int value) { super.put(key, value); record("put", key, value); + if (cacheGetStreamVolume()) { + if (DEBUG_VOL) { + Log.d(TAG, "Clear volume cache after update index map"); + } + AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API); + } } @Override public void setValueAt(int index, int value) { super.setValueAt(index, value); record("setValueAt", keyAt(index), value); + if (cacheGetStreamVolume()) { + if (DEBUG_VOL) { + Log.d(TAG, "Clear volume cache after update index map"); + } + AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API); + } } // Record all changes in the VolumeStreamState @@ -9988,8 +10016,9 @@ public class AudioService extends IAudioService.Stub * @return true if the mute state was changed */ public boolean mute(boolean state, boolean apply, String src) { + boolean changed; synchronized (VolumeStreamState.class) { - boolean changed = state != mIsMuted; + changed = state != mIsMuted; if (changed) { sMuteLogger.enqueue( new AudioServiceEvents.StreamMuteEvent(mStreamType, state, src)); @@ -10007,8 +10036,16 @@ public class AudioService extends IAudioService.Stub doMute(); } } - return changed; } + + if (cacheGetStreamVolume() && changed) { + if (DEBUG_VOL) { + Log.d(TAG, "Clear volume cache after changing mute state"); + } + AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API); + } + + return changed; } public void doMute() { |