Bug 1267141 - Part3 - Extracting clearkey library and info files into data cache folder.
MozReview-Commit-ID: KudWfPYGf6
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -7,16 +7,18 @@ package org.mozilla.gecko;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.DownloadManager;
import android.content.ContentProviderClient;
import android.os.Environment;
import android.os.Process;
import android.support.annotation.NonNull;
+import android.system.Os;
+import android.system.ErrnoException;
import android.graphics.Rect;
import org.json.JSONArray;
import org.mozilla.gecko.activitystream.ActivityStream;
import org.mozilla.gecko.adjust.AdjustBrowserAppDelegate;
import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.AppConstants.Versions;
@@ -56,16 +58,17 @@ import org.mozilla.gecko.home.HomePager.
import org.mozilla.gecko.home.HomePanelsManager;
import org.mozilla.gecko.home.HomeScreen;
import org.mozilla.gecko.home.SearchEngine;
import org.mozilla.gecko.icons.Icons;
import org.mozilla.gecko.javaaddons.JavaAddonManager;
import org.mozilla.gecko.media.VideoPlayer;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.GeckoMenuItem;
+import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.mozglue.SafeIntent;
import org.mozilla.gecko.notifications.NotificationHelper;
import org.mozilla.gecko.overlays.ui.ShareDialog;
import org.mozilla.gecko.permissions.Permissions;
import org.mozilla.gecko.preferences.ClearOnShutdownPref;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.promotion.AddToHomeScreenPromotion;
import org.mozilla.gecko.delegates.BookmarkStateChangeDelegate;
@@ -97,18 +100,20 @@ import org.mozilla.gecko.updater.PostUpd
import org.mozilla.gecko.updater.UpdateServiceHelper;
import org.mozilla.gecko.util.ActivityUtils;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.ContextUtils;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.FloatUtils;
import org.mozilla.gecko.util.GamepadUtils;
import org.mozilla.gecko.util.GeckoBundle;
+import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.IntentUtils;
+import org.mozilla.gecko.util.IOUtils;
import org.mozilla.gecko.util.MenuUtils;
import org.mozilla.gecko.util.PrefUtils;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.widget.AnchoredPopup;
import org.mozilla.gecko.widget.GeckoActionProvider;
@@ -582,16 +587,104 @@ public class BrowserApp extends GeckoApp
}
if (AndroidGamepadManager.handleKeyEvent(event)) {
return true;
}
return super.onKeyUp(keyCode, event);
}
+ private void ensureParentDirExist(final File file) throws Exception {
+ final File oParentDir = file.getParentFile();
+ if (!oParentDir.exists()) {
+ Log.i(LOGTAG, "Creating " + oParentDir.getAbsolutePath());
+ if (!oParentDir.mkdirs()) {
+ final String description = "Unable to create directories: " + oParentDir.getAbsolutePath();
+ throw new Exception(description);
+ }
+ }
+ }
+
+ /**
+ * Copies files under /assets/@ANDROID_CPU_ARCH@/PLUGIN_NAME/PLUGIN_VER out
+ * of the APK and into the app's cache directory.
+ */
+ private void extractClearKeyFiles(final String dstClearkeyPath) {
+ File dstDirFile = new File(dstClearkeyPath);
+ File dstParentDirFile = new File(dstDirFile.getParent());
+ Log.i(LOGTAG, " extracting clearkey files to ... " + dstClearkeyPath + " . parent = " + dstDirFile.getParent());
+ final String plugInName = dstParentDirFile.getName();
+ final String plugInVersion = dstDirFile.getName();
+ String schemeName = "";
+ if (plugInName.startsWith("gmp-")) {
+ schemeName = plugInName.substring(4);
+ } else {
+ GeckoAppShell.notifyObservers("EME:ExtractClearkeyDone", "");
+ return;
+ }
+ final String libName = "lib" + schemeName + ".so";
+ final String infoName = "manifest.json";
+ final String pkgPath = getContext().getPackageResourcePath();
+ final String libPath = plugInName + File.separator +
+ plugInVersion + File.separator +
+ libName;
+ final String assetsInfoPath = "assets" + File.separator +
+ android.os.Build.CPU_ABI + File.separator +
+ plugInName + File.separator +
+ plugInVersion + File.separator +
+ infoName;
+
+ final String srcinfoPath = "jar:file://" + pkgPath + "!" + File.separator + assetsInfoPath;
+ final String dstLibPath = dstClearkeyPath + File.separator + libName;
+ final String dstInfoPath = dstClearkeyPath + File.separator + infoName;
+
+ try {
+
+ try {
+ // Extract compressed libclearkey.so file to cache foler.
+ GeckoLoader.extractGeckoLibs(getContext(),
+ pkgPath,
+ libPath);
+ // Create symlink to the gmp-clearkey folder if needed.
+ final File oDstLibFile = new File(dstLibPath);
+ if (!oDstLibFile.exists()) {
+ try {
+ ensureParentDirExist(oDstLibFile);
+ Os.symlink(getContext().getCacheDir().getAbsolutePath() + File.separator + libName,
+ dstLibPath);
+ } catch(ErrnoException e) {
+ Log.i(LOGTAG, "Error create symlink failed." + e);
+ }
+ }
+ } catch (Exception e) {
+ Log.i(LOGTAG, "Error extracting '" + libPath + "' from APK to dst...e = " + e);
+ }
+
+ final File oInfoFile = new File(dstInfoPath);
+ if (!oInfoFile.exists()) {
+ // Extract compressed .info file to destination path.
+ java.io.InputStream inStream = GeckoJarReader.getStream(getContext(),
+ srcinfoPath);
+ final java.io.OutputStream outStream = new java.io.FileOutputStream(oInfoFile);
+ try {
+ IOUtils.copy(inStream, outStream);
+ } catch (IOException e) {
+ Log.i(LOGTAG, "Error copying '" + srcinfoPath + "' from APK to dst...e = " + e);
+ } finally {
+ IOUtils.safeStreamClose(inStream);
+ IOUtils.safeStreamClose(outStream);
+ }
+ }
+ GeckoAppShell.notifyObservers("EME:ExtractClearkeyDone", dstClearkeyPath);
+ } catch (Throwable e) {
+ Log.i(LOGTAG, "Error extracting Clearkey related files, e :" + e);
+ GeckoAppShell.notifyObservers("EME:ExtractClearkeyDone", "");
+ }
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
if (!HardwareUtils.isSupportedSystem()) {
// This build does not support the Android version of the device; Exit early.
super.onCreate(savedInstanceState);
return;
}
@@ -748,16 +841,17 @@ public class BrowserApp extends GeckoApp
"CharEncoding:Data",
"CharEncoding:State",
"Settings:Show",
"Updater:Launch",
"Sanitize:OpenTabs",
null);
EventDispatcher.getInstance().registerBackgroundThreadListener(this,
+ "EME:ExtractClearkey",
"Experiments:GetActive",
"Experiments:SetOverride",
"Experiments:ClearOverride",
"Favicon:Request",
"Feedback:MaybeLater",
"Sanitize:ClearHistory",
"Sanitize:ClearSyncedTabs",
"Telemetry:Gather",
@@ -1440,16 +1534,17 @@ public class BrowserApp extends GeckoApp
if (mZoomedView != null) {
mZoomedView.destroy();
}
mSearchEngineManager.unregisterListeners();
EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
"Search:Keyword",
+ "EME:ExtractClearkey",
null);
EventDispatcher.getInstance().unregisterUiThreadListener(this,
"Menu:Open",
"Menu:Update",
"Menu:Add",
"Menu:Remove",
"LightweightTheme:Update",
@@ -1775,16 +1870,21 @@ public class BrowserApp extends GeckoApp
// TODO: Better scheduling of DLC actions (Bug 1257492)
DownloadContentService.startSync(this);
DownloadContentService.startVerification(this);
}
FeedService.setup(this);
break;
+ case "EME:ExtractClearkey":
+ final String dstClearkeyDir = message.getString("targetpath");
+ extractClearKeyFiles(dstClearkeyDir);
+ break;
+
case "Menu:Open":
if (mBrowserToolbar.isEditing()) {
mBrowserToolbar.cancelEdit();
}
openOptionsMenu();
break;
case "Menu:Update":
--- a/toolkit/mozapps/extensions/internal/GMPProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
@@ -23,16 +23,18 @@ Cu.import("resource://gre/modules/GMPUti
/* globals GMP_PLUGIN_IDS, GMPPrefs, GMPUtils, OPEN_H264_ID, WIDEVINE_ID */
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/UpdateUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(
this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm");
XPCOMUtils.defineLazyModuleGetter(
this, "setTimeout", "resource://gre/modules/Timer.jsm");
+XPCOMUtils.defineLazyModuleGetter(
+ this, "Messaging", "resource://gre/modules/Messaging.jsm");
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
const STRING_TYPE_NAME = "type.%ID%.name";
const SEC_IN_A_DAY = 24 * 60 * 60;
// How long to wait after a user enabled EME before attempting to download CDMs.
const GMP_CHECK_DELAY = 10 * 1000; // milliseconds
@@ -518,20 +520,40 @@ GMPWrapper.prototype = {
},
};
var GMPProvider = {
get name() { return "GMPProvider"; },
_plugins: null,
+ observe: function observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "EME:ExtractClearkeyDone":
+ this._log.info("Receiving topic - 'EME:ExtractClearkeyDone'");
+ if (aData) {
+ gmpService.addPluginDirectory(aData);
+ } else {
+ this._log.info("EME:ExtractClearkeyDone - adding gmp directory failed ");
+ }
+ break;
+ default:
+ break;
+ }
+ },
+
startup() {
+
configureLogging();
this._log = Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP",
"GMPProvider.");
+ if (AppConstants.platform === "android") {
+ Services.obs.addObserver(this, "EME:ExtractClearkeyDone", false);
+ }
+
this.buildPluginList();
this.ensureProperCDMInstallState();
Preferences.observe(GMPPrefs.KEY_LOG_BASE, configureLogging);
for (let plugin of this._plugins.values()) {
let wrapper = plugin.wrapper;
let gmpPath = wrapper.gmpPath;
@@ -563,29 +585,46 @@ var GMPProvider = {
e.name + " - sandboxing not available?", e);
}
}
}
try {
let greDir = Services.dirsvc.get(NS_GRE_DIR,
Ci.nsILocalFile);
- let clearkeyPath = OS.Path.join(greDir.path,
- CLEARKEY_PLUGIN_ID,
- CLEARKEY_VERSION);
- this._log.info("startup - adding clearkey CDM directory " +
- clearkeyPath);
- gmpService.addPluginDirectory(clearkeyPath);
+ let clearkeyPath;
+ if (AppConstants.platform === "android") {
+ clearkeyPath = OS.Path.join(greDir.path,
+ "cache",
+ CLEARKEY_PLUGIN_ID,
+ CLEARKEY_VERSION);
+ this._log.info("startup - request clearkey lib extraction " +
+ clearkeyPath);
+ Messaging.sendRequest({type: "EME:ExtractClearkey",
+ targetpath: clearkeyPath });
+ } else {
+ clearkeyPath = OS.Path.join(greDir.path,
+ CLEARKEY_PLUGIN_ID,
+ CLEARKEY_VERSION);
+ this._log.info("startup - adding clearkey CDM directory " +
+ clearkeyPath);
+ gmpService.addPluginDirectory(clearkeyPath);
+ }
} catch (e) {
this._log.warn("startup - adding clearkey CDM failed", e);
}
},
shutdown() {
this._log.trace("shutdown");
+
+ if (AppConstants.platform === "android") {
+ Services.obs.removeObserver(this, "EME:ExtractClearkeyDone");
+ }
+
Preferences.ignore(GMPPrefs.KEY_LOG_BASE, configureLogging);
let shutdownTask = Task.spawn(function*() {
this._log.trace("shutdown - shutdownTask");
let shutdownSucceeded = true;
for (let plugin of this._plugins.values()) {
try {