audioflinger: add effect creation probe mode

Add an argument to IAudioFlinger::createEffect() API to
ask AudioFlinger to just run the pre flight checks but not
create the actual audio effect instance and allocate resources.
This is the basis of an API for apps to query if a given
effect can be created without having to allocate the resources
and risk an exception when calling the constructor.

Bug: 150699608
Test: CTS and GTS Tests for audio effects
Change-Id: Ibdda22fd945c88c33e3c7342a7a5ed3e02d399ac
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index a1b141b..c183ab0 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -36,7 +36,7 @@
 // ---------------------------------------------------------------------------
 
 AudioEffect::AudioEffect(const String16& opPackageName)
-    : mStatus(NO_INIT), mOpPackageName(opPackageName)
+    : mStatus(NO_INIT), mProbe(false), mOpPackageName(opPackageName)
 {
 }
 
@@ -49,12 +49,13 @@
                 void* user,
                 audio_session_t sessionId,
                 audio_io_handle_t io,
-                const AudioDeviceTypeAddr& device
+                const AudioDeviceTypeAddr& device,
+                bool probe
                 )
-    : mStatus(NO_INIT), mOpPackageName(opPackageName)
+    : mStatus(NO_INIT), mProbe(false), mOpPackageName(opPackageName)
 {
     AutoMutex lock(mConstructLock);
-    mStatus = set(type, uuid, priority, cbf, user, sessionId, io, device);
+    mStatus = set(type, uuid, priority, cbf, user, sessionId, io, device, probe);
 }
 
 AudioEffect::AudioEffect(const char *typeStr,
@@ -65,9 +66,10 @@
                 void* user,
                 audio_session_t sessionId,
                 audio_io_handle_t io,
-                const AudioDeviceTypeAddr& device
+                const AudioDeviceTypeAddr& device,
+                bool probe
                 )
-    : mStatus(NO_INIT), mOpPackageName(opPackageName)
+    : mStatus(NO_INIT), mProbe(false), mOpPackageName(opPackageName)
 {
     effect_uuid_t type;
     effect_uuid_t *pType = NULL;
@@ -89,7 +91,7 @@
     }
 
     AutoMutex lock(mConstructLock);
-    mStatus = set(pType, pUuid, priority, cbf, user, sessionId, io, device);
+    mStatus = set(pType, pUuid, priority, cbf, user, sessionId, io, device, probe);
 }
 
 status_t AudioEffect::set(const effect_uuid_t *type,
@@ -99,7 +101,8 @@
                 void* user,
                 audio_session_t sessionId,
                 audio_io_handle_t io,
-                const AudioDeviceTypeAddr& device)
+                const AudioDeviceTypeAddr& device,
+                bool probe)
 {
     sp<IEffect> iEffect;
     sp<IMemory> cblk;
@@ -126,7 +129,7 @@
         ALOGW("Must specify at least type or uuid");
         return BAD_VALUE;
     }
-
+    mProbe = probe;
     mPriority = priority;
     mCbf = cbf;
     mUserData = user;
@@ -142,15 +145,18 @@
 
     iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor,
             mIEffectClient, priority, io, mSessionId, device, mOpPackageName, mClientPid,
-            &mStatus, &mId, &enabled);
+            probe, &mStatus, &mId, &enabled);
 
-    if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
+    // In probe mode, we stop here and return the status: the IEffect interface to
+    // audio flinger will not be retained. initCheck() will return the creation status
+    // but all other APIs will return invalid operation.
+    if (probe || iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
         char typeBuffer[64], uuidBuffer[64];
         guidToString(type, typeBuffer, sizeof(typeBuffer));
         guidToString(uuid, uuidBuffer, sizeof(uuidBuffer));
-        ALOGE("set(): AudioFlinger could not create effect %s / %s, status: %d",
+        ALOGE_IF(!probe, "set(): AudioFlinger could not create effect %s / %s, status: %d",
                 typeBuffer, uuidBuffer, mStatus);
-        if (iEffect == 0) {
+        if (!probe && iEffect == 0) {
             mStatus = NO_INIT;
         }
         return mStatus;
@@ -191,7 +197,7 @@
 {
     ALOGV("Destructor %p", this);
 
-    if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) {
+    if (!mProbe && (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS)) {
         if (!audio_is_global_session(mSessionId)) {
             AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
         }
@@ -201,9 +207,9 @@
         }
         mIEffect.clear();
         mCblkMemory.clear();
-        mIEffectClient.clear();
-        IPCThreadState::self()->flushCommands();
     }
+    mIEffectClient.clear();
+    IPCThreadState::self()->flushCommands();
 }
 
 
@@ -226,6 +232,9 @@
 
 status_t AudioEffect::setEnabled(bool enabled)
 {
+    if (mProbe) {
+        return INVALID_OPERATION;
+    }
     if (mStatus != NO_ERROR) {
         return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
     }
@@ -254,6 +263,9 @@
                               uint32_t *replySize,
                               void *replyData)
 {
+    if (mProbe) {
+        return INVALID_OPERATION;
+    }
     if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
         ALOGV("command() bad status %d", mStatus);
         return mStatus;
@@ -287,6 +299,9 @@
 
 status_t AudioEffect::setParameter(effect_param_t *param)
 {
+    if (mProbe) {
+        return INVALID_OPERATION;
+    }
     if (mStatus != NO_ERROR) {
         return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
     }
@@ -307,6 +322,9 @@
 
 status_t AudioEffect::setParameterDeferred(effect_param_t *param)
 {
+    if (mProbe) {
+        return INVALID_OPERATION;
+    }
     if (mStatus != NO_ERROR) {
         return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
     }
@@ -333,6 +351,9 @@
 
 status_t AudioEffect::setParameterCommit()
 {
+    if (mProbe) {
+        return INVALID_OPERATION;
+    }
     if (mStatus != NO_ERROR) {
         return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
     }
@@ -347,6 +368,9 @@
 
 status_t AudioEffect::getParameter(effect_param_t *param)
 {
+    if (mProbe) {
+        return INVALID_OPERATION;
+    }
     if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
         return mStatus;
     }
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 513da2b..16d2232 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -662,6 +662,7 @@
                                     const AudioDeviceTypeAddr& device,
                                     const String16& opPackageName,
                                     pid_t pid,
+                                    bool probe,
                                     status_t *status,
                                     int *id,
                                     int *enabled)
@@ -689,6 +690,7 @@
         }
         data.writeString16(opPackageName);
         data.writeInt32((int32_t) pid);
+        data.writeInt32(probe ? 1 : 0);
 
         status_t lStatus = remote()->transact(CREATE_EFFECT, data, &reply);
         if (lStatus != NO_ERROR) {
@@ -1395,12 +1397,13 @@
             }
             const String16 opPackageName = data.readString16();
             pid_t pid = (pid_t)data.readInt32();
+            bool probe = data.readInt32() == 1;
 
             int id = 0;
             int enabled = 0;
 
             sp<IEffect> effect = createEffect(&desc, client, priority, output, sessionId, device,
-                    opPackageName, pid, &status, &id, &enabled);
+                    opPackageName, pid, probe, &status, &id, &enabled);
             reply->writeInt32(status);
             reply->writeInt32(id);
             reply->writeInt32(enabled);
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index eec9dfc..cb76252 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -365,6 +365,10 @@
      * device: An audio device descriptor. Only used when "sessionID" is AUDIO_SESSION_DEVICE.
      *         Specifies the audio device type and address the effect must be attached to.
      *         If "sessionID" is AUDIO_SESSION_DEVICE then "io" must be AUDIO_IO_HANDLE_NONE.
+     * probe: true if created in a degraded mode to only verify if effect creation is possible.
+     *        In this mode, no IEffect interface to AudioFlinger is created and all actions
+     *        besides getters implemented in client AudioEffect object are no ops
+     *        after effect creation.
      */
 
     AudioEffect(const effect_uuid_t *type,
@@ -375,8 +379,8 @@
                 void* user = NULL,
                 audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
                 audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
-                const AudioDeviceTypeAddr& device = {}
-                );
+                const AudioDeviceTypeAddr& device = {},
+                bool probe = false);
 
     /* Constructor.
      *      Same as above but with type and uuid specified by character strings
@@ -389,8 +393,8 @@
                     void* user = NULL,
                     audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
                     audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
-                    const AudioDeviceTypeAddr& device = {}
-                    );
+                    const AudioDeviceTypeAddr& device = {},
+                    bool probe = false);
 
     /* Terminates the AudioEffect and unregisters it from AudioFlinger.
      * The effect engine is also destroyed if this AudioEffect was the last controlling
@@ -412,8 +416,8 @@
                             void* user = NULL,
                             audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
                             audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
-                            const AudioDeviceTypeAddr& device = {}
-                            );
+                            const AudioDeviceTypeAddr& device = {},
+                            bool probe = false);
 
     /* Result of constructing the AudioEffect. This must be checked
      * before using any AudioEffect API.
@@ -547,6 +551,8 @@
      audio_session_t         mSessionId;         // audio session ID
      int32_t                 mPriority;          // priority for effect control
      status_t                mStatus;            // effect status
+     bool                    mProbe;             // effect created in probe mode: all commands
+                                                 // are no ops because mIEffect is NULL
      effect_callback_t       mCbf;               // callback function for status, control and
                                                  // parameter changes notifications
      void*                   mUserData;          // client context for callback function
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index f4ce5d7..c9d9716 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -476,6 +476,7 @@
                                     const AudioDeviceTypeAddr& device,
                                     const String16& callingPackage,
                                     pid_t pid,
+                                    bool probe,
                                     status_t *status,
                                     int *id,
                                     int *enabled) = 0;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 74a09d1..ecda56b 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -3375,6 +3375,7 @@
         const AudioDeviceTypeAddr& device,
         const String16& opPackageName,
         pid_t pid,
+        bool probe,
         status_t *status,
         int *id,
         int *enabled)
@@ -3490,10 +3491,10 @@
 
         if (sessionId == AUDIO_SESSION_DEVICE) {
             sp<Client> client = registerPid(pid);
-            ALOGV("%s device type %d address %s", __func__, device.mType, device.getAddress());
+            ALOGV("%s device type %#x address %s", __func__, device.mType, device.getAddress());
             handle = mDeviceEffectManager.createEffect_l(
                     &desc, device, client, effectClient, mPatchPanel.patches_l(),
-                    enabled, &lStatus);
+                    enabled, &lStatus, probe);
             if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
                 // remove local strong reference to Client with mClientLock held
                 Mutex::Autolock _cl(mClientLock);
@@ -3588,7 +3589,7 @@
         // create effect on selected output thread
         bool pinned = !audio_is_global_session(sessionId) && isSessionAcquired_l(sessionId);
         handle = thread->createEffect_l(client, effectClient, priority, sessionId,
-                &desc, enabled, &lStatus, pinned);
+                &desc, enabled, &lStatus, pinned, probe);
         if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
             // remove local strong reference to Client with mClientLock held
             Mutex::Autolock _cl(mClientLock);
@@ -3600,7 +3601,7 @@
     }
 
 Register:
-    if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) {
+    if (!probe && (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS)) {
         // Check CPU and memory usage
         sp<EffectBase> effect = handle->effect().promote();
         if (effect != nullptr) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 6d7bf3c..40519b0 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -238,6 +238,7 @@
                         const AudioDeviceTypeAddr& device,
                         const String16& opPackageName,
                         pid_t pid,
+                        bool probe,
                         status_t *status /*non-NULL*/,
                         int *id,
                         int *enabled);
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
index 87a4c6e..a3c3b84 100644
--- a/services/audioflinger/DeviceEffectManager.cpp
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -74,13 +74,14 @@
         const sp<IEffectClient>& effectClient,
         const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches,
         int *enabled,
-        status_t *status) {
+        status_t *status,
+        bool probe) {
     sp<DeviceEffectProxy> effect;
     sp<EffectHandle> handle;
     status_t lStatus;
 
     lStatus = checkEffectCompatibility(descriptor);
-    if (lStatus != NO_ERROR) {
+    if (probe || lStatus != NO_ERROR) {
        *status = lStatus;
        return handle;
     }
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
index 14ff14d..81e6065 100644
--- a/services/audioflinger/DeviceEffectManager.h
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -36,7 +36,8 @@
                 const sp<IEffectClient>& effectClient,
                 const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches,
                 int *enabled,
-                status_t *status);
+                status_t *status,
+                bool probe);
     void createAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch);
     void releaseAudioPatch(audio_patch_handle_t handle);
 
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 82b9c96..8a65122 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -3047,7 +3047,7 @@
         int enabled;
         *handle = thread->createEffect_l(nullptr, nullptr, 0, AUDIO_SESSION_DEVICE,
                                          const_cast<effect_descriptor_t *>(&mDescriptor),
-                                         &enabled, &status, false);
+                                         &enabled, &status, false, false /*probe*/);
         ALOGV("%s thread->createEffect_l status %d", __func__, status);
     } else {
         status = BAD_VALUE;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index f24dcd7..dc39b62 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1365,7 +1365,8 @@
         effect_descriptor_t *desc,
         int *enabled,
         status_t *status,
-        bool pinned)
+        bool pinned,
+        bool probe)
 {
     sp<EffectModule> effect;
     sp<EffectHandle> handle;
@@ -1387,7 +1388,7 @@
         Mutex::Autolock _l(mLock);
 
         lStatus = checkEffectCompatibility_l(desc, sessionId);
-        if (lStatus != NO_ERROR) {
+        if (probe || lStatus != NO_ERROR) {
             goto Exit;
         }
 
@@ -1433,7 +1434,7 @@
     }
 
 Exit:
-    if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+    if (!probe && lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
         Mutex::Autolock _l(mLock);
         if (effectCreated) {
             chain->removeEffect_l(effect);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 153cf7c..8149e95 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -342,7 +342,8 @@
                                     effect_descriptor_t *desc,
                                     int *enabled,
                                     status_t *status /*non-NULL*/,
-                                    bool pinned);
+                                    bool pinned,
+                                    bool probe);
 
                 // return values for hasAudioSession (bit field)
                 enum effect_state {