diff --git a/api/system-current.txt b/api/system-current.txt index 741b67228c4e1..28cfe49316a35 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4389,6 +4389,7 @@ package android.media.audiofx { public class AudioEffect { ctor @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public AudioEffect(@NonNull java.util.UUID, @NonNull android.media.AudioDeviceAttributes); + method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static boolean isEffectSupportedForDevice(@NonNull java.util.UUID, @NonNull android.media.AudioDeviceAttributes); } } diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java index 2587350b68304..42635216e98f3 100644 --- a/media/java/android/media/audiofx/AudioEffect.java +++ b/media/java/android/media/audiofx/AudioEffect.java @@ -477,13 +477,21 @@ public class AudioEffect { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS) public AudioEffect(@NonNull UUID uuid, @NonNull AudioDeviceAttributes device) { - this(EFFECT_TYPE_NULL, Objects.requireNonNull(uuid), 0, -2, Objects.requireNonNull(device)); + this(EFFECT_TYPE_NULL, Objects.requireNonNull(uuid), + 0, -2, Objects.requireNonNull(device)); } private AudioEffect(UUID type, UUID uuid, int priority, int audioSession, @Nullable AudioDeviceAttributes device) throws IllegalArgumentException, UnsupportedOperationException, RuntimeException { + this(type, uuid, priority, audioSession, device, false); + } + + private AudioEffect(UUID type, UUID uuid, int priority, + int audioSession, @Nullable AudioDeviceAttributes device, boolean probe) + throws IllegalArgumentException, UnsupportedOperationException, + RuntimeException { int[] id = new int[1]; Descriptor[] desc = new Descriptor[1]; @@ -498,7 +506,7 @@ public class AudioEffect { int initResult = native_setup(new WeakReference(this), type.toString(), uuid.toString(), priority, audioSession, deviceType, deviceAddress, - id, desc, ActivityThread.currentOpPackageName()); + id, desc, ActivityThread.currentOpPackageName(), probe); if (initResult != SUCCESS && initResult != ALREADY_EXISTS) { Log.e(TAG, "Error code " + initResult + " when initializing AudioEffect."); @@ -517,8 +525,33 @@ public class AudioEffect { } mId = id[0]; mDescriptor = desc[0]; - synchronized (mStateLock) { - mState = STATE_INITIALIZED; + if (!probe) { + synchronized (mStateLock) { + mState = STATE_INITIALIZED; + } + } + } + + /** + * Checks if an AudioEffect identified by the supplied uuid can be attached + * to an audio device described by the supplied AudioDeviceAttributes. + * @param uuid unique identifier of a particular effect implementation. + * @param device the device the effect would be attached to. + * @return true if possible, false otherwise. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS) + public static boolean isEffectSupportedForDevice( + @NonNull UUID uuid, @NonNull AudioDeviceAttributes device) { + try { + AudioEffect fx = new AudioEffect( + EFFECT_TYPE_NULL, Objects.requireNonNull(uuid), + 0, -2, Objects.requireNonNull(device), true); + fx.release(); + return true; + } catch (Exception e) { + return false; } } @@ -1340,7 +1373,7 @@ public class AudioEffect { private native final int native_setup(Object audioeffect_this, String type, String uuid, int priority, int audioSession, int deviceType, String deviceAddress, int[] id, Object[] desc, - String opPackageName); + String opPackageName, boolean probe); private native final void native_finalize(); diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp index 007dd10aba1f9..dbe7b4b619c93 100644 --- a/media/jni/audioeffect/android_media_AudioEffect.cpp +++ b/media/jni/audioeffect/android_media_AudioEffect.cpp @@ -270,7 +270,7 @@ static jint android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, jstring type, jstring uuid, jint priority, jint sessionId, jint deviceType, jstring deviceAddress, - jintArray jId, jobjectArray javadesc, jstring opPackageName) + jintArray jId, jobjectArray javadesc, jstring opPackageName, jboolean probe) { ALOGV("android_media_AudioEffect_native_setup"); AudioEffectJniStorage* lpJniStorage = NULL; @@ -345,12 +345,14 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t &lpJniStorage->mCallbackData, (audio_session_t) sessionId, AUDIO_IO_HANDLE_NONE, - device); + device, + probe); if (lpAudioEffect == 0) { ALOGE("Error creating AudioEffect"); goto setup_failure; } + lStatus = AudioEffectJni::translateNativeErrorToJava(lpAudioEffect->initCheck()); if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) { ALOGE("AudioEffect initCheck failed %d", lStatus); @@ -387,7 +389,13 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t env->SetObjectArrayElement(javadesc, 0, jdesc); env->DeleteLocalRef(jdesc); - setAudioEffect(env, thiz, lpAudioEffect); + // In probe mode, release the native object and clear our strong reference + // to force all method calls from JAVA to be rejected. + if (probe) { + setAudioEffect(env, thiz, 0); + } else { + setAudioEffect(env, thiz, lpAudioEffect); + } env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage); @@ -766,7 +774,7 @@ android_media_AudioEffect_native_queryPreProcessings(JNIEnv *env, jclass clazz _ // Dalvik VM type signatures static const JNINativeMethod gMethods[] = { {"native_init", "()V", (void *)android_media_AudioEffect_native_init}, - {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIILjava/lang/String;[I[Ljava/lang/Object;Ljava/lang/String;)I", + {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIILjava/lang/String;[I[Ljava/lang/Object;Ljava/lang/String;Z)I", (void *)android_media_AudioEffect_native_setup}, {"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize}, {"native_release", "()V", (void *)android_media_AudioEffect_native_release},