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::optional<std::string>(mime) : std::nullopt,
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         ALOGW("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         ALOGW("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.c_str(),
246                     RTLD_NOW | RTLD_LOCAL, dlextinfo);
247             if (libHandle == nullptr) {
248                 ALOGI("dlopen(%s) reported error %s", libPath.c_str(), strerror(errno));
249                 continue;
250             }
251 
252             GetExtractorDef getDef =
253                 (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
254             if (getDef == nullptr) {
255                 ALOGI("no sniffer found in %s", libPath.c_str());
256                 dlclose(libHandle);
257                 continue;
258             }
259 
260             ALOGV("registering sniffer for %s", libPath.c_str());
261             RegisterExtractor(
262                     new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
263         }
264         closedir(libDir);
265     } else {
266         ALOGI("plugin directory not present (%s)", libDirPath);
267     }
268 }
269 
compareFunc(const sp<ExtractorPlugin> & first,const sp<ExtractorPlugin> & second)270 static bool compareFunc(const sp<ExtractorPlugin>& first, const sp<ExtractorPlugin>& second) {
271     return strcmp(first->def.extractor_name, second->def.extractor_name) < 0;
272 }
273 
274 static std::vector<std::string> gSupportedExtensions;
275 
276 // static
LoadExtractors()277 void MediaExtractorFactory::LoadExtractors() {
278     Mutex::Autolock autoLock(gPluginMutex);
279 
280     if (gPluginsRegistered) {
281         return;
282     }
283 
284     gIgnoreVersion = property_get_bool("debug.extractor.ignore_version", false);
285 
286     std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());
287 
288     android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
289     if (mediaNs != NULL) {
290         const android_dlextinfo dlextinfo = {
291             .flags = ANDROID_DLEXT_USE_NAMESPACE,
292             .library_namespace = mediaNs,
293         };
294         RegisterExtractors("/apex/com.android.media/lib"
295 #ifdef __LP64__
296                 "64"
297 #endif
298                 "/extractors", &dlextinfo, *newList);
299 
300     } else {
301         ALOGE("couldn't find media namespace.");
302     }
303 
304     RegisterExtractors("/system/lib"
305 #ifdef __LP64__
306             "64"
307 #endif
308             "/extractors", NULL, *newList);
309 
310     RegisterExtractors("/system_ext/lib"
311 #ifdef __LP64__
312             "64"
313 #endif
314             "/extractors", NULL, *newList);
315 
316     newList->sort(compareFunc);
317     gPlugins = newList;
318 
319     for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
320         if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
321             for (size_t i = 0;; i++) {
322                 const char* ext = (*it)->def.u.v3.supported_types[i];
323                 if (ext == nullptr) {
324                     break;
325                 }
326                 gSupportedExtensions.push_back(std::string(ext));
327             }
328         }
329     }
330 
331     gPluginsRegistered = true;
332 }
333 
334 // static
getSupportedTypes()335 std::vector<std::string> MediaExtractorFactory::getSupportedTypes() {
336     if (getuid() == AID_MEDIA_EX) {
337         return gSupportedExtensions;
338     }
339     ALOGV("get service manager");
340     sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
341 
342     if (binder != 0) {
343         sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
344         std::vector<std::string> supportedTypes;
345         mediaExService->getSupportedTypes(&supportedTypes);
346         return supportedTypes;
347     }
348     return std::vector<std::string>();
349 }
350 
dump(int fd,const Vector<String16> &)351 status_t MediaExtractorFactory::dump(int fd, const Vector<String16>&) {
352     Mutex::Autolock autoLock(gPluginMutex);
353     String8 out;
354 
355     const IPCThreadState* ipc = IPCThreadState::self();
356     const int pid = ipc->getCallingPid();
357     const int uid = ipc->getCallingUid();
358     if (!PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
359         // dumpExtractors() will append the following string.
360         // out.appendFormat("Permission Denial: "
361         //        "can't dump MediaExtractor from pid=%d, uid=%d\n", pid, uid);
362         ALOGE("Permission Denial: can't dump MediaExtractor from pid=%d, uid=%d", pid, uid);
363     } else {
364         out.append("Available extractors:\n");
365         if (gPluginsRegistered) {
366             for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
367                 out.appendFormat("  %25s: plugin_version(%d), uuid(%s), version(%u), path(%s)",
368                         (*it)->def.extractor_name,
369                     (*it)->def.def_version,
370                         (*it)->uuidString.c_str(),
371                         (*it)->def.extractor_version,
372                         (*it)->libPath.c_str());
373                 if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
374                     out.append(", supports: ");
375                     for (size_t i = 0;; i++) {
376                         const char* mime = (*it)->def.u.v3.supported_types[i];
377                         if (mime == nullptr) {
378                             break;
379                         }
380                         out.appendFormat("%s ", mime);
381                     }
382                 }
383                 out.append("\n");
384             }
385             out.append("\n");
386         } else {
387             out.append("  (no plugins registered)\n");
388         }
389     }
390     write(fd, out.c_str(), out.size());
391     return OK;
392 }
393 
394 
395 }  // namespace android
396