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 <binder/IServiceManager.h>
22 #include <media/DataSource.h>
23 #include <media/MediaAnalyticsItem.h>
24 #include <media/MediaExtractor.h>
25 #include <media/stagefright/foundation/AMessage.h>
26 #include <media/stagefright/InterfaceUtils.h>
27 #include <media/stagefright/MediaExtractorFactory.h>
28 #include <media/IMediaExtractor.h>
29 #include <media/IMediaExtractorService.h>
30 #include <cutils/properties.h>
31 #include <utils/String8.h>
32 #include <ziparchive/zip_archive.h>
33 
34 #include <dirent.h>
35 #include <dlfcn.h>
36 
37 namespace android {
38 
39 // static
Create(const sp<DataSource> & source,const char * mime)40 sp<IMediaExtractor> MediaExtractorFactory::Create(
41         const sp<DataSource> &source, const char *mime) {
42     ALOGV("MediaExtractorFactory::Create %s", mime);
43 
44     if (!property_get_bool("media.stagefright.extractremote", true)) {
45         // local extractor
46         ALOGW("creating media extractor in calling process");
47         return CreateFromService(source, mime);
48     } else {
49         // remote extractor
50         ALOGV("get service manager");
51         sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
52 
53         if (binder != 0) {
54             sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
55             sp<IMediaExtractor> ex = mediaExService->makeExtractor(
56                     CreateIDataSourceFromDataSource(source), mime);
57             return ex;
58         } else {
59             ALOGE("extractor service not running");
60             return NULL;
61         }
62     }
63     return NULL;
64 }
65 
CreateFromService(const sp<DataSource> & source,const char * mime)66 sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
67         const sp<DataSource> &source, const char *mime) {
68 
69     ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
70 
71     UpdateExtractors(nullptr);
72 
73     // initialize source decryption if needed
74     source->DrmInitialization(nullptr /* mime */);
75 
76     void *meta = nullptr;
77     MediaExtractor::CreatorFunc creator = NULL;
78     MediaExtractor::FreeMetaFunc freeMeta = nullptr;
79     float confidence;
80     sp<ExtractorPlugin> plugin;
81     creator = sniff(source.get(), &confidence, &meta, &freeMeta, plugin);
82     if (!creator) {
83         ALOGV("FAILED to autodetect media content.");
84         return NULL;
85     }
86 
87     MediaExtractor *ret = creator(source.get(), meta);
88     if (meta != nullptr && freeMeta != nullptr) {
89         freeMeta(meta);
90     }
91 
92     ALOGV("Created an extractor '%s' with confidence %.2f",
93          ret != nullptr ? ret->name() : "<null>", confidence);
94 
95     return CreateIMediaExtractorFromMediaExtractor(ret, source, plugin);
96 }
97 
98 //static
LoadPlugins(const::std::string & apkPath)99 void MediaExtractorFactory::LoadPlugins(const ::std::string& apkPath) {
100     // TODO: Verify apk path with package manager in extractor process.
101     ALOGV("Load plugins from: %s", apkPath.c_str());
102     UpdateExtractors(apkPath.empty() ? nullptr : apkPath.c_str());
103 }
104 
105 struct ExtractorPlugin : public RefBase {
106     MediaExtractor::ExtractorDef def;
107     void *libHandle;
108     String8 libPath;
109     String8 uuidString;
110 
ExtractorPluginandroid::ExtractorPlugin111     ExtractorPlugin(MediaExtractor::ExtractorDef definition, void *handle, String8 &path)
112         : def(definition), libHandle(handle), libPath(path) {
113         for (size_t i = 0; i < sizeof MediaExtractor::ExtractorDef::extractor_uuid; i++) {
114             uuidString.appendFormat("%02x", def.extractor_uuid.b[i]);
115         }
116     }
~ExtractorPluginandroid::ExtractorPlugin117     ~ExtractorPlugin() {
118         if (libHandle != nullptr) {
119             ALOGV("closing handle for %s %d", libPath.c_str(), def.extractor_version);
120             dlclose(libHandle);
121         }
122     }
123 };
124 
125 Mutex MediaExtractorFactory::gPluginMutex;
126 std::shared_ptr<List<sp<ExtractorPlugin>>> MediaExtractorFactory::gPlugins;
127 bool MediaExtractorFactory::gPluginsRegistered = false;
128 
129 // static
sniff(DataSourceBase * source,float * confidence,void ** meta,MediaExtractor::FreeMetaFunc * freeMeta,sp<ExtractorPlugin> & plugin)130 MediaExtractor::CreatorFunc MediaExtractorFactory::sniff(
131         DataSourceBase *source, float *confidence, void **meta,
132         MediaExtractor::FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin) {
133     *confidence = 0.0f;
134     *meta = nullptr;
135 
136     std::shared_ptr<List<sp<ExtractorPlugin>>> plugins;
137     {
138         Mutex::Autolock autoLock(gPluginMutex);
139         if (!gPluginsRegistered) {
140             return NULL;
141         }
142         plugins = gPlugins;
143     }
144 
145     MediaExtractor::CreatorFunc curCreator = NULL;
146     MediaExtractor::CreatorFunc bestCreator = NULL;
147     for (auto it = plugins->begin(); it != plugins->end(); ++it) {
148         float newConfidence;
149         void *newMeta = nullptr;
150         MediaExtractor::FreeMetaFunc newFreeMeta = nullptr;
151         if ((curCreator = (*it)->def.sniff(source, &newConfidence, &newMeta, &newFreeMeta))) {
152             if (newConfidence > *confidence) {
153                 *confidence = newConfidence;
154                 if (*meta != nullptr && *freeMeta != nullptr) {
155                     (*freeMeta)(*meta);
156                 }
157                 *meta = newMeta;
158                 *freeMeta = newFreeMeta;
159                 plugin = *it;
160                 bestCreator = curCreator;
161             } else {
162                 if (newMeta != nullptr && newFreeMeta != nullptr) {
163                     newFreeMeta(newMeta);
164                 }
165             }
166         }
167     }
168 
169     return bestCreator;
170 }
171 
172 // static
RegisterExtractor(const sp<ExtractorPlugin> & plugin,List<sp<ExtractorPlugin>> & pluginList)173 void MediaExtractorFactory::RegisterExtractor(const sp<ExtractorPlugin> &plugin,
174         List<sp<ExtractorPlugin>> &pluginList) {
175     // sanity check check struct version, uuid, name
176     if (plugin->def.def_version == 0
177             || plugin->def.def_version > MediaExtractor::EXTRACTORDEF_VERSION) {
178         ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version);
179         return;
180     }
181     if (memcmp(&plugin->def.extractor_uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
182         ALOGE("invalid UUID, ignoring");
183         return;
184     }
185     if (plugin->def.extractor_name == NULL || strlen(plugin->def.extractor_name) == 0) {
186         ALOGE("extractors should have a name, ignoring");
187         return;
188     }
189 
190     for (auto it = pluginList.begin(); it != pluginList.end(); ++it) {
191         if (memcmp(&((*it)->def.extractor_uuid), &plugin->def.extractor_uuid, 16) == 0) {
192             // there's already an extractor with the same uuid
193             if ((*it)->def.extractor_version < plugin->def.extractor_version) {
194                 // this one is newer, replace the old one
195                 ALOGW("replacing extractor '%s' version %u with version %u",
196                         plugin->def.extractor_name,
197                         (*it)->def.extractor_version,
198                         plugin->def.extractor_version);
199                 pluginList.erase(it);
200                 break;
201             } else {
202                 ALOGW("ignoring extractor '%s' version %u in favor of version %u",
203                         plugin->def.extractor_name,
204                         plugin->def.extractor_version,
205                         (*it)->def.extractor_version);
206                 return;
207             }
208         }
209     }
210     ALOGV("registering extractor for %s", plugin->def.extractor_name);
211     pluginList.push_back(plugin);
212 }
213 
214 //static
RegisterExtractorsInApk(const char * apkPath,List<sp<ExtractorPlugin>> & pluginList)215 void MediaExtractorFactory::RegisterExtractorsInApk(
216         const char *apkPath, List<sp<ExtractorPlugin>> &pluginList) {
217     ALOGV("search for plugins at %s", apkPath);
218     ZipArchiveHandle zipHandle;
219     int32_t ret = OpenArchive(apkPath, &zipHandle);
220     if (ret == 0) {
221         char abi[PROPERTY_VALUE_MAX];
222         property_get("ro.product.cpu.abi", abi, "arm64-v8a");
223         String8 prefix8 = String8::format("lib/%s/", abi);
224         ZipString prefix(prefix8.c_str());
225         ZipString suffix("extractor.so");
226         void* cookie;
227         ret = StartIteration(zipHandle, &cookie, &prefix, &suffix);
228         if (ret == 0) {
229             ZipEntry entry;
230             ZipString name;
231             while (Next(cookie, &entry, &name) == 0) {
232                 String8 libPath = String8(apkPath) + "!/" +
233                     String8(reinterpret_cast<const char*>(name.name), name.name_length);
234                 // TODO: Open with a linker namespace so that it can be linked with sub-libraries
235                 // within the apk instead of system libraries already loaded.
236                 void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL);
237                 if (libHandle) {
238                     MediaExtractor::GetExtractorDef getDef =
239                         (MediaExtractor::GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
240                     if (getDef) {
241                         ALOGV("registering sniffer for %s", libPath.string());
242                         RegisterExtractor(
243                                 new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
244                     } else {
245                         ALOGW("%s does not contain sniffer", libPath.string());
246                         dlclose(libHandle);
247                     }
248                 } else {
249                     ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno));
250                 }
251             }
252             EndIteration(cookie);
253         } else {
254             ALOGW("couldn't find plugins from %s, %d", apkPath, ret);
255         }
256         CloseArchive(zipHandle);
257     } else {
258         ALOGW("couldn't open(%s) %d", apkPath, ret);
259     }
260 }
261 
262 //static
RegisterExtractorsInSystem(const char * libDirPath,List<sp<ExtractorPlugin>> & pluginList)263 void MediaExtractorFactory::RegisterExtractorsInSystem(
264         const char *libDirPath, List<sp<ExtractorPlugin>> &pluginList) {
265     ALOGV("search for plugins at %s", libDirPath);
266     DIR *libDir = opendir(libDirPath);
267     if (libDir) {
268         struct dirent* libEntry;
269         while ((libEntry = readdir(libDir))) {
270             String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
271             void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL);
272             if (libHandle) {
273                 MediaExtractor::GetExtractorDef getDef =
274                     (MediaExtractor::GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
275                 if (getDef) {
276                     ALOGV("registering sniffer for %s", libPath.string());
277                     RegisterExtractor(
278                             new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
279                 } else {
280                     ALOGW("%s does not contain sniffer", libPath.string());
281                     dlclose(libHandle);
282                 }
283             } else {
284                 ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno));
285             }
286         }
287 
288         closedir(libDir);
289     } else {
290         ALOGE("couldn't opendir(%s)", libDirPath);
291     }
292 }
293 
294 // static
UpdateExtractors(const char * newUpdateApkPath)295 void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) {
296     Mutex::Autolock autoLock(gPluginMutex);
297     if (newUpdateApkPath != nullptr) {
298         gPluginsRegistered = false;
299     }
300     if (gPluginsRegistered) {
301         return;
302     }
303 
304     std::shared_ptr<List<sp<ExtractorPlugin>>> newList(new List<sp<ExtractorPlugin>>());
305 
306     RegisterExtractorsInSystem("/system/lib"
307 #ifdef __LP64__
308             "64"
309 #endif
310             "/extractors", *newList);
311 
312     RegisterExtractorsInSystem("/vendor/lib"
313 #ifdef __LP64__
314             "64"
315 #endif
316             "/extractors", *newList);
317 
318     if (newUpdateApkPath != nullptr) {
319         RegisterExtractorsInApk(newUpdateApkPath, *newList);
320     }
321 
322     gPlugins = newList;
323     gPluginsRegistered = true;
324 }
325 
dump(int fd,const Vector<String16> &)326 status_t MediaExtractorFactory::dump(int fd, const Vector<String16>&) {
327     Mutex::Autolock autoLock(gPluginMutex);
328     String8 out;
329     out.append("Available extractors:\n");
330     if (gPluginsRegistered) {
331         for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
332             out.appendFormat("  %25s: uuid(%s), version(%u), path(%s)\n",
333                     (*it)->def.extractor_name,
334                     (*it)->uuidString.c_str(),
335                     (*it)->def.extractor_version,
336                     (*it)->libPath.c_str());
337         }
338     } else {
339         out.append("  (no plugins registered)\n");
340     }
341     write(fd, out.string(), out.size());
342     return OK;
343 }
344 
345 
346 }  // namespace android
347