Add imagedecoder_heif_fuzzer

Test: ./imagedecoder_heif_fuzzer
exec/s: 251
Bug: 326543079

Change-Id: I1cf4a1938eb537d2dd5bc0f22c2536866dead1c4
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 8f16f76..0fb3049 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -127,3 +127,30 @@
         "-DPNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR",
     ],
 }
+
+cc_fuzz {
+    name: "imagedecoder_heif_fuzzer",
+    defaults: ["imagedecoder_fuzzer_defaults"],
+    team: "trendy_team_android_core_graphics_stack",
+    shared_libs: [
+        "libfakeservicemanager",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "libmediaplayerservice",
+                "libmediaextractorservice",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libbinder_random_parcel",
+                "libcutils",
+            ],
+        },
+    },
+    include_dirs: ["frameworks/av/services/mediaextractor"],
+    cflags: [
+        "-DFUZZ_HEIF_FORMAT",
+    ],
+}
diff --git a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
index 6743997..f739e4a 100644
--- a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
+++ b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
@@ -18,6 +18,16 @@
 #include <binder/IPCThreadState.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#ifdef FUZZ_HEIF_FORMAT
+#include <fakeservicemanager/FakeServiceManager.h>
+#ifdef __ANDROID__
+#include <MediaExtractorService.h>
+#include <MediaPlayerService.h>
+#else
+#include <fuzzbinder/random_binder.h>
+#endif //__ANDROID__
+#endif // FUZZ_HEIF_FORMAT
+
 #ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
 #include <fuzz/png_mutator.h>
 #endif
@@ -31,8 +41,42 @@
 
 using PixelPointer = std::unique_ptr<void, PixelFreer>;
 
+#ifndef FUZZ_HEIF_FORMAT
+#define FOURCC(c1, c2, c3, c4) ((c1) << 24 | (c2) << 16 | (c3) << 8 | (c4))
+/** Reverse all 4 bytes in a 32bit value.
+    e.g. 0x12345678 -> 0x78563412
+*/
+static uint32_t endianSwap32(uint32_t value) {
+    return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) |
+            (value >> 24);
+}
+
+static bool isFtyp(const uint8_t* data, size_t size) {
+    constexpr int32_t headerSize = 8;
+    constexpr int32_t chunkTypeOffset = 4;
+    constexpr int32_t ftypFourCCVal = FOURCC('f', 't', 'y', 'p');
+    if (size >= headerSize) {
+        const uint32_t* chunk = reinterpret_cast<const uint32_t*>(data + chunkTypeOffset);
+        if (endianSwap32(*chunk) == ftypFourCCVal) {
+            return true;
+        }
+    }
+    return false;
+}
+#endif
+
 AImageDecoder* init(const uint8_t* data, size_t size, bool useFileDescriptor) {
     AImageDecoder* decoder = nullptr;
+#ifndef FUZZ_HEIF_FORMAT
+    if (isFtyp(data, size)) {
+        /* We want to ignore HEIF data when fuzzing non-HEIF image decoders. Use 'FTYP'
+         * as a signal to ignore, though note that this excludes more than just HEIF.
+         * But when this code was added, `AImageDecoder` did not support any formats
+         * in 'FTYP' besides HEIF.
+         */
+        return nullptr;
+    }
+#endif // FUZZ_HEIF_FORMAT
     if (useFileDescriptor) {
         constexpr char testFd[] = "tempFd";
         int32_t fileDesc = open(testFd, O_RDWR | O_CREAT | O_TRUNC);
@@ -47,6 +91,27 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     FuzzedDataProvider dataProvider = FuzzedDataProvider(data, size);
+#ifdef FUZZ_HEIF_FORMAT
+    /**
+     * For image formats like HEIF, a new metadata object is
+     * created which requires "media.player" service running
+     */
+    static std::once_flag callOnceHEIF;
+    std::call_once(callOnceHEIF, [&]() {
+        android::sp<android::IServiceManager> fakeServiceManager =
+                new android::FakeServiceManager();
+        setDefaultServiceManager(fakeServiceManager);
+#ifdef __ANDROID__
+        android::MediaPlayerService::instantiate();
+        android::MediaExtractorService::instantiate();
+#else
+        auto binderExtractor = android::getRandomBinder(&dataProvider);
+        auto binderPlayer = android::getRandomBinder(&dataProvider);
+        fakeServiceManager->addService(android::String16("media.extractor"), binderExtractor);
+        fakeServiceManager->addService(android::String16("media.player"), binderPlayer);
+#endif //__ANDROID__
+    });
+#endif // FUZZ_HEIF_FORMAT
     /**
      * Use maximum of 80% of buffer for creating decoder and save at least
      * 20% buffer for fuzzing other APIs