1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "MediaExtractorFactory"
19 #include <utils/Log.h>
20 
21 #include <android/dlext.h>
22 #include <android-base/logging.h>
23 #include <binder/IPCThreadState.h>
24 #include <binder/PermissionCache.h>
25 #include <binder/IServiceManager.h>
26 #include <media/DataSource.h>
27 #include <media/stagefright/InterfaceUtils.h>
28 #include <media/stagefright/MediaExtractor.h>
29 #include <media/stagefright/MediaExtractorFactory.h>
30 #include <media/IMediaExtractor.h>
31 #include <media/IMediaExtractorService.h>
32 #include <nativeloader/dlext_namespaces.h>
33 #include <private/android_filesystem_config.h>
34 #include <cutils/properties.h>
35 #include <utils/String8.h>
36 
37 #include <dirent.h>
38 #include <dlfcn.h>
39 
40 namespace android {
41 
42 // static
Create(const sp<DataSource> & source,const char * mime)43 sp<IMediaExtractor> MediaExtractorFactory::Create(
44         const sp<DataSource> &source, const char *mime) {
45     ALOGV("MediaExtractorFactory::Create %s", mime);
46 
47     if (!property_get_bool("media.stagefright.extractremote", true)) {
48         // local extractor
49         ALOGW("creating media extractor in calling process");
50         return CreateFromService(source, mime);
51     } else {
52         // remote extractor
53         ALOGV("get service manager");
54         sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
55 
56         if (binder != 0) {
57             sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
58             sp<IMediaExtractor> ex = mediaExService->makeExtractor(
59                     CreateIDataSourceFromDataSource(source), mime);
60             return ex;
61         } else {
62             ALOGE("extractor service not running");
63             return NULL;
64         }
65     }
66     return NULL;
67 }
68 
CreateFromService(const sp<DataSource> & source,const char * mime)69 sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
70         const sp<DataSource> &source, const char *mime) {
71 
72     ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
73 
74     void *meta = nullptr;
75     void *creator = NULL;
76     FreeMetaFunc freeMeta = nullptr;
77     float confidence;
78     sp<ExtractorPlugin> plugin;
79     uint32_t creatorVersion = 0;
80     creator = sniff(source, &confidence, &meta, &freeMeta, plugin, &creatorVersion);
81     if (!creator) {
82         ALOGV("FAILED to autodetect media content.");
83         return NULL;
84     }
85 
86     MediaExtractor *ex = nullptr;
87     if (creatorVersion == EXTRACTORDEF_VERSION_NDK_V1 ||
88             creatorVersion == EXTRACTORDEF_VERSION_NDK_V2) {
89         CMediaExtractor *ret = ((CreatorFunc)creator)(source->wrap(), meta);
90         if (meta != nullptr && freeMeta != nullptr) {
91             freeMeta(meta);
92         }
93         ex = ret != nullptr ? new MediaExtractorCUnwrapper(ret) : nullptr;
94     }
95 
96     ALOGV("Created an extractor '%s' with confidence %.2f",
97          ex != nullptr ? ex->name() : "<null>", confidence);
98 
99     return CreateIMediaExtractorFromMediaExtractor(ex, source, plugin);
100 }
101 
102 struct ExtractorPlugin : public RefBase {
103     ExtractorDef def;
104     void *libHandle;
105     String8 libPath;
106     String8 uuidString;
107 
ExtractorPluginandroid::ExtractorPlugin108     ExtractorPlugin(ExtractorDef definition, void *handle, String8 &path)
109         : def(definition), libHandle(handle), libPath(path) {
110         for (size_t i = 0; i < sizeof ExtractorDef::extractor_uuid; i++) {
111             uuidString.appendFormat("%02x", def.extractor_uuid.b[i]);
112         }
113     }
~ExtractorPluginandroid::ExtractorPlugin114     ~ExtractorPlugin() {
115         if (libHandle != nullptr) {
116             ALOGV("closing handle for %s %d", libPath.c_str(), def.extractor_version);
117             dlclose(libHandle);
118         }
119     }
120 };
121 
122 Mutex MediaExtractorFactory::gPluginMutex;
123 std::shared_ptr<std::list<sp<ExtractorPlugin>>> MediaExtractorFactory::gPlugins;
124 bool MediaExtractorFactory::gPluginsRegistered = false;
125 bool MediaExtractorFactory::gIgnoreVersion = false;
126 
127 // static
sniff(const sp<DataSource> & source,float * confidence,void ** meta,FreeMetaFunc * freeMeta,sp<ExtractorPlugin> & plugin,uint32_t * creatorVersion)128 void *MediaExtractorFactory::sniff(
129         const sp<DataSource> &source, float *confidence, void **meta,
130         FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin, uint32_t *creatorVersion) {
131     *confidence = 0.0f;
132     *meta = nullptr;
133 
134     std::shared_ptr<std::list<sp<ExtractorPlugin>>> plugins;
135     {
136         Mutex::Autolock autoLock(gPluginMutex);
137         if (!gPluginsRegistered) {
138             return NULL;
139         }
140         plugins = gPlugins;
141     }
142 
143     void *bestCreator = NULL;
144     for (auto it = plugins->begin(); it != plugins->end(); ++it) {
145         ALOGV("sniffing %s", (*it)->def.extractor_name);
146         float newConfidence;
147         void *newMeta = nullptr;
148         FreeMetaFunc newFreeMeta = nullptr;
149 
150         void *curCreator = NULL;
151         if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V1) {
152             curCreator = (void*) (*it)->def.u.v2.sniff(
153                     source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
154         } else if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
155             curCreator = (void*) (*it)->def.u.v3.sniff(
156                     source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
157         }
158 
159         if (curCreator) {
160             if (newConfidence > *confidence) {
161                 *confidence = newConfidence;
162                 if (*meta != nullptr && *freeMeta != nullptr) {
163                     (*freeMeta)(*meta);
164                 }
165                 *meta = newMeta;
166                 *freeMeta = newFreeMeta;
167                 plugin = *it;
168                 bestCreator = curCreator;
169                 *creatorVersion = (*it)->def.def_version;
170             } else {
171                 if (newMeta != nullptr && newFreeMeta != nullptr) {
172                     newFreeMeta(newMeta);
173                 }
174             }
175         }
176     }
177 
178     return bestCreator;
179 }
180 
181 // static
RegisterExtractor(const sp<ExtractorPlugin> & plugin,std::list<sp<ExtractorPlugin>> & pluginList)182 void MediaExtractorFactory::RegisterExtractor(const sp<ExtractorPlugin> &plugin,
183         std::list<sp<ExtractorPlugin>> &pluginList) {
184     // sanity check check struct version, uuid, name
185     if (plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V1 &&
186             plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V2) {
187         ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version);
188         return;
189     }
190     if (memcmp(&plugin->def.extractor_uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
191         ALOGE("invalid UUID, ignoring");
192         return;
193     }
194     if (plugin->def.extractor_name == NULL || strlen(plugin->def.extractor_name) == 0) {
195         ALOGE("extractors should have a name, ignoring");
196         return;
197     }
198 
199     for (auto it = pluginList.begin(); it != pluginList.end(); ++it) {
200         if (memcmp(&((*it)->def.extractor_uuid), &plugin->def.extractor_uuid, 16) == 0) {
201             // there's already an extractor with the same uuid
202             if (gIgnoreVersion || (*it)->def.extractor_version < plugin->def.extractor_version) {
203                 // this one is newer, replace the old one
204                 ALOGW("replacing extractor '%s' version %u with version %u",
205                         plugin->def.extractor_name,
206                         (*it)->def.extractor_version,
207                         plugin->def.extractor_version);
208                 pluginList.erase(it);
209                 break;
210             } else {
211                 ALOGW("ignoring extractor '%s' version %u in favor of version %u",
212                         plugin->def.extractor_name,
213                         plugin->def.extractor_version,
214                         (*it)->def.extractor_version);
215                 return;
216             }
217         }
218     }
219     ALOGV("registering extractor for %s", plugin->def.extractor_name);
220     pluginList.push_back(plugin);
221 }
222 
223 //static
RegisterExtractors(const char * libDirPath,const android_dlextinfo * dlextinfo,std::list<sp<ExtractorPlugin>> & pluginList)224 void MediaExtractorFactory::RegisterExtractors(
225         const char *libDirPath, const android_dlextinfo* dlextinfo,
226         std::list<sp<ExtractorPlugin>> &pluginList) {
227     ALOGV("search for plugins at %s", libDirPath);
228 
229     DIR *libDir = opendir(libDirPath);
230     if (libDir) {
231         struct dirent* libEntry;
232         while ((libEntry = readdir(libDir))) {
233             if (libEntry->d_name[0] == '.') {
234                 continue;
235             }
236             String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
237             if (!libPath.contains("extractor.so")) {
238                 continue;
239             }
240             void *libHandle = android_dlopen_ext(
241                     libPath.string(),
242                     RTLD_NOW | RTLD_LOCAL, dlextinfo);
243             CHECK(libHandle != nullptr)
244                     << "couldn't dlopen(" << libPath.string() << ") " << strerror(errno);
245 
246             GetExtractorDef getDef =
247                 (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
248             CHECK(getDef != nullptr)
249                     << libPath.string() << " does not contain sniffer";
250 
251             ALOGV("registering sniffer for %s", libPath.string());
252             RegisterExtractor(
253                     new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
254         }
255         closedir(libDir);
256     } else {
257         ALOGE("couldn't opendir(%s)", libDirPath);
258     }
259 }
260 
compareFunc(const sp<ExtractorPlugin> & first,const sp<ExtractorPlugin> & second)261 static bool compareFunc(const sp<ExtractorPlugin>& first, const sp<ExtractorPlugin>& second) {
262     return strcmp(first->def.extractor_name, second->def.extractor_name) < 0;
263 }
264 
265 static std::unordered_set<std::string> gSupportedExtensions;
266 
267 // static
LoadExtractors()268 void MediaExtractorFactory::LoadExtractors() {
269     Mutex::Autolock autoLock(gPluginMutex);
270 
271     if (gPluginsRegistered) {
272         return;
273     }
274 
275     gIgnoreVersion = property_get_bool("debug.extractor.ignore_version", false);
276 
277     std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());
278 
279     android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
280     if (mediaNs != NULL) {
281         const android_dlextinfo dlextinfo = {
282             .flags = ANDROID_DLEXT_USE_NAMESPACE,
283             .library_namespace = mediaNs,
284         };
285         RegisterExtractors("/apex/com.android.media/lib"
286 #ifdef __LP64__
287                 "64"
288 #endif
289                 "/extractors", &dlextinfo, *newList);
290 
291     } else {
292         ALOGE("couldn't find media namespace.");
293     }
294 
295     RegisterExtractors("/system/lib"
296 #ifdef __LP64__
297             "64"
298 #endif
299             "/extractors", NULL, *newList);
300 
301     RegisterExtractors("/system_ext/lib"
302 #ifdef __LP64__
303             "64"
304 #endif
305             "/extractors", NULL, *newList);
306 
307     newList->sort(compareFunc);
308     gPlugins = newList;
309 
310     for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
311         if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
312             for (size_t i = 0;; i++) {
313                 const char* ext = (*it)->def.u.v3.supported_types[i];
314                 if (ext == nullptr) {
315                     break;
316                 }
317                 gSupportedExtensions.insert(std::string(ext));
318             }
319         }
320     }
321 
322     gPluginsRegistered = true;
323 }
324 
325 // static
getSupportedTypes()326 std::unordered_set<std::string> MediaExtractorFactory::getSupportedTypes() {
327     if (getuid() == AID_MEDIA_EX) {
328         return gSupportedExtensions;
329     }
330     ALOGV("get service manager");
331     sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
332 
333     if (binder != 0) {
334         sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
335         return mediaExService->getSupportedTypes();
336     }
337     return std::unordered_set<std::string>();
338 }
339 
dump(int fd,const Vector<String16> &)340 status_t MediaExtractorFactory::dump(int fd, const Vector<String16>&) {
341     Mutex::Autolock autoLock(gPluginMutex);
342     String8 out;
343 
344     const IPCThreadState* ipc = IPCThreadState::self();
345     const int pid = ipc->getCallingPid();
346     const int uid = ipc->getCallingUid();
347     if (!PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
348         // dumpExtractors() will append the following string.
349         // out.appendFormat("Permission Denial: "
350         //        "can't dump MediaExtractor from pid=%d, uid=%d\n", pid, uid);
351         ALOGE("Permission Denial: can't dump MediaExtractor from pid=%d, uid=%d", pid, uid);
352     } else {
353         out.append("Available extractors:\n");
354         if (gPluginsRegistered) {
355             for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
356                 out.appendFormat("  %25s: plugin_version(%d), uuid(%s), version(%u), path(%s)",
357                         (*it)->def.extractor_name,
358                     (*it)->def.def_version,
359                         (*it)->uuidString.c_str(),
360                         (*it)->def.extractor_version,
361                         (*it)->libPath.c_str());
362                 if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
363                     out.append(", supports: ");
364                     for (size_t i = 0;; i++) {
365                         const char* mime = (*it)->def.u.v3.supported_types[i];
366                         if (mime == nullptr) {
367                             break;
368                         }
369                         out.appendFormat("%s ", mime);
370                     }
371                 }
372                 out.append("\n");
373             }
374             out.append("\n");
375         } else {
376             out.append("  (no plugins registered)\n");
377         }
378     }
379     write(fd, out.string(), out.size());
380     return OK;
381 }
382 
383 
384 }  // namespace android
385