Keep the in-use extractor plugins when updating.

To track usage of extractors and close the so handle when they are
destroied, MediaExtractorPlugin/RefBase class is introduced.

Test: play MP4 file. install and uninstall media update apk.
Bug: 67908547
Change-Id: I24926f943bc7247627e62d38edafd13d9c305a51
diff --git a/media/libstagefright/InterfaceUtils.cpp b/media/libstagefright/InterfaceUtils.cpp
index cf9fdf8..f174ba4 100644
--- a/media/libstagefright/InterfaceUtils.cpp
+++ b/media/libstagefright/InterfaceUtils.cpp
@@ -38,11 +38,12 @@
     return RemoteDataSource::wrap(source);
 }
 
-sp<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor(const sp<MediaExtractor> &extractor) {
+sp<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor(
+        const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin) {
     if (extractor == nullptr) {
         return nullptr;
     }
-    return RemoteMediaExtractor::wrap(extractor);
+    return RemoteMediaExtractor::wrap(extractor, plugin);
 }
 
 sp<MediaSource> CreateMediaSourceFromIMediaSource(const sp<IMediaSource> &source) {
@@ -52,11 +53,12 @@
     return new CallbackMediaSource(source);
 }
 
-sp<IMediaSource> CreateIMediaSourceFromMediaSource(const sp<MediaSource> &source) {
+sp<IMediaSource> CreateIMediaSourceFromMediaSource(
+        const sp<MediaSource> &source, const sp<RefBase> &plugin) {
     if (source == nullptr) {
         return nullptr;
     }
-    return RemoteMediaSource::wrap(source);
+    return RemoteMediaSource::wrap(source, plugin);
 }
 
 }  // namespace android
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 8a90e93..4ca2d0d 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -15,7 +15,7 @@
  */
 
 //#define LOG_NDEBUG 0
-#define LOG_TAG "MediaExtractor"
+#define LOG_TAG "MediaExtractorFactory"
 #include <utils/Log.h>
 
 #include <binder/IServiceManager.h>
@@ -45,8 +45,7 @@
     if (!property_get_bool("media.stagefright.extractremote", true)) {
         // local extractor
         ALOGW("creating media extractor in calling process");
-        sp<MediaExtractor> extractor = CreateFromService(source, mime);
-        return CreateIMediaExtractorFromMediaExtractor(extractor);
+        return CreateFromService(source, mime);
     } else {
         // remote extractor
         ALOGV("get service manager");
@@ -103,11 +102,11 @@
     return Create(*out, mime);
 }
 
-sp<MediaExtractor> MediaExtractorFactory::CreateFromService(
+sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
         const sp<DataSource> &source, const char *mime) {
 
-    ALOGV("MediaExtractorFactory::%s %s", __func__, mime);
-    RegisterDefaultSniffers();
+    ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
+    UpdateExtractors(nullptr);
 
     // initialize source decryption if needed
     source->DrmInitialization(nullptr /* mime */);
@@ -117,7 +116,8 @@
     MediaExtractor::CreatorFunc creator = NULL;
     String8 tmp;
     float confidence;
-    creator = sniff(source, &tmp, &confidence, &meta);
+    sp<ExtractorPlugin> plugin;
+    creator = sniff(source, &tmp, &confidence, &meta, plugin);
     if (!creator) {
         ALOGV("FAILED to autodetect media content.");
         return NULL;
@@ -128,39 +128,63 @@
          mime, confidence);
 
     MediaExtractor *ret = creator(source, meta);
-    return ret;
+    return CreateIMediaExtractorFromMediaExtractor(ret, plugin);
 }
 
-Mutex MediaExtractorFactory::gSnifferMutex;
-List<MediaExtractor::ExtractorDef> MediaExtractorFactory::gSniffers;
-bool MediaExtractorFactory::gSniffersRegistered = false;
+//static
+void MediaExtractorFactory::LoadPlugins(const ::std::string& libraryPath) {
+    ALOGV("Load plugins from: %s", libraryPath.c_str());
+    UpdateExtractors(libraryPath.c_str());
+}
+
+struct ExtractorPlugin : public RefBase {
+    MediaExtractor::ExtractorDef def;
+    void *libHandle;
+    String8 libPath;
+
+    ExtractorPlugin(MediaExtractor::ExtractorDef definition, void *handle, String8 &path)
+        : def(definition), libHandle(handle), libPath(path) { }
+    ~ExtractorPlugin() {
+        if (libHandle != nullptr) {
+            ALOGV("closing handle for %s %d", libPath.c_str(), def.extractor_version);
+            dlclose(libHandle);
+        }
+    }
+};
+
+Mutex MediaExtractorFactory::gPluginMutex;
+std::shared_ptr<List<sp<ExtractorPlugin>>> MediaExtractorFactory::gPlugins;
+bool MediaExtractorFactory::gPluginsRegistered = false;
 
 // static
 MediaExtractor::CreatorFunc MediaExtractorFactory::sniff(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *meta) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *meta,
+        sp<ExtractorPlugin> &plugin) {
     *mimeType = "";
     *confidence = 0.0f;
     meta->clear();
 
+    std::shared_ptr<List<sp<ExtractorPlugin>>> plugins;
     {
-        Mutex::Autolock autoLock(gSnifferMutex);
-        if (!gSniffersRegistered) {
+        Mutex::Autolock autoLock(gPluginMutex);
+        if (!gPluginsRegistered) {
             return NULL;
         }
+        plugins = gPlugins;
     }
 
     MediaExtractor::CreatorFunc curCreator = NULL;
     MediaExtractor::CreatorFunc bestCreator = NULL;
-    for (List<MediaExtractor::ExtractorDef>::iterator it = gSniffers.begin();
-         it != gSniffers.end(); ++it) {
+    for (auto it = plugins->begin(); it != plugins->end(); ++it) {
         String8 newMimeType;
         float newConfidence;
         sp<AMessage> newMeta;
-        if ((curCreator = (*it).sniff(source, &newMimeType, &newConfidence, &newMeta))) {
+        if ((curCreator = (*it)->def.sniff(source, &newMimeType, &newConfidence, &newMeta))) {
             if (newConfidence > *confidence) {
                 *mimeType = newMimeType;
                 *confidence = newConfidence;
                 *meta = newMeta;
+                plugin = *it;
                 bestCreator = curCreator;
             }
         }
@@ -170,95 +194,109 @@
 }
 
 // static
-void MediaExtractorFactory::RegisterSniffer_l(const MediaExtractor::ExtractorDef &def) {
+void MediaExtractorFactory::RegisterExtractor(const sp<ExtractorPlugin> &plugin,
+        List<sp<ExtractorPlugin>> &pluginList) {
     // sanity check check struct version, uuid, name
-    if (def.def_version == 0 || def.def_version > MediaExtractor::EXTRACTORDEF_VERSION) {
-        ALOGE("don't understand extractor format %u, ignoring.", def.def_version);
+    if (plugin->def.def_version == 0
+            || plugin->def.def_version > MediaExtractor::EXTRACTORDEF_VERSION) {
+        ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version);
         return;
     }
-    if (memcmp(&def.extractor_uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
+    if (memcmp(&plugin->def.extractor_uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
         ALOGE("invalid UUID, ignoring");
         return;
     }
-    if (def.extractor_name == NULL || strlen(def.extractor_name) == 0) {
+    if (plugin->def.extractor_name == NULL || strlen(plugin->def.extractor_name) == 0) {
         ALOGE("extractors should have a name, ignoring");
         return;
     }
 
-    for (List<MediaExtractor::ExtractorDef>::iterator it = gSniffers.begin();
-            it != gSniffers.end(); ++it) {
-        if (memcmp(&((*it).extractor_uuid), &def.extractor_uuid, 16) == 0) {
+    for (auto it = pluginList.begin(); it != pluginList.end(); ++it) {
+        if (memcmp(&((*it)->def.extractor_uuid), &plugin->def.extractor_uuid, 16) == 0) {
             // there's already an extractor with the same uuid
-            if ((*it).extractor_version < def.extractor_version) {
+            if ((*it)->def.extractor_version < plugin->def.extractor_version) {
                 // this one is newer, replace the old one
                 ALOGW("replacing extractor '%s' version %u with version %u",
-                        def.extractor_name,
-                        (*it).extractor_version,
-                        def.extractor_version);
-                gSniffers.erase(it);
+                        plugin->def.extractor_name,
+                        (*it)->def.extractor_version,
+                        plugin->def.extractor_version);
+                pluginList.erase(it);
                 break;
             } else {
                 ALOGW("ignoring extractor '%s' version %u in favor of version %u",
-                        def.extractor_name,
-                        def.extractor_version,
-                        (*it).extractor_version);
+                        plugin->def.extractor_name,
+                        plugin->def.extractor_version,
+                        (*it)->def.extractor_version);
                 return;
             }
         }
     }
-    ALOGV("registering extractor for %s", def.extractor_name);
-    gSniffers.push_back(def);
+    ALOGV("registering extractor for %s", plugin->def.extractor_name);
+    pluginList.push_back(plugin);
+}
+
+//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);
+                } else {
+                    ALOGW("%s does not contain sniffer", libPath.string());
+                    dlclose(libHandle);
+                }
+            } else {
+                ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno));
+            }
+        }
+
+        closedir(libDir);
+    } else {
+        ALOGE("couldn't opendir(%s)", libDirPath);
+    }
 }
 
 // static
-void MediaExtractorFactory::RegisterDefaultSniffers() {
-    Mutex::Autolock autoLock(gSnifferMutex);
-    if (gSniffersRegistered) {
+void MediaExtractorFactory::UpdateExtractors(const char *newlyInstalledLibPath) {
+    Mutex::Autolock autoLock(gPluginMutex);
+    if (newlyInstalledLibPath != nullptr) {
+        gPluginsRegistered = false;
+    }
+    if (gPluginsRegistered) {
         return;
     }
 
-    auto registerExtractors = [](const char *libDirPath) -> void {
-        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 getsniffer =
-                            (MediaExtractor::GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
-                    if (getsniffer) {
-                        ALOGV("registering sniffer for %s", libPath.string());
-                        RegisterSniffer_l(getsniffer());
-                    } else {
-                        ALOGW("%s does not contain sniffer", libPath.string());
-                        dlclose(libHandle);
-                    }
-                } else {
-                    ALOGW("couldn't dlopen(%s)", libPath.string());
-                }
-            }
+    std::shared_ptr<List<sp<ExtractorPlugin>>> newList(new List<sp<ExtractorPlugin>>());
 
-            closedir(libDir);
-        } else {
-            ALOGE("couldn't opendir(%s)", libDirPath);
-        }
-    };
-
-    registerExtractors("/system/lib"
+    RegisterExtractors("/system/lib"
 #ifdef __LP64__
             "64"
 #endif
-            "/extractors/");
+            "/extractors", *newList);
 
-    registerExtractors("/vendor/lib"
+    RegisterExtractors("/vendor/lib"
 #ifdef __LP64__
             "64"
 #endif
-            "/extractors/");
+            "/extractors", *newList);
 
-    gSniffersRegistered = true;
+    if (newlyInstalledLibPath != nullptr) {
+        RegisterExtractors(newlyInstalledLibPath, *newList);
+    }
+
+    gPlugins = newList;
+    gPluginsRegistered = true;
 }
 
-
 }  // namespace android
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
index 2a16e16..12654d9 100644
--- a/media/libstagefright/RemoteMediaExtractor.cpp
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -36,8 +36,10 @@
 static const char *kExtractorTracks = "android.media.mediaextractor.ntrk";
 static const char *kExtractorFormat = "android.media.mediaextractor.fmt";
 
-RemoteMediaExtractor::RemoteMediaExtractor(const sp<MediaExtractor> &extractor)
-    :mExtractor(extractor) {
+RemoteMediaExtractor::RemoteMediaExtractor(
+        const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin)
+    :mExtractor(extractor),
+    mExtractorPlugin(plugin) {
 
     mAnalyticsItem = nullptr;
     if (MEDIA_LOG) {
@@ -65,6 +67,8 @@
 }
 
 RemoteMediaExtractor::~RemoteMediaExtractor() {
+    mExtractor = nullptr;
+    mExtractorPlugin = nullptr;
     // log the current record, provided it has some information worth recording
     if (MEDIA_LOG) {
         if (mAnalyticsItem != nullptr) {
@@ -86,7 +90,8 @@
 
 sp<IMediaSource> RemoteMediaExtractor::getTrack(size_t index) {
     sp<MediaSource> source = mExtractor->getTrack(index);
-    return (source.get() == nullptr) ? nullptr : CreateIMediaSourceFromMediaSource(source);
+    return (source.get() == nullptr)
+            ? nullptr : CreateIMediaSourceFromMediaSource(source, mExtractorPlugin);
 }
 
 sp<MetaData> RemoteMediaExtractor::getTrackMetaData(size_t index, uint32_t flags) {
@@ -133,11 +138,12 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 // static
-sp<IMediaExtractor> RemoteMediaExtractor::wrap(const sp<MediaExtractor> &extractor) {
+sp<IMediaExtractor> RemoteMediaExtractor::wrap(
+        const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin) {
     if (extractor.get() == nullptr) {
         return nullptr;
     }
-    return new RemoteMediaExtractor(extractor);
+    return new RemoteMediaExtractor(extractor, plugin);
 }
 
 }  // namespace android
diff --git a/media/libstagefright/RemoteMediaSource.cpp b/media/libstagefright/RemoteMediaSource.cpp
index 866d163..d97329c 100644
--- a/media/libstagefright/RemoteMediaSource.cpp
+++ b/media/libstagefright/RemoteMediaSource.cpp
@@ -19,10 +19,14 @@
 
 namespace android {
 
-RemoteMediaSource::RemoteMediaSource(const sp<MediaSource> &source)
-    :mSource(source) {}
+RemoteMediaSource::RemoteMediaSource(const sp<MediaSource> &source, const sp<RefBase> &plugin)
+    :mSource(source),
+    mExtractorPlugin(plugin) {}
 
-RemoteMediaSource::~RemoteMediaSource() {}
+RemoteMediaSource::~RemoteMediaSource() {
+    mSource = nullptr;
+    mExtractorPlugin = nullptr;
+}
 
 status_t RemoteMediaSource::start(MetaData *params) {
     return mSource->start(params);
@@ -51,11 +55,11 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 // static
-sp<IMediaSource> RemoteMediaSource::wrap(const sp<MediaSource> &source) {
+sp<IMediaSource> RemoteMediaSource::wrap(const sp<MediaSource> &source, const sp<RefBase> &plugin) {
     if (source.get() == nullptr) {
         return nullptr;
     }
-    return new RemoteMediaSource(source);
+    return new RemoteMediaSource(source, plugin);
 }
 
 }  // namespace android
diff --git a/media/libstagefright/include/media/stagefright/InterfaceUtils.h b/media/libstagefright/include/media/stagefright/InterfaceUtils.h
index 783f109..224c1f1 100644
--- a/media/libstagefright/include/media/stagefright/InterfaceUtils.h
+++ b/media/libstagefright/include/media/stagefright/InterfaceUtils.h
@@ -31,13 +31,15 @@
 sp<IDataSource> CreateIDataSourceFromDataSource(const sp<DataSource> &source);
 
 // Creates an IMediaExtractor wrapper to the given MediaExtractor.
-sp<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor(const sp<MediaExtractor> &extractor);
+sp<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor(
+        const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin);
 
 // Creates a MediaSource which wraps the given IMediaSource object.
 sp<MediaSource> CreateMediaSourceFromIMediaSource(const sp<IMediaSource> &source);
 
 // Creates an IMediaSource wrapper to the given MediaSource.
-sp<IMediaSource> CreateIMediaSourceFromMediaSource(const sp<MediaSource> &source);
+sp<IMediaSource> CreateIMediaSourceFromMediaSource(
+        const sp<MediaSource> &source, const sp<RefBase> &plugin);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
index f216ff8..0a9514a 100644
--- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
+++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
@@ -26,6 +26,7 @@
 namespace android {
 
 class DataSource;
+struct ExtractorPlugin;
 
 class MediaExtractorFactory {
 public:
@@ -37,20 +38,24 @@
     // will be alsp returned with |out|.
     static sp<IMediaExtractor> CreateFromFd(
             int fd, int64_t offset, int64_t length, const char *mime, sp<DataSource> *out);
-    static sp<MediaExtractor> CreateFromService(
+    static sp<IMediaExtractor> CreateFromService(
             const sp<DataSource> &source, const char *mime = NULL);
 
 private:
-    static Mutex gSnifferMutex;
-    static List<MediaExtractor::ExtractorDef> gSniffers;
-    static bool gSniffersRegistered;
+    static Mutex gPluginMutex;
+    static std::shared_ptr<List<sp<ExtractorPlugin>>> gPlugins;
+    static bool gPluginsRegistered;
 
-    static void RegisterSniffer_l(const MediaExtractor::ExtractorDef &def);
+    static void RegisterExtractors(
+            const char *libDirPath, List<sp<ExtractorPlugin>> &pluginList);
+    static void RegisterExtractor(
+            const sp<ExtractorPlugin> &plugin, List<sp<ExtractorPlugin>> &pluginList);
 
     static MediaExtractor::CreatorFunc sniff(const sp<DataSource> &source,
-            String8 *mimeType, float *confidence, sp<AMessage> *meta);
+            String8 *mimeType, float *confidence, sp<AMessage> *meta,
+            sp<ExtractorPlugin> &plugin);
 
-    static void RegisterDefaultSniffers();
+    static void UpdateExtractors(const char *newlyInstalledLibPath);
 };
 
 }  // namespace android
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
index 98b8b4d..2bd71ee 100644
--- a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
@@ -27,7 +27,7 @@
 // IMediaExtractor wrapper to the MediaExtractor.
 class RemoteMediaExtractor : public BnMediaExtractor {
 public:
-    static sp<IMediaExtractor> wrap(const sp<MediaExtractor> &extractor);
+    static sp<IMediaExtractor> wrap(const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin);
 
     virtual ~RemoteMediaExtractor();
     virtual size_t countTracks();
@@ -44,10 +44,11 @@
 
 private:
     sp<MediaExtractor> mExtractor;
+    sp<RefBase> mExtractorPlugin;
 
     MediaAnalyticsItem *mAnalyticsItem;
 
-    explicit RemoteMediaExtractor(const sp<MediaExtractor> &extractor);
+    explicit RemoteMediaExtractor(const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin);
 
     DISALLOW_EVIL_CONSTRUCTORS(RemoteMediaExtractor);
 };
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaSource.h b/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
index 0a446a5..cb222cc 100644
--- a/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
@@ -26,7 +26,7 @@
 // IMediaSrouce wrapper to the MediaSource.
 class RemoteMediaSource : public BnMediaSource {
 public:
-    static sp<IMediaSource> wrap(const sp<MediaSource> &source);
+    static sp<IMediaSource> wrap(const sp<MediaSource> &source, const sp<RefBase> &plugin);
     virtual ~RemoteMediaSource();
     virtual status_t start(MetaData *params = NULL);
     virtual status_t stop();
@@ -39,8 +39,9 @@
 
 private:
     sp<MediaSource> mSource;
+    sp<RefBase> mExtractorPlugin;
 
-    explicit RemoteMediaSource(const sp<MediaSource> &source);
+    explicit RemoteMediaSource(const sp<MediaSource> &source, const sp<RefBase> &plugin);
 
     DISALLOW_EVIL_CONSTRUCTORS(RemoteMediaSource);
 };
diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp
index f09d7cf..0dc1fce 100644
--- a/services/mediaextractor/MediaExtractorService.cpp
+++ b/services/mediaextractor/MediaExtractorService.cpp
@@ -36,16 +36,15 @@
 
     sp<DataSource> localSource = CreateDataSourceFromIDataSource(remoteSource);
 
-    sp<MediaExtractor> extractor = MediaExtractorFactory::CreateFromService(localSource, mime);
+    sp<IMediaExtractor> extractor = MediaExtractorFactory::CreateFromService(localSource, mime);
 
     ALOGV("extractor service created %p (%s)",
             extractor.get(),
             extractor == nullptr ? "" : extractor->name());
 
     if (extractor != nullptr) {
-        sp<IMediaExtractor> ret = CreateIMediaExtractorFromMediaExtractor(extractor);
-        registerMediaExtractor(ret, localSource, mime);
-        return ret;
+        registerMediaExtractor(extractor, localSource, mime);
+        return extractor;
     }
     return nullptr;
 }