Merge "AudioService: cache routing queries by stream types"

This commit is contained in:
TreeHugger Robot
2020-12-23 19:57:11 +00:00
committed by Android (Google) Code Review
4 changed files with 228 additions and 4 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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):");

View File

@@ -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");
}
}