Load extractor libs directly from update APK

Test: play MP4 file. install and uninstall media update apk.
Bug: 67908547
Change-Id: I80c7772e25ba010be468834ce238f0fb86bc8d14
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 0b4fd25..3c9bfdd 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -139,10 +139,18 @@
     },
 }
 
+filegroup {
+    name: "mediaupdateservice_aidl",
+    srcs: [
+        "aidl/android/media/IMediaExtractorUpdateService.aidl",
+    ],
+}
+
 cc_library_shared {
     name: "libmedia",
 
     srcs: [
+        ":mediaupdateservice_aidl",
         "IDataSource.cpp",
         "BufferingSettings.cpp",
         "mediaplayer.cpp",
@@ -179,6 +187,11 @@
         "StringArray.cpp",
     ],
 
+    aidl: {
+        local_include_dirs: ["aidl"],
+        export_aidl_headers: true,
+    },
+
     shared_libs: [
         "liblog",
         "libcutils",
diff --git a/media/libmedia/aidl/android/media/IMediaExtractorUpdateService.aidl b/media/libmedia/aidl/android/media/IMediaExtractorUpdateService.aidl
new file mode 100644
index 0000000..57b1bc9
--- /dev/null
+++ b/media/libmedia/aidl/android/media/IMediaExtractorUpdateService.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Service to reload extractor plugins when update package is installed/uninstalled.
+ * @hide
+ */
+interface IMediaExtractorUpdateService {
+    void loadPlugins(@utf8InCpp String apkPath);
+}
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 26de4ca..95e3721 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -112,6 +112,7 @@
         "libRScpp",
         "libhidlbase",
         "libhidlmemory",
+        "libziparchive",
         "android.hidl.allocator@1.0",
         "android.hardware.cas.native@1.0",
         "android.hardware.media.omx@1.0",
@@ -215,6 +216,7 @@
         "libmedia_helper",
         "libstagefright_foundation",
         "libdl",
+        "libziparchive",
     ],
 
     static_libs: [
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 4ca2d0d..2ade598 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -31,12 +31,15 @@
 #include <media/IMediaExtractorService.h>
 #include <cutils/properties.h>
 #include <utils/String8.h>
+#include <ziparchive/zip_archive.h>
 
 #include <dirent.h>
 #include <dlfcn.h>
 
 namespace android {
 
+static const char *kUpdateApkPath = "/system/priv-app/MediaUpdate/MediaUpdate.apk";
+
 // static
 sp<IMediaExtractor> MediaExtractorFactory::Create(
         const sp<DataSource> &source, const char *mime) {
@@ -106,6 +109,7 @@
         const sp<DataSource> &source, const char *mime) {
 
     ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
+
     UpdateExtractors(nullptr);
 
     // initialize source decryption if needed
@@ -132,9 +136,10 @@
 }
 
 //static
-void MediaExtractorFactory::LoadPlugins(const ::std::string& libraryPath) {
-    ALOGV("Load plugins from: %s", libraryPath.c_str());
-    UpdateExtractors(libraryPath.c_str());
+void MediaExtractorFactory::LoadPlugins(const ::std::string& apkPath) {
+    // TODO: Verify apk path with package manager in extractor process.
+    ALOGV("Load plugins from: %s", apkPath.c_str());
+    UpdateExtractors(apkPath.empty() ? nullptr : apkPath.c_str());
 }
 
 struct ExtractorPlugin : public RefBase {
@@ -237,40 +242,53 @@
 
 //static
 void MediaExtractorFactory::RegisterExtractors(
-        const char *libDirPath, List<sp<ExtractorPlugin>> &pluginList) {
-    ALOGV("search for plugins at %s", libDirPath);
-    DIR *libDir = opendir(libDirPath);
-    if (libDir) {
-        struct dirent* libEntry;
-        while ((libEntry = readdir(libDir))) {
-            String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
-            void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL);
-            if (libHandle) {
-                MediaExtractor::GetExtractorDef getDef =
-                    (MediaExtractor::GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
-                if (getDef) {
-                    ALOGV("registering sniffer for %s", libPath.string());
-                    RegisterExtractor(
-                            new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
+        const char *apkPath, List<sp<ExtractorPlugin>> &pluginList) {
+    ALOGV("search for plugins at %s", apkPath);
+    ZipArchiveHandle zipHandle;
+    int32_t ret = OpenArchive(apkPath, &zipHandle);
+    if (ret == 0) {
+        char abi[PROPERTY_VALUE_MAX];
+        property_get("ro.product.cpu.abi", abi, "arm64-v8a");
+        ZipString prefix(String8::format("lib/%s/", abi).c_str());
+        ZipString suffix("extractor.so");
+        void* cookie;
+        ret = StartIteration(zipHandle, &cookie, &prefix, &suffix);
+        if (ret == 0) {
+            ZipEntry entry;
+            ZipString name;
+            while (Next(cookie, &entry, &name) == 0) {
+                String8 libPath = String8(apkPath) + "!/" +
+                    String8(reinterpret_cast<const char*>(name.name), name.name_length);
+                void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL);
+                if (libHandle) {
+                    MediaExtractor::GetExtractorDef getDef =
+                        (MediaExtractor::GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
+                    if (getDef) {
+                        ALOGV("registering sniffer for %s", libPath.string());
+                        RegisterExtractor(
+                                new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
+                    } else {
+                        ALOGW("%s does not contain sniffer", libPath.string());
+                        dlclose(libHandle);
+                    }
                 } else {
-                    ALOGW("%s does not contain sniffer", libPath.string());
-                    dlclose(libHandle);
+                    ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno));
                 }
-            } else {
-                ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno));
             }
+            EndIteration(cookie);
+        } else {
+            ALOGW("couldn't find plugins from %s, %d", apkPath, ret);
         }
-
-        closedir(libDir);
+        CloseArchive(zipHandle);
     } else {
-        ALOGE("couldn't opendir(%s)", libDirPath);
+        ALOGW("couldn't open(%s) %d", apkPath, ret);
     }
 }
 
 // static
-void MediaExtractorFactory::UpdateExtractors(const char *newlyInstalledLibPath) {
+void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) {
     Mutex::Autolock autoLock(gPluginMutex);
-    if (newlyInstalledLibPath != nullptr) {
+    if (newUpdateApkPath != nullptr) {
         gPluginsRegistered = false;
     }
     if (gPluginsRegistered) {
@@ -279,20 +297,10 @@
 
     std::shared_ptr<List<sp<ExtractorPlugin>>> newList(new List<sp<ExtractorPlugin>>());
 
-    RegisterExtractors("/system/lib"
-#ifdef __LP64__
-            "64"
-#endif
-            "/extractors", *newList);
+    RegisterExtractors(kUpdateApkPath, *newList);
 
-    RegisterExtractors("/vendor/lib"
-#ifdef __LP64__
-            "64"
-#endif
-            "/extractors", *newList);
-
-    if (newlyInstalledLibPath != nullptr) {
-        RegisterExtractors(newlyInstalledLibPath, *newList);
+    if (newUpdateApkPath != nullptr) {
+        RegisterExtractors(newUpdateApkPath, *newList);
     }
 
     gPlugins = newList;
diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
index 0a9514a..7fddf80 100644
--- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
+++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
@@ -40,6 +40,7 @@
             int fd, int64_t offset, int64_t length, const char *mime, sp<DataSource> *out);
     static sp<IMediaExtractor> CreateFromService(
             const sp<DataSource> &source, const char *mime = NULL);
+    static void LoadPlugins(const ::std::string& apkPath);
 
 private:
     static Mutex gPluginMutex;
@@ -47,7 +48,7 @@
     static bool gPluginsRegistered;
 
     static void RegisterExtractors(
-            const char *libDirPath, List<sp<ExtractorPlugin>> &pluginList);
+            const char *apkPath, List<sp<ExtractorPlugin>> &pluginList);
     static void RegisterExtractor(
             const sp<ExtractorPlugin> &plugin, List<sp<ExtractorPlugin>> &pluginList);
 
@@ -55,7 +56,7 @@
             String8 *mimeType, float *confidence, sp<AMessage> *meta,
             sp<ExtractorPlugin> &plugin);
 
-    static void UpdateExtractors(const char *newlyInstalledLibPath);
+    static void UpdateExtractors(const char *newUpdateApkPath);
 };
 
 }  // namespace android
diff --git a/packages/MediaUpdate/Android.mk b/packages/MediaUpdate/Android.mk
index 4a71401..ec614f6 100644
--- a/packages/MediaUpdate/Android.mk
+++ b/packages/MediaUpdate/Android.mk
@@ -31,4 +31,22 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_PROGUARD_FLAG_FILES := proguard.cfg
 
+LOCAL_MULTILIB := first
+
+# Embed native libraries in package, rather than installing to /system/lib*.
+LOCAL_MODULE_TAGS := samples
+
+# To embed native libraries in package, uncomment the lines below.
+LOCAL_JNI_SHARED_LIBRARIES := \
+    libaacextractor \
+    libamrextractor \
+    libflacextractor \
+    libmidiextractor \
+    libmkvextractor \
+    libmp3extractor \
+    libmp4extractor \
+    libmpeg2extractor \
+    liboggextractor \
+    libwavextractor \
+
 include $(BUILD_PACKAGE)
diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk
index cd086f9..b6de7e3 100644
--- a/services/mediaextractor/Android.mk
+++ b/services/mediaextractor/Android.mk
@@ -2,8 +2,11 @@
 
 # service library
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := MediaExtractorService.cpp
 LOCAL_CFLAGS := -Wall -Werror
+LOCAL_SRC_FILES := \
+    MediaExtractorService.cpp \
+    MediaExtractorUpdateService.cpp \
+
 LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils liblog
 LOCAL_MODULE:= libmediaextractorservice
 include $(BUILD_SHARED_LIBRARY)
@@ -18,16 +21,7 @@
 
 # extractor libraries
 LOCAL_REQUIRED_MODULES := \
-    libaacextractor \
-    libamrextractor \
-    libflacextractor \
-    libmidiextractor \
-    libmkvextractor \
-    libmp3extractor \
-    libmp4extractor \
-    libmpeg2extractor \
-    liboggextractor \
-    libwavextractor \
+    MediaUpdate \
 
 LOCAL_SRC_FILES := main_extractorservice.cpp
 LOCAL_SHARED_LIBRARIES := libmedia libmediaextractorservice libbinder libutils \
diff --git a/services/mediaextractor/MediaExtractorUpdateService.cpp b/services/mediaextractor/MediaExtractorUpdateService.cpp
new file mode 100644
index 0000000..473a698
--- /dev/null
+++ b/services/mediaextractor/MediaExtractorUpdateService.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MediaExtractorUpdateService"
+#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaExtractorFactory.h>
+
+#include "MediaExtractorUpdateService.h"
+
+namespace android {
+namespace media {
+
+binder::Status MediaExtractorUpdateService::loadPlugins(const ::std::string& apkPath) {
+    ALOGV("loadPlugins %s", apkPath.c_str());
+    MediaExtractorFactory::LoadPlugins(apkPath);
+    return binder::Status::ok();
+}
+
+}   // namespace media
+}   // namespace android
diff --git a/services/mediaextractor/MediaExtractorUpdateService.h b/services/mediaextractor/MediaExtractorUpdateService.h
new file mode 100644
index 0000000..4115f6d
--- /dev/null
+++ b/services/mediaextractor/MediaExtractorUpdateService.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_EXTRACTOR_UPDATE_SERVICE_H
+#define ANDROID_MEDIA_EXTRACTOR_UPDATE_SERVICE_H
+
+#include <binder/BinderService.h>
+#include <android/media/BnMediaExtractorUpdateService.h>
+
+namespace android {
+namespace media {
+
+class MediaExtractorUpdateService
+    : public BinderService<MediaExtractorUpdateService>, public BnMediaExtractorUpdateService
+{
+    friend class BinderService<MediaExtractorUpdateService>;
+public:
+    MediaExtractorUpdateService() : BnMediaExtractorUpdateService() { }
+    virtual ~MediaExtractorUpdateService() { }
+    static const char* getServiceName() { return "media.extractor.update"; }
+    binder::Status loadPlugins(const ::std::string& apkPath);
+};
+
+}   // namespace media
+}   // namespace android
+
+#endif  // ANDROID_MEDIA_EXTRACTOR_UPDATE_SERVICE_H
diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp
index 6a5320d..1069b09 100644
--- a/services/mediaextractor/main_extractorservice.cpp
+++ b/services/mediaextractor/main_extractorservice.cpp
@@ -30,6 +30,7 @@
 // from LOCAL_C_INCLUDES
 #include "IcuUtils.h"
 #include "MediaExtractorService.h"
+#include "MediaExtractorUpdateService.h"
 #include "MediaUtils.h"
 #include "minijail.h"
 
@@ -63,6 +64,7 @@
     sp<ProcessState> proc(ProcessState::self());
     sp<IServiceManager> sm = defaultServiceManager();
     MediaExtractorService::instantiate();
+    media::MediaExtractorUpdateService::instantiate();
     ProcessState::self()->startThreadPool();
     IPCThreadState::self()->joinThreadPool();
 }