From a44a5f917705e2973da8411630ba8eed3fc06825 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Wed, 20 Apr 2022 02:41:51 +0000 Subject: [PATCH] AudioService: reset volume ranges when requested by APM Implement callback from AudioPolicy to resend volume ranges when requested, after an error in volume min/max indices was detected by AudioPolicyManager Bug: 206966260 Test: adb shell dumpsys audio | grep MSG_VOL_RANGE_INIT_REQ Change-Id: Ic257f8007c2908e3c5e6c648cca7598993e8f93e --- core/jni/android_media_AudioSystem.cpp | 25 ++++++++++++ media/java/android/media/AudioSystem.java | 36 +++++++++++++++++ .../android/server/audio/AudioService.java | 14 ++++++- .../server/audio/AudioSystemAdapter.java | 39 ++++++++++++++++++- 4 files changed, 112 insertions(+), 2 deletions(-) diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index e7817606aae9f..b6fbe206a262d 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -180,6 +180,7 @@ static struct { jmethodID postDynPolicyEventFromNative; jmethodID postRecordConfigEventFromNative; jmethodID postRoutingUpdatedFromNative; + jmethodID postVolRangeInitReqFromNative; } gAudioPolicyEventHandlerMethods; jclass gListClass; @@ -585,6 +586,20 @@ android_media_AudioSystem_routing_callback() env->DeleteLocalRef(clazz); } +static void android_media_AudioSystem_vol_range_init_req_callback() +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (env == NULL) { + return; + } + + // callback into java + jclass clazz = env->FindClass(kClassPathName); + env->CallStaticVoidMethod(clazz, + gAudioPolicyEventHandlerMethods.postVolRangeInitReqFromNative); + env->DeleteLocalRef(clazz); +} + static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint state, jobject jParcel, jint codec) { @@ -2063,6 +2078,11 @@ android_media_AudioSystem_registerRoutingCallback(JNIEnv *env, jobject thiz) AudioSystem::setRoutingCallback(android_media_AudioSystem_routing_callback); } +static void android_media_AudioSystem_registerVolRangeInitReqCallback(JNIEnv *env, jobject thiz) +{ + AudioSystem::setVolInitReqCallback(android_media_AudioSystem_vol_range_init_req_callback); +} + void javaAudioFormatToNativeAudioConfig(JNIEnv *env, audio_config_t *nConfig, const jobject jFormat, bool isInput) { *nConfig = AUDIO_CONFIG_INITIALIZER; @@ -2990,6 +3010,8 @@ static const JNINativeMethod gMethods[] = (void *)android_media_AudioSystem_registerRecordingCallback}, {"native_register_routing_callback", "()V", (void *)android_media_AudioSystem_registerRoutingCallback}, + {"native_register_vol_range_init_req_callback", "()V", + (void *)android_media_AudioSystem_registerVolRangeInitReqCallback}, {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady}, {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB}, {"native_get_offload_support", "(IIIII)I", @@ -3202,6 +3224,9 @@ int register_android_media_AudioSystem(JNIEnv *env) gAudioPolicyEventHandlerMethods.postRoutingUpdatedFromNative = GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName), "routingCallbackFromNative", "()V"); + gAudioPolicyEventHandlerMethods.postVolRangeInitReqFromNative = + GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName), + "volRangeInitReqCallbackFromNative", "()V"); jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix"); gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass); diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index acb34b527e068..955bfcc902a41 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -828,6 +828,40 @@ public class AudioSystem cb.onRoutingUpdated(); } + /** + * @hide + * Handles requests from the audio policy manager to (re-)initialize the volume ranges + */ + public interface VolumeRangeInitRequestCallback { + /** + * Callback to notify volume ranges need to be initialized + */ + void onVolumeRangeInitializationRequested(); + } + + @GuardedBy("AudioSystem.class") + private static VolumeRangeInitRequestCallback sVolRangeInitReqCallback; + + /** @hide */ + public static void setVolumeRangeInitRequestCallback(VolumeRangeInitRequestCallback cb) { + synchronized (AudioSystem.class) { + sVolRangeInitReqCallback = cb; + native_register_vol_range_init_req_callback(); + } + } + + private static void volRangeInitReqCallbackFromNative() { + final VolumeRangeInitRequestCallback cb; + synchronized (AudioSystem.class) { + cb = sVolRangeInitReqCallback; + } + if (cb == null) { + Log.e(TAG, "APM requested volume range initialization, but no callback found"); + return; + } + cb.onVolumeRangeInitializationRequested(); + } + /* * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...) * Must be kept in sync with frameworks/base/core/jni/android_media_AudioErrors.h @@ -1812,6 +1846,8 @@ public class AudioSystem private static native final void native_register_recording_callback(); // declare this instance as having a routing update callback handler private static native void native_register_routing_callback(); + // declare this instance as having a volume range init request handler + private static native void native_register_vol_range_init_req_callback(); // must be kept in sync with value in include/system/audio.h /** @hide */ public static final int AUDIO_HW_SYNC_INVALID = 0; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 1357ed244d10a..1f3fbff34536c 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -223,7 +223,8 @@ import java.util.stream.Collectors; public class AudioService extends IAudioService.Stub implements AccessibilityManager.TouchExplorationStateChangeListener, AccessibilityManager.AccessibilityServicesStateChangeListener, - AudioSystemAdapter.OnRoutingUpdatedListener { + AudioSystemAdapter.OnRoutingUpdatedListener, + AudioSystemAdapter.OnVolRangeInitRequestListener { private static final String TAG = "AS.AudioService"; @@ -1137,6 +1138,9 @@ public class AudioService extends IAudioService.Stub // monitor routing updates coming from native mAudioSystem.setRoutingListener(this); + // monitor requests for volume range initialization coming from native (typically when + // errors are found by AudioPolicyManager + mAudioSystem.setVolRangeInitReqListener(this); // done with service initialization, continue additional work in our Handler thread queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_STREAMS_VOLUMES, @@ -1357,6 +1361,14 @@ public class AudioService extends IAudioService.Stub checkMuteAwaitConnection(); } + //----------------------------------------------------------------- + // monitoring requests for volume range initialization + @Override // AudioSystemAdapter.OnVolRangeInitRequestListener + public void onVolumeRangeInitRequestFromNative() { + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_REPLACE, 0, 0, + "onVolumeRangeInitRequestFromNative" /*obj: caller, for dumpsys*/, /*delay*/ 0); + } + //----------------------------------------------------------------- RoleObserver mRoleObserver; diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index 1429b3cf20411..758a4ec96288b 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -40,7 +40,8 @@ import java.util.concurrent.ConcurrentHashMap; * Use the "real" AudioSystem through the default adapter. * Use the "always ok" adapter to avoid dealing with the APM behaviors during a test. */ -public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { +public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback, + AudioSystem.VolumeRangeInitRequestCallback { private static final String TAG = "AudioSystemAdapter"; @@ -65,6 +66,9 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { private static final Object sRoutingListenerLock = new Object(); @GuardedBy("sRoutingListenerLock") private static @Nullable OnRoutingUpdatedListener sRoutingListener; + private static final Object sVolRangeInitReqListenerLock = new Object(); + @GuardedBy("sVolRangeInitReqListenerLock") + private static @Nullable OnVolRangeInitRequestListener sVolRangeInitReqListener; /** * should be false except when trying to debug caching errors. When true, the value retrieved @@ -101,6 +105,30 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { } } + /** + * Implementation of AudioSystem.VolumeRangeInitRequestCallback + */ + @Override + public void onVolumeRangeInitializationRequested() { + final OnVolRangeInitRequestListener listener; + synchronized (sVolRangeInitReqListenerLock) { + listener = sVolRangeInitReqListener; + } + if (listener != null) { + listener.onVolumeRangeInitRequestFromNative(); + } + } + + interface OnVolRangeInitRequestListener { + void onVolumeRangeInitRequestFromNative(); + } + + static void setVolRangeInitReqListener(@Nullable OnVolRangeInitRequestListener listener) { + synchronized (sVolRangeInitReqListenerLock) { + sVolRangeInitReqListener = listener; + } + } + /** * Create a wrapper around the {@link AudioSystem} static methods, all functions are directly * forwarded to the AudioSystem class. @@ -110,6 +138,7 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { if (sSingletonDefaultAdapter == null) { sSingletonDefaultAdapter = new AudioSystemAdapter(); AudioSystem.setRoutingCallback(sSingletonDefaultAdapter); + AudioSystem.setVolumeRangeInitRequestCallback(sSingletonDefaultAdapter); if (USE_CACHE_FOR_GETDEVICES) { sSingletonDefaultAdapter.mDevicesForAttrCache = new ConcurrentHashMap<>(AudioSystem.getNumStreamTypes()); @@ -160,7 +189,15 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { synchronized (mDevicesForAttrCache) { res = mDevicesForAttrCache.get(key); if (res == null) { + // result from AudioSystem guaranteed non-null, but could be invalid + // if there is a failure to talk to APM res = AudioSystem.getDevicesForAttributes(attributes, forVolume); + if (res.size() > 1 && res.get(0) != null + && res.get(0).getInternalType() == AudioSystem.DEVICE_NONE) { + Log.e(TAG, "unable to get devices for " + attributes); + // return now, do not put invalid value in cache + return res; + } mDevicesForAttrCache.put(key, res); if (DEBUG_CACHE) { Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES]