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 <android/IMediaExtractor.h>
31 #include <android/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(
58                     interface_cast<IMediaExtractorService>(binder));
59             sp<IMediaExtractor> ex;
60             mediaExService->makeExtractor(
61                     CreateIDataSourceFromDataSource(source),
62                     mime ? std::make_unique<std::string>(mime) : nullptr,
63                     &ex);
64             return ex;
65         } else {
66             ALOGE("extractor service not running");
67             return NULL;
68         }
69     }
70     return NULL;
71 }
72 
CreateFromService(const sp<DataSource> & source,const char * mime)73 sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
74         const sp<DataSource> &source, const char *mime) {
75 
76     ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
77 
78     void *meta = nullptr;
79     void *creator = NULL;
80     FreeMetaFunc freeMeta = nullptr;
81     float confidence;
82     sp<ExtractorPlugin> plugin;
83     uint32_t creatorVersion = 0;
84     creator = sniff(source, &confidence, &meta, &freeMeta, plugin, &creatorVersion);
85     if (!creator) {
86         ALOGV("FAILED to autodetect media content.");
87         return NULL;
88     }
89 
90     MediaExtractor *ex = nullptr;
91     if (creatorVersion == EXTRACTORDEF_VERSION_NDK_V1 ||
92             creatorVersion == EXTRACTORDEF_VERSION_NDK_V2) {
93         CMediaExtractor *ret = ((CreatorFunc)creator)(source->wrap(), meta);
94         if (meta != nullptr && freeMeta != nullptr) {
95             freeMeta(meta);
96         }
97         ex = ret != nullptr ? new MediaExtractorCUnwrapper(ret) : nullptr;
98     }
99 
100     ALOGV("Created an extractor '%s' with confidence %.2f",
101          ex != nullptr ? ex->name() : "<null>", confidence);
102 
103     return CreateIMediaExtractorFromMediaExtractor(ex, source, plugin);
104 }
105 
106 struct ExtractorPlugin : public RefBase {
107     ExtractorDef def;
108     void *libHandle;
109     String8 libPath;
110     String8 uuidString;
111 
ExtractorPluginandroid::ExtractorPlugin112     ExtractorPlugin(ExtractorDef definition, void *handle, String8 &path)
113         : def(definition), libHandle(handle), libPath(path) {
114         for (size_t i = 0; i < sizeof ExtractorDef::extractor_uuid; i++) {
115             uuidString.appendFormat("%02x", def.extractor_uuid.b[i]);
116         }
117     }
~ExtractorPluginandroid::ExtractorPlugin118     ~ExtractorPlugin() {
119         if (libHandle != nullptr) {
120             ALOGV("closing handle for %s %d", libPath.c_str(), def.extractor_version);
121             dlclose(libHandle);
122         }
123     }
124 };
125 
126 Mutex MediaExtractorFactory::gPluginMutex;
127 std::shared_ptr<std::list<sp<ExtractorPlugin>>> MediaExtractorFactory::gPlugins;
128 bool MediaExtractorFactory::gPluginsRegistered = false;
129 bool MediaExtractorFactory::gIgnoreVersion = false;
130 
131 // static
sniff(const sp<DataSource> & source,float * confidence,void ** meta,FreeMetaFunc * freeMeta,sp<ExtractorPlugin> & plugin,uint32_t * creatorVersion)132 void *MediaExtractorFactory::sniff(
133         const sp<DataSource> &source, float *confidence, void **meta,
134         FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin, uint32_t *creatorVersion) {
135     *confidence = 0.0f;
136     *meta = nullptr;
137 
138     std::shared_ptr<std::list<sp<ExtractorPlugin>>> plugins;
139     {
140         Mutex::Autolock autoLock(gPluginMutex);
141         if (!gPluginsRegistered) {
142             return NULL;
143         }
144         plugins = gPlugins;
145     }
146 
147     void *bestCreator = NULL;
148     for (auto it = plugins->begin(); it != plugins->end(); ++it) {
149         ALOGV("sniffing %s", (*it)->def.extractor_name);
150         float newConfidence;
151         void *newMeta = nullptr;
152         FreeMetaFunc newFreeMeta = nullptr;
153 
154         void *curCreator = NULL;
155         if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V1) {
156             curCreator = (void*) (*it)->def.u.v2.sniff(
157                     source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
158         } else if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
159             curCreator = (void*) (*it)->def.u.v3.sniff(
160                     source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
161         }
162 
163         if (curCreator) {
164             if (newConfidence > *confidence) {
165                 *confidence = newConfidence;
166                 if (*meta != nullptr && *freeMeta != nullptr) {
167                     (*freeMeta)(*meta);
168                 }
169                 *meta = newMeta;
170                 *freeMeta = newFreeMeta;
171                 plugin = *it;
172                 bestCreator = curCreator;
173                 *creatorVersion = (*it)->def.def_version;
174             } else {
175                 if (newMeta != nullptr && newFreeMeta != nullptr) {
176                     newFreeMeta(newMeta);
177                 }
178             }
179         }
180     }
181 
182     return bestCreator;
183 }
184 
185 // static
RegisterExtractor(const sp<ExtractorPlugin> & plugin,std::list<sp<ExtractorPlugin>> & pluginList)186 void MediaExtractorFactory::RegisterExtractor(const sp<ExtractorPlugin> &plugin,
187         std::list<sp<ExtractorPlugin>> &pluginList) {
188     // sanity check check struct version, uuid, name
189     if (plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V1 &&
190             plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V2) {
191         ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version);
192         return;
193     }
194     if (memcmp(&plugin->def.extractor_uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
195         ALOGE("invalid UUID, ignoring");
196         return;
197     }
198     if (plugin->def.extractor_name == NULL || strlen(plugin->def.extractor_name) == 0) {
199         ALOGE("extractors should have a name, ignoring");
200         return;
201     }
202 
203     for (auto it = pluginList.begin(); it != pluginList.end(); ++it) {
204         if (memcmp(&((*it)->def.extractor_uuid), &plugin->def.extractor_uuid, 16) == 0) {
205             // there's already an extractor with the same uuid
206             if (gIgnoreVersion || (*it)->def.extractor_version < plugin->def.extractor_version) {
207                 // this one is newer, replace the old one
208                 ALOGW("replacing extractor '%s' version %u with version %u",
209                         plugin->def.extractor_name,
210                         (*it)->def.extractor_version,
211                         plugin->def.extractor_version);
212                 pluginList.erase(it);
213                 break;
214             } else {
215                 ALOGW("ignoring extractor '%s' version %u in favor of version %u",
216                         plugin->def.extractor_name,
217                         plugin->def.extractor_version,
218                         (*it)->def.extractor_version);
219                 return;
220             }
221         }
222     }
223     ALOGV("registering extractor for %s", plugin->def.extractor_name);
224     pluginList.push_back(plugin);
225 }
226 
227 //static
RegisterExtractors(const char * libDirPath,const android_dlextinfo * dlextinfo,std::list<sp<ExtractorPlugin>> & pluginList)228 void MediaExtractorFactory::RegisterExtractors(
229         const char *libDirPath, const android_dlextinfo* dlextinfo,
230         std::list<sp<ExtractorPlugin>> &pluginList) {
231     ALOGV("search for plugins at %s", libDirPath);
232 
233     DIR *libDir = opendir(libDirPath);
234     if (libDir) {
235         struct dirent* libEntry;
236         while ((libEntry = readdir(libDir))) {
237             if (libEntry->d_name[0] == '.') {
238                 continue;
239             }
240             String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
241             if (!libPath.contains("extractor.so")) {
242                 continue;
243             }
244             void *libHandle = android_dlopen_ext(
245                     libPath.string(),
246                     RTLD_NOW | RTLD_LOCAL, dlextinfo);
247             CHECK(libHandle != nullptr)
248                     << "couldn't dlopen(" << libPath.string() << ") " << strerror(errno);
249 
250             GetExtractorDef getDef =
251                 (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
252             CHECK(getDef != nullptr)
253                     << libPath.string() << " does not contain sniffer";
254 
255             ALOGV("registering sniffer for %s", libPath.string());
256             RegisterExtractor(
257                     new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
258         }
259         closedir(libDir);
260     } else {
261         ALOGE("couldn't opendir(%s)", libDirPath);
262     }
263 }
264 
compareFunc(const sp<ExtractorPlugin> & first,const sp<ExtractorPlugin> & second)265 static bool compareFunc(const sp<ExtractorPlugin>& first, const sp<ExtractorPlugin>& second) {
266     return strcmp(first->def.extractor_name, second->def.extractor_name) < 0;
267 }
268 
269 static std::vector<std::string> gSupportedExtensions;
270 
271 // static
LoadExtractors()272 void MediaExtractorFactory::LoadExtractors() {
273     Mutex::Autolock autoLock(gPluginMutex);
274 
275     if (gPluginsRegistered) {
276         return;
277     }
278 
279     gIgnoreVersion = property_get_bool("debug.extractor.ignore_version", false);
280 
281     std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());
282 
283     android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
284     if (mediaNs != NULL) {
285         const android_dlextinfo dlextinfo = {
286             .flags = ANDROID_DLEXT_USE_NAMESPACE,
287             .library_namespace = mediaNs,
288         };
289         RegisterExtractors("/apex/com.android.media/lib"
290 #ifdef __LP64__
291                 "64"
292 #endif
293                 "/extractors", &dlextinfo, *newList);
294 
295     } else {
296         ALOGE("couldn't find media namespace.");
297     }
298 
299     RegisterExtractors("/system/lib"
300 #ifdef __LP64__
301             "64"
302 #endif
303             "/extractors", NULL, *newList);
304 
305     RegisterExtractors("/system_ext/lib"
306 #ifdef __LP64__
307             "64"
308 #endif
309             "/extractors", NULL, *newList);
310 
311     newList->sort(compareFunc);
312     gPlugins = newList;
313 
314     for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
315         if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
316             for (size_t i = 0;; i++) {
317                 const char* ext = (*it)->def.u.v3.supported_types[i];
318                 if (ext == nullptr) {
319                     break;
320                 }
321                 gSupportedExtensions.push_back(std::string(ext));
322             }
323         }
324     }
325 
326     gPluginsRegistered = true;
327 }
328 
329 // static
getSupportedTypes()330 std::vector<std::string> MediaExtractorFactory::getSupportedTypes() {
331     if (getuid() == AID_MEDIA_EX) {
332         return gSupportedExtensions;
333     }
334     ALOGV("get service manager");
335     sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
336 
337     if (binder != 0) {
338         sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
339         std::vector<std::string> supportedTypes;
340         mediaExService->getSupportedTypes(&supportedTypes);
341         return supportedTypes;
342     }
343     return std::vector<std::string>();
344 }
345 
dump(int fd,const Vector<String16> &)346 status_t MediaExtractorFactory::dump(int fd, const Vector<String16>&) {
347     Mutex::Autolock autoLock(gPluginMutex);
348     String8 out;
349 
350     const IPCThreadState* ipc = IPCThreadState::self();
351     const int pid = ipc->getCallingPid();
352     const int uid = ipc->getCallingUid();
353     if (!PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
354         // dumpExtractors() will append the following string.
355         // out.appendFormat("Permission Denial: "
356         //        "can't dump MediaExtractor from pid=%d, uid=%d\n", pid, uid);
357         ALOGE("Permission Denial: can't dump MediaExtractor from pid=%d, uid=%d", pid, uid);
358     } else {
359         out.append("Available extractors:\n");
360         if (gPluginsRegistered) {
361             for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
362                 out.appendFormat("  %25s: plugin_version(%d), uuid(%s), version(%u), path(%s)",
363                         (*it)->def.extractor_name,
364                     (*it)->def.def_version,
365                         (*it)->uuidString.c_str(),
366                         (*it)->def.extractor_version,
367                         (*it)->libPath.c_str());
368                 if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
369                     out.append(", supports: ");
370                     for (size_t i = 0;; i++) {
371                         const char* mime = (*it)->def.u.v3.supported_types[i];
372                         if (mime == nullptr) {
373                             break;
374                         }
375                         out.appendFormat("%s ", mime);
376                     }
377                 }
378                 out.append("\n");
379             }
380             out.append("\n");
381         } else {
382             out.append("  (no plugins registered)\n");
383         }
384     }
385     write(fd, out.string(), out.size());
386     return OK;
387 }
388 
389 
390 }  // namespace android
391