Merge "AudioService: cache routing queries by stream types"
This commit is contained in:
committed by
Android (Google) Code Review
commit
4fa37e6985
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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):");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user