diff options
4 files changed, 228 insertions, 4 deletions
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 5cffbefbd9a3..2871aa1ffeba 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -171,6 +171,7 @@ static struct { static struct { jmethodID postDynPolicyEventFromNative; jmethodID postRecordConfigEventFromNative; + jmethodID postRoutingUpdatedFromNative; } gAudioPolicyEventHandlerMethods; static struct { jmethodID add; } gListMethods; @@ -539,6 +540,21 @@ android_media_AudioSystem_recording_callback(int event, env->DeleteLocalRef(jEffects); } +static void +android_media_AudioSystem_routing_callback() +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + // callback into java + jclass clazz = env->FindClass(kClassPathName); + + if (env == NULL) { + return; + } + env->CallStaticVoidMethod(clazz, + gAudioPolicyEventHandlerMethods.postRoutingUpdatedFromNative); + env->DeleteLocalRef(clazz); +} + static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address, jstring device_name, jint codec) @@ -1894,6 +1910,12 @@ android_media_AudioSystem_registerRecordingCallback(JNIEnv *env, jobject thiz) AudioSystem::setRecordConfigCallback(android_media_AudioSystem_recording_callback); } +static void +android_media_AudioSystem_registerRoutingCallback(JNIEnv *env, jobject thiz) +{ + AudioSystem::setRoutingCallback(android_media_AudioSystem_routing_callback); +} + static jint convertAudioMixToNative(JNIEnv *env, AudioMix *nAudioMix, @@ -2579,6 +2601,8 @@ static const JNINativeMethod gMethods[] = (void *)android_media_AudioSystem_registerDynPolicyCallback}, {"native_register_recording_callback", "()V", (void *)android_media_AudioSystem_registerRecordingCallback}, + {"native_register_routing_callback", "()V", + (void *)android_media_AudioSystem_registerRoutingCallback}, {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady}, {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB}, {"native_get_offload_support", "(IIIII)I", @@ -2762,6 +2786,9 @@ int register_android_media_AudioSystem(JNIEnv *env) gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative = GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName), "recordingCallbackFromNative", "(IIIIIIZ[I[Landroid/media/audiofx/AudioEffect$Descriptor;[Landroid/media/audiofx/AudioEffect$Descriptor;I)V"); + gAudioPolicyEventHandlerMethods.postRoutingUpdatedFromNative = + GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName), + "routingCallbackFromNative", "()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 164b194996a6..81ef06400521 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -31,6 +31,8 @@ import android.telephony.TelephonyManager; import android.util.Log; import android.util.Pair; +import com.android.internal.annotations.GuardedBy; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -712,6 +714,42 @@ public class AudioSystem } } + /** + * @hide + * Handles events from the audio policy manager about routing events + */ + public interface RoutingUpdateCallback { + /** + * Callback to notify a routing update event occurred + */ + void onRoutingUpdated(); + } + + @GuardedBy("AudioSystem.class") + private static RoutingUpdateCallback sRoutingUpdateCallback; + + /** @hide */ + public static void setRoutingCallback(RoutingUpdateCallback cb) { + synchronized (AudioSystem.class) { + sRoutingUpdateCallback = cb; + native_register_routing_callback(); + } + } + + private static void routingCallbackFromNative() { + final RoutingUpdateCallback cb; + synchronized (AudioSystem.class) { + cb = sRoutingUpdateCallback; + } + //### + Log.i(TAG, "#################### update"); + if (cb == null) { + Log.e(TAG, "routing update from APM was not captured"); + return; + } + cb.onRoutingUpdated(); + } + /* * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...) * Must be kept in sync with frameworks/base/core/jni/android_media_AudioErrors.h @@ -1597,6 +1635,8 @@ public class AudioSystem private static native final void native_register_dynamic_policy_callback(); // declare this instance as having a recording configuration update callback handler 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(); // 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 ada67b1d59af..2ab5aa616748 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -3467,6 +3467,8 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#getStreamVolume(int) */ public int getStreamVolume(int streamType) { ensureValidStreamType(streamType); + Log.e(TAG, "AudioSystem.getDevicesForStream In AudioService from u/pid" + + Binder.getCallingUid() + "/" + Binder.getCallingPid()); int device = getDeviceForStream(streamType); synchronized (VolumeStreamState.class) { int index = mStreamStates[streamType].getIndex(device); @@ -7951,6 +7953,8 @@ public class AudioService extends IAudioService.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + mAudioSystem.dump(pw); + sLifecycleLogger.dump(pw); if (mAudioHandler != null) { pw.println("\nMessage handler (watch for unhandled messages):"); diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index 891c713d30fb..ff84505e59a9 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -21,9 +21,13 @@ import android.media.AudioAttributes; import android.media.AudioDeviceAttributes; import android.media.AudioSystem; import android.media.audiopolicy.AudioMix; +import android.os.SystemClock; +import android.util.Log; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; /** * Provides an adapter to access functionality of the android.media.AudioSystem class for device @@ -31,15 +35,79 @@ import java.util.List; * 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 { +public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { + + private static final String TAG = "AudioSystemAdapter"; + + // initialized in factory getDefaultAdapter() + private static AudioSystemAdapter sSingletonDefaultAdapter; + + /** + * should be false by default unless enabling measurements of method call counts and time spent + * in measured methods + */ + private static final boolean ENABLE_GETDEVICES_STATS = false; + private static final int NB_MEASUREMENTS = 2; + private static final int METHOD_GETDEVICESFORSTREAM = 0; + private static final int METHOD_GETDEVICESFORATTRIBUTES = 1; + private long[] mMethodTimeNs; + private int[] mMethodCallCounter; + private String[] mMethodNames = {"getDevicesForStream", "getDevicesForAttributes"}; + + private static final boolean USE_CACHE_FOR_GETDEVICES = true; + private ConcurrentHashMap<Integer, Integer> mDevicesForStreamCache; + private int[] mMethodCacheHit; + + /** + * should be false except when trying to debug caching errors. When true, the value retrieved + * from the cache will be compared against the real queried value, which defeats the purpose of + * the cache in terms of performance. + */ + private static final boolean DEBUG_CACHE = false; + + /** + * Implementation of AudioSystem.RoutingUpdateCallback + */ + @Override + public void onRoutingUpdated() { + if (DEBUG_CACHE) { + Log.d(TAG, "---- onRoutingUpdated (from native) ----------"); + } + invalidateRoutingCache(); + } /** * Create a wrapper around the {@link AudioSystem} static methods, all functions are directly * forwarded to the AudioSystem class. * @return an adapter around AudioSystem */ - static final @NonNull AudioSystemAdapter getDefaultAdapter() { - return new AudioSystemAdapter(); + static final synchronized @NonNull AudioSystemAdapter getDefaultAdapter() { + if (sSingletonDefaultAdapter == null) { + sSingletonDefaultAdapter = new AudioSystemAdapter(); + AudioSystem.setRoutingCallback(sSingletonDefaultAdapter); + if (USE_CACHE_FOR_GETDEVICES) { + sSingletonDefaultAdapter.mDevicesForStreamCache = + new ConcurrentHashMap<Integer, Integer>(AudioSystem.getNumStreamTypes()); + sSingletonDefaultAdapter.mMethodCacheHit = new int[NB_MEASUREMENTS]; + } + if (ENABLE_GETDEVICES_STATS) { + sSingletonDefaultAdapter.mMethodCallCounter = new int[NB_MEASUREMENTS]; + sSingletonDefaultAdapter.mMethodTimeNs = new long[NB_MEASUREMENTS]; + } + } + return sSingletonDefaultAdapter; + } + + private void invalidateRoutingCache() { + if (DEBUG_CACHE) { + Log.d(TAG, "---- clearing cache ----------"); + } + if (mDevicesForStreamCache == null) { + return; + } + synchronized (mDevicesForStreamCache) { + mDevicesForStreamCache.clear(); + } } /** @@ -48,6 +116,44 @@ public class AudioSystemAdapter { * @return a mask of device types */ public int getDevicesForStream(int stream) { + if (!ENABLE_GETDEVICES_STATS) { + return getDevicesForStreamImpl(stream); + } + mMethodCallCounter[METHOD_GETDEVICESFORSTREAM]++; + final long startTime = SystemClock.uptimeNanos(); + final int res = getDevicesForStreamImpl(stream); + mMethodTimeNs[METHOD_GETDEVICESFORSTREAM] += SystemClock.uptimeNanos() - startTime; + return res; + } + + private int getDevicesForStreamImpl(int stream) { + if (USE_CACHE_FOR_GETDEVICES) { + Integer res; + synchronized (mDevicesForStreamCache) { + res = mDevicesForStreamCache.get(stream); + if (res == null) { + res = AudioSystem.getDevicesForStream(stream); + mDevicesForStreamCache.put(stream, res); + if (DEBUG_CACHE) { + Log.d(TAG, " stream=" + stream + " dev=0x" + Integer.toHexString(res)); + } + return res; + } + // cache hit + mMethodCacheHit[METHOD_GETDEVICESFORSTREAM]++; + if (DEBUG_CACHE) { + final int real = AudioSystem.getDevicesForStream(stream); + if (res == real) { + Log.d(TAG, " stream=" + stream + " dev=0x" + Integer.toHexString(res) + + " CACHE"); + } else { + Log.e(TAG, " stream=" + stream + " dev=0x" + Integer.toHexString(res) + + " CACHE ERROR real dev=0x" + Integer.toHexString(real)); + } + } + } + return res; + } return AudioSystem.getDevicesForStream(stream); } @@ -58,6 +164,19 @@ public class AudioSystemAdapter { */ public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes( @NonNull AudioAttributes attributes) { + if (!ENABLE_GETDEVICES_STATS) { + return getDevicesForAttributesImpl(attributes); + } + mMethodCallCounter[METHOD_GETDEVICESFORATTRIBUTES]++; + final long startTime = SystemClock.uptimeNanos(); + final ArrayList<AudioDeviceAttributes> res = getDevicesForAttributesImpl(attributes); + mMethodTimeNs[METHOD_GETDEVICESFORATTRIBUTES] += SystemClock.uptimeNanos() - startTime; + return res; + } + + private @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesImpl( + @NonNull AudioAttributes attributes) { + // TODO implement caching for attributes-based routing return AudioSystem.getDevicesForAttributes(attributes); } @@ -72,6 +191,7 @@ public class AudioSystemAdapter { */ public int setDeviceConnectionState(int device, int state, String deviceAddress, String deviceName, int codecFormat) { + invalidateRoutingCache(); return AudioSystem.setDeviceConnectionState(device, state, deviceAddress, deviceName, codecFormat); } @@ -96,6 +216,7 @@ public class AudioSystemAdapter { */ public int handleDeviceConfigChange(int device, String deviceAddress, String deviceName, int codecFormat) { + invalidateRoutingCache(); return AudioSystem.handleDeviceConfigChange(device, deviceAddress, deviceName, codecFormat); } @@ -109,6 +230,7 @@ public class AudioSystemAdapter { */ public int setDevicesRoleForStrategy(int strategy, int role, @NonNull List<AudioDeviceAttributes> devices) { + invalidateRoutingCache(); return AudioSystem.setDevicesRoleForStrategy(strategy, role, devices); } @@ -119,6 +241,7 @@ public class AudioSystemAdapter { * @return */ public int removeDevicesRoleForStrategy(int strategy, int role) { + invalidateRoutingCache(); return AudioSystem.removeDevicesRoleForStrategy(strategy, role); } @@ -131,11 +254,12 @@ public class AudioSystemAdapter { */ public int setDevicesRoleForCapturePreset(int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { + invalidateRoutingCache(); return AudioSystem.setDevicesRoleForCapturePreset(capturePreset, role, devices); } /** - * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int)} + * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, int[], String[])} * @param capturePreset * @param role * @param devicesToRemove @@ -143,6 +267,7 @@ public class AudioSystemAdapter { */ public int removeDevicesRoleForCapturePreset( int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) { + invalidateRoutingCache(); return AudioSystem.removeDevicesRoleForCapturePreset(capturePreset, role, devicesToRemove); } @@ -153,6 +278,7 @@ public class AudioSystemAdapter { * @return */ public int clearDevicesRoleForCapturePreset(int capturePreset, int role) { + invalidateRoutingCache(); return AudioSystem.clearDevicesRoleForCapturePreset(capturePreset, role); } @@ -218,6 +344,7 @@ public class AudioSystemAdapter { * @return */ public int setPhoneState(int state, int uid) { + invalidateRoutingCache(); return AudioSystem.setPhoneState(state, uid); } @@ -238,6 +365,7 @@ public class AudioSystemAdapter { * @return */ public int setForceUse(int usage, int config) { + invalidateRoutingCache(); return AudioSystem.setForceUse(usage, config); } @@ -257,6 +385,7 @@ public class AudioSystemAdapter { * @return */ public int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register) { + invalidateRoutingCache(); return AudioSystem.registerPolicyMixes(mixes, register); } @@ -268,6 +397,7 @@ public class AudioSystemAdapter { * @return */ public int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) { + invalidateRoutingCache(); return AudioSystem.setUidDeviceAffinities(uid, types, addresses); } @@ -277,6 +407,7 @@ public class AudioSystemAdapter { * @return */ public int removeUidDeviceAffinities(int uid) { + invalidateRoutingCache(); return AudioSystem.removeUidDeviceAffinities(uid); } @@ -289,6 +420,7 @@ public class AudioSystemAdapter { */ public int setUserIdDeviceAffinities(int userId, @NonNull int[] types, @NonNull String[] addresses) { + invalidateRoutingCache(); return AudioSystem.setUserIdDeviceAffinities(userId, types, addresses); } @@ -298,6 +430,27 @@ public class AudioSystemAdapter { * @return */ public int removeUserIdDeviceAffinities(int userId) { + invalidateRoutingCache(); return AudioSystem.removeUserIdDeviceAffinities(userId); } + + /** + * Part of AudioService dump + * @param pw + */ + public void dump(PrintWriter pw) { + if (!ENABLE_GETDEVICES_STATS) { + // only stats in this dump + return; + } + pw.println("\nAudioSystemAdapter:"); + for (int i = 0; i < NB_MEASUREMENTS; i++) { + pw.println(mMethodNames[i] + + ": counter=" + mMethodCallCounter[i] + + " time(ms)=" + (mMethodTimeNs[i] / 1E6) + + (USE_CACHE_FOR_GETDEVICES + ? (" FScacheHit=" + mMethodCacheHit[METHOD_GETDEVICESFORSTREAM]) : "")); + } + pw.println("\n"); + } } |