Implement CreateInputSurface CCodec plugin

createInputSurface() will attempt to create an input surface from a list
of services, returning the first success, in the following order:
  0 - Hardware Codec2.0
  1 - OMX
  2 - Software Codec2.0

However, the system property debug.stagefright.c2inputsurface can be
used to mask available services. The k-th bit (counting from 0) of
debug.stagefright.c2inputsurface corresponds to enabling of the
k-indexed service.

The default value of debug.stagefright.c2inputsurface is 6, which means
createInputSurface() will try to create an input surface from the OMX
service first. If it fails, the software Codec2.0 service will be tried
next.

Test: adb shell setprop debug.stagefright.ccodec 3
adb shell setprop debug.stagefright.c2inputsurface 4
adb shell killall mediaserver
adb shell /system/bin/screenrecord --codec-name c2.android.avc.encoder \
/sdcard/screenrecord.mp4
adb pull /sdcard/screenrecord.mp4

Bug: 64121714

Change-Id: I35288442d5fce41738a12bdcb4be32fecfaf6b88
diff --git a/codec2/hidl/1.0/utils/ComponentStore.cpp b/codec2/hidl/1.0/utils/ComponentStore.cpp
index 9972b2a..24fb3eb 100644
--- a/codec2/hidl/1.0/utils/ComponentStore.cpp
+++ b/codec2/hidl/1.0/utils/ComponentStore.cpp
@@ -28,6 +28,7 @@
 #include <media/stagefright/bqhelper/GraphicBufferSource.h>
 
 #include <C2PlatformSupport.h>
+#include <util/C2InterfaceHelper.h>
 
 #include <utils/Errors.h>
 
@@ -130,6 +131,9 @@
 
 c2_status_t ComponentStore::validateSupportedParams(
         const std::vector<std::shared_ptr<C2ParamDescriptor>>& params) {
+    if (!mParamReflector) {
+        return C2_BAD_INDEX;
+    }
     c2_status_t res = C2_OK;
 
     for (const std::shared_ptr<C2ParamDescriptor> &desc : params) {
@@ -228,8 +232,16 @@
     typedef ::android::hardware::graphics::bufferqueue::V1_0::
             IGraphicBufferProducer HGBP;
     typedef ::android::TWGraphicBufferProducer<HGBP> B2HGBP;
+    std::shared_ptr<C2ComponentStore> platformStore =
+            GetCodec2PlatformComponentStore();
+    std::shared_ptr<C2ReflectorHelper> reflector;
+    if (platformStore) {
+        reflector = std::static_pointer_cast<C2ReflectorHelper>(
+                platformStore->getParamReflector());
+    }
     return new InputSurface(
             this,
+            reflector,
             new B2HGBP(source->getIGraphicBufferProducer()),
             source);
 }
@@ -257,6 +269,10 @@
         if (item == mStructDescriptors.end()) {
             // not in the cache, and not known to be unsupported, query local reflector
             if (!mUnsupportedStructDescriptors.count(coreIndex)) {
+                if (!mParamReflector) {
+                    mUnsupportedStructDescriptors.emplace(coreIndex);
+                    continue;
+                }
                 std::shared_ptr<C2StructDescriptor> structDesc =
                     mParamReflector->describe(coreIndex);
                 if (!structDesc) {
diff --git a/codec2/hidl/1.0/utils/InputSurface.cpp b/codec2/hidl/1.0/utils/InputSurface.cpp
index 2d58183..ec50bc4 100644
--- a/codec2/hidl/1.0/utils/InputSurface.cpp
+++ b/codec2/hidl/1.0/utils/InputSurface.cpp
@@ -42,6 +42,10 @@
             const std::shared_ptr<C2ReflectorHelper> &helper)
         : C2InterfaceHelper(helper) {
 
+        if (!helper) {
+            return;
+        }
+
         setDerivedInstance(this);
 
         addParameter(
@@ -287,13 +291,13 @@
 // Constructor is exclusive to ComponentStore.
 InputSurface::InputSurface(
         const sp<ComponentStore>& store,
+        const std::shared_ptr<C2ReflectorHelper>& reflector,
         const sp<HGraphicBufferProducer>& base,
         const sp<GraphicBufferSource>& source) :
     mStore(store),
     mBase(base),
     mSource(source),
-    mHelper(std::make_shared<ConfigurableImpl>(
-            std::static_pointer_cast<C2ReflectorHelper>(store->mParamReflector))),
+    mHelper(std::make_shared<ConfigurableImpl>(reflector)),
     mConfigurable(new CachedConfigurable(
             std::make_unique<ConfigurableWrapper>(mHelper, source))) {
 
diff --git a/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h b/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h
index ee82a24..0e86d63 100644
--- a/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h
+++ b/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h
@@ -33,6 +33,8 @@
 #include <hidl/HidlSupport.h>
 #include <hidl/Status.h>
 
+class C2ReflectorHelper;
+
 namespace hardware {
 namespace google {
 namespace media {
@@ -178,6 +180,7 @@
 
     InputSurface(
             const sp<ComponentStore>& store,
+            const std::shared_ptr<C2ReflectorHelper>& reflector,
             const sp<HGraphicBufferProducer>& base,
             const sp<GraphicBufferSource>& source);
 
diff --git a/codec2/hidl/client/client.cpp b/codec2/hidl/client/client.cpp
index 76a3196..9b7c90b 100644
--- a/codec2/hidl/client/client.cpp
+++ b/codec2/hidl/client/client.cpp
@@ -62,22 +62,22 @@
 // c2_status_t value that corresponds to hwbinder transaction failure.
 constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED;
 
-// List of known IComponentStore services.
+// List of known IComponentStore services in the decreasing order of preference.
 constexpr const char* kClientNames[] = {
         "default",
         "software",
     };
 
-typedef std::array<
-        std::shared_ptr<Codec2Client>,
-        std::extent<decltype(kClientNames)>::value> ClientList;
+// Number of known IComponentStore services.
+constexpr size_t kNumClients = std::extent<decltype(kClientNames)>::value;
+
+// Start index of the clients in the above list that are considered "backup" by
+// Codec2Client::CreateInputSurface().
+constexpr size_t kBackupClientStartIndex = 1;
+
+typedef std::array<std::shared_ptr<Codec2Client>, kNumClients> ClientList;
 
 // Convenience methods to obtain known clients.
-size_t getClientCount() {
-    // TODO: this may not work if there is no default service
-    return std::extent<decltype(kClientNames)>::value;
-}
-
 std::shared_ptr<Codec2Client> getClient(size_t index) {
     return Codec2Client::CreateFromService(kClientNames[index]);
 }
@@ -508,10 +508,14 @@
         ALOGE("createInputSurface -- failed transaction.");
         return C2_TRANSACTION_FAILED;
     }
-    *inputSurface = std::make_shared<InputSurface>(
-            static_cast<sp<IInputSurface>>(transResult));
+    sp<IInputSurface> result = static_cast<sp<IInputSurface>>(transResult);
+    if (!result) {
+        *inputSurface = nullptr;
+        return C2_OK;
+    }
+    *inputSurface = std::make_shared<InputSurface>(result);
     if (!*inputSurface) {
-        ALOGE("createInputSurface -- failed to create client.");
+        ALOGE("createInputSurface -- unknown error.");
         return C2_CORRUPTED;
     }
     return C2_OK;
@@ -631,7 +635,7 @@
     // client fails, retry once. We do this by pushing the last known client in front of the
     // list of all clients.
     std::deque<size_t> indices;
-    for (size_t index = getClientCount(); index > 0; ) {
+    for (size_t index = kNumClients; index > 0; ) {
         indices.push_front(--index);
     }
 
@@ -715,6 +719,31 @@
     return interface;
 }
 
+std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface(
+        bool useBackupClients) {
+    size_t i;
+    size_t endIndex;
+    if (useBackupClients) {
+        i = kBackupClientStartIndex;
+        endIndex = kNumClients;
+    } else {
+        i = 0;
+        endIndex = kBackupClientStartIndex;
+    }
+    for (; i < endIndex; ++i) {
+        std::shared_ptr<Codec2Client> client = getClient(i);
+        std::shared_ptr<Codec2Client::InputSurface> inputSurface;
+        if (client &&
+                client->createInputSurface(&inputSurface) == C2_OK &&
+                inputSurface) {
+            return inputSurface;
+        }
+    }
+    ALOGW("Could not create an input surface from any %s Codec2.0 services.",
+            useBackupClients ? "backup" : "preferred");
+    return nullptr;
+}
+
 const std::vector<C2Component::Traits>& Codec2Client::ListComponents() {
     static std::vector<C2Component::Traits> traitsList = [](){
         std::vector<C2Component::Traits> list;
@@ -1256,6 +1285,10 @@
     return mGraphicBufferProducer;
 }
 
+const sp<IInputSurface>& Codec2Client::InputSurface::getHalInterface() const {
+    return mBase;
+}
+
 // Codec2Client::InputSurfaceConnection
 
 Codec2Client::InputSurfaceConnection::Base*
diff --git a/codec2/hidl/client/include/codec2/hidl/client.h b/codec2/hidl/client/include/codec2/hidl/client.h
index 24b6f7e..73baea5 100644
--- a/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/codec2/hidl/client/include/codec2/hidl/client.h
@@ -214,6 +214,13 @@
     // List traits from all known IComponentStore services.
     static const std::vector<C2Component::Traits>& ListComponents();
 
+    // Try to call createInputSurface on a list of known clients, returning the
+    // first non-null value.
+    // There are two list of known clients: preferred and backup.
+    // useBackupClients determines which list of known clients will be used.
+    static std::shared_ptr<InputSurface> CreateInputSurface(
+            bool useBackupClients = false);
+
     // base cannot be null.
     Codec2Client(const sp<Base>& base, std::string instanceName);
 
@@ -393,6 +400,9 @@
 
     const sp<IGraphicBufferProducer>& getGraphicBufferProducer() const;
 
+    // Return the underlying IInputSurface.
+    const sp<Base>& getHalInterface() const;
+
     // base cannot be null.
     InputSurface(const sp<Base>& base);
 
diff --git a/media/sfplugin/Android.bp b/media/sfplugin/Android.bp
index 9843162..5c48cc6 100644
--- a/media/sfplugin/Android.bp
+++ b/media/sfplugin/Android.bp
@@ -26,6 +26,7 @@
         "android.hardware.cas.native@1.0",
         "android.hardware.graphics.bufferqueue@1.0",
         "android.hardware.media.omx@1.0",
+        "hardware.google.media.c2@1.0",
         "libbase",
         "libbinder",
         "libcodec2_hidl_client",
diff --git a/media/sfplugin/CCodec.cpp b/media/sfplugin/CCodec.cpp
index 519ebd7..33c68e0 100644
--- a/media/sfplugin/CCodec.cpp
+++ b/media/sfplugin/CCodec.cpp
@@ -27,30 +27,35 @@
 #include <C2PlatformSupport.h>
 #include <C2V4l2Support.h>
 
-#include <android/IOMXBufferSource.h>
 #include <android/IGraphicBufferSource.h>
+#include <android/IOMXBufferSource.h>
+#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
+#include <android/hardware/media/omx/1.0/IOmx.h>
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
-#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/Surface.h>
-#include <media/omx/1.0/WOmx.h>
-#include <media/stagefright/codec2/1.0/InputSurface.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <media/omx/1.0/WGraphicBufferSource.h>
 #include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/MediaCodecConstants.h>
 #include <media/stagefright/PersistentSurface.h>
+#include <media/stagefright/codec2/1.0/InputSurface.h>
 
 #include "C2OMXNode.h"
 #include "CCodec.h"
 #include "CCodecBufferChannel.h"
 #include "InputSurfaceWrapper.h"
 
+extern "C" android::PersistentSurface *CreateInputSurface();
+
 namespace android {
 
 using namespace std::chrono_literals;
 using ::android::hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer;
 using android::base::StringPrintf;
 using BGraphicBufferSource = ::android::IGraphicBufferSource;
+using ::hardware::google::media::c2::V1_0::IInputSurface;
 
 namespace {
 
@@ -866,47 +871,32 @@
         outputFormat = config->mOutputFormat;
     }
 
-    // TODO: Remove this property check and assume it's always true.
-    if (property_get_bool("debug.stagefright.c2inputsurface", false)) {
-        std::shared_ptr<Codec2Client::InputSurface> surface;
+    std::shared_ptr<PersistentSurface> persistentSurface(CreateInputSurface());
+    if (!persistentSurface) {
+        ALOGE("Failed to create input surface");
+        mCallback->onInputSurfaceCreationFailed(NO_INIT);
+        return;
+    }
 
-        err = static_cast<status_t>(mClient->createInputSurface(&surface));
-        if (err != OK) {
-            ALOGE("Failed to create input surface: %d", static_cast<int>(err));
-            mCallback->onInputSurfaceCreationFailed(err);
-            return;
-        }
-        if (!surface) {
-            ALOGE("Failed to create input surface: null input surface");
+    if (persistentSurface->getHidlTarget()) {
+        sp<IInputSurface> inputSurface = IInputSurface::castFrom(
+                persistentSurface->getHidlTarget());
+        if (!inputSurface) {
+            ALOGE("Corrupted input surface");
             mCallback->onInputSurfaceCreationFailed(UNKNOWN_ERROR);
             return;
         }
-        bufferProducer = surface->getGraphicBufferProducer();
-        err = setupInputSurface(std::make_shared<C2InputSurfaceWrapper>(surface));
-    } else { // TODO: Remove this block.
-        using namespace ::android::hardware::media::omx::V1_0;
-        sp<IOmx> tOmx = IOmx::getService("default");
-        if (tOmx == nullptr) {
-            ALOGE("Failed to create input surface");
-            mCallback->onInputSurfaceCreationFailed(UNKNOWN_ERROR);
-            return;
-        }
-        sp<IOMX> omx = new utils::LWOmx(tOmx);
-
-        sp<BGraphicBufferSource> bufferSource;
-        err = omx->createInputSurface(&bufferProducer, &bufferSource);
-
-        if (err != OK) {
-            ALOGE("Failed to create input surface: %d", err);
-            mCallback->onInputSurfaceCreationFailed(err);
-            return;
-        }
+        err = setupInputSurface(std::make_shared<C2InputSurfaceWrapper>(
+                std::make_shared<Codec2Client::InputSurface>(inputSurface)));
+        bufferProducer = new H2BGraphicBufferProducer(inputSurface);
+    } else {
         int32_t width = 0;
         (void)outputFormat->findInt32("width", &width);
         int32_t height = 0;
         (void)outputFormat->findInt32("height", &height);
         err = setupInputSurface(std::make_shared<GraphicBufferSourceWrapper>(
-                bufferSource, width, height));
+                persistentSurface->getBufferSource(), width, height));
+        bufferProducer = persistentSurface->getBufferProducer();
     }
 
     if (err != OK) {
@@ -914,6 +904,7 @@
         mCallback->onInputSurfaceCreationFailed(err);
         return;
     }
+
     mCallback->onInputSurfaceCreated(
             inputFormat,
             outputFormat,
@@ -952,16 +943,34 @@
         inputFormat = config->mInputFormat;
         outputFormat = config->mOutputFormat;
     }
-    int32_t width = 0;
-    (void)outputFormat->findInt32("width", &width);
-    int32_t height = 0;
-    (void)outputFormat->findInt32("height", &height);
-    status_t err = setupInputSurface(std::make_shared<GraphicBufferSourceWrapper>(
-            surface->getBufferSource(), width, height));
-    if (err != OK) {
-        ALOGE("Failed to set up input surface: %d", err);
-        mCallback->onInputSurfaceDeclined(err);
-        return;
+    auto hidlTarget = surface->getHidlTarget();
+    if (hidlTarget) {
+        sp<IInputSurface> inputSurface =
+                IInputSurface::castFrom(hidlTarget);
+        if (!inputSurface) {
+            ALOGE("Failed to set input surface: Corrupted surface.");
+            mCallback->onInputSurfaceDeclined(UNKNOWN_ERROR);
+            return;
+        }
+        status_t err = setupInputSurface(std::make_shared<C2InputSurfaceWrapper>(
+                std::make_shared<Codec2Client::InputSurface>(inputSurface)));
+        if (err != OK) {
+            ALOGE("Failed to set up input surface: %d", err);
+            mCallback->onInputSurfaceDeclined(err);
+            return;
+        }
+    } else {
+        int32_t width = 0;
+        (void)outputFormat->findInt32("width", &width);
+        int32_t height = 0;
+        (void)outputFormat->findInt32("height", &height);
+        status_t err = setupInputSurface(std::make_shared<GraphicBufferSourceWrapper>(
+                surface->getBufferSource(), width, height));
+        if (err != OK) {
+            ALOGE("Failed to set up input surface: %d", err);
+            mCallback->onInputSurfaceDeclined(err);
+            return;
+        }
     }
     mCallback->onInputSurfaceAccepted(inputFormat, outputFormat);
 }
@@ -1495,3 +1504,64 @@
     return new android::CCodec;
 }
 
+extern "C" android::PersistentSurface *CreateInputSurface() {
+    int32_t useC2InputSurface =
+            property_get_int32("debug.stagefright.c2inputsurface", 6);
+
+    // Preferred (hardware) Codec2.0 service
+    if (useC2InputSurface & 1) {
+        std::shared_ptr<android::Codec2Client::InputSurface> inputSurface =
+                android::Codec2Client::CreateInputSurface();
+        if (inputSurface) {
+            return new android::PersistentSurface(
+                    inputSurface->getGraphicBufferProducer(),
+                    static_cast<android::sp<android::hidl::base::V1_0::IBase>>(
+                    inputSurface->getHalInterface()));
+        }
+    }
+
+    // OMX service
+    if (useC2InputSurface & 2) {
+        using namespace android::hardware::media::omx::V1_0;
+        using namespace android::hardware::media::omx::V1_0::utils;
+        using namespace android::hardware::graphics::bufferqueue::V1_0::utils;
+        android::sp<IOmx> omx = IOmx::getService();
+        Status s;
+        typedef android::hardware::graphics::bufferqueue::V1_0::
+                IGraphicBufferProducer HGraphicBufferProducer;
+        typedef android::hardware::media::omx::V1_0::
+                IGraphicBufferSource HGraphicBufferSource;
+        android::sp<HGraphicBufferProducer> gbp;
+        android::sp<HGraphicBufferSource> gbs;
+        android::Return<void> transStatus = omx->createInputSurface(
+                [&s, &gbp, &gbs](
+                        Status status,
+                        const android::sp<HGraphicBufferProducer>& producer,
+                        const android::sp<HGraphicBufferSource>& source) {
+                    s = status;
+                    gbp = producer;
+                    gbs = source;
+                });
+        if (transStatus.isOk() && s == Status::OK) {
+            return new android::PersistentSurface(
+                    new H2BGraphicBufferProducer(gbp),
+                    sp<::android::IGraphicBufferSource>(
+                        new LWGraphicBufferSource(gbs)));
+        }
+    }
+
+    // Backup (software) Codec2.0 service
+    if (useC2InputSurface & 4) {
+        std::shared_ptr<android::Codec2Client::InputSurface> inputSurface =
+                android::Codec2Client::CreateInputSurface(true);
+        if (inputSurface) {
+            return new android::PersistentSurface(
+                    inputSurface->getGraphicBufferProducer(),
+                    static_cast<android::sp<android::hidl::base::V1_0::IBase>>(
+                    inputSurface->getHalInterface()));
+        }
+    }
+
+    return nullptr;
+}
+