diff -r 000000000000 -r 6474c204b198 mobile/android/base/mozglue/GeckoLoader.java.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mobile/android/base/mozglue/GeckoLoader.java.in Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,322 @@ +#filter substitution +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.mozglue; + +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Environment; +import android.util.Log; + +import java.io.File; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Locale; + +public final class GeckoLoader { + private static final String LOGTAG = "GeckoLoader"; + + // This matches AppConstants, but we're built earlier. + private static final String ANDROID_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@"; + + private static volatile Intent sIntent; + private static File sCacheFile; + private static File sGREDir; + + private static final Object sLibLoadingLock = new Object(); + // Must hold sLibLoadingLock while accessing the following boolean variables. + private static boolean sSQLiteLibsLoaded; + private static boolean sNSSLibsLoaded; + private static boolean sMozGlueLoaded; + private static boolean sLibsSetup; + + private GeckoLoader() { + // prevent instantiation + } + + public static File getCacheDir(Context context) { + if (sCacheFile == null) { + sCacheFile = context.getCacheDir(); + } + return sCacheFile; + } + + public static File getGREDir(Context context) { + if (sGREDir == null) { + sGREDir = new File(context.getApplicationInfo().dataDir); + } + return sGREDir; + } + + private static void setupPluginEnvironment(Context context, String[] pluginDirs) { + // setup plugin path directories + try { + // Check to see if plugins were blocked. + if (pluginDirs == null) { + putenv("MOZ_PLUGINS_BLOCKED=1"); + putenv("MOZ_PLUGIN_PATH="); + return; + } + + StringBuilder pluginSearchPath = new StringBuilder(); + for (int i = 0; i < pluginDirs.length; i++) { + pluginSearchPath.append(pluginDirs[i]); + pluginSearchPath.append(":"); + } + putenv("MOZ_PLUGIN_PATH="+pluginSearchPath); + + File pluginDataDir = context.getDir("plugins", 0); + putenv("ANDROID_PLUGIN_DATADIR=" + pluginDataDir.getPath()); + + File pluginPrivateDataDir = context.getDir("plugins_private", 0); + putenv("ANDROID_PLUGIN_DATADIR_PRIVATE=" + pluginPrivateDataDir.getPath()); + + } catch (Exception ex) { + Log.w(LOGTAG, "Caught exception getting plugin dirs.", ex); + } + } + + private static void setupDownloadEnvironment(Context context) { + try { + File downloadDir = null; + File updatesDir = null; + if (Build.VERSION.SDK_INT >= 8) { + downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + updatesDir = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); + } + if (downloadDir == null) { + downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download"); + } + if (updatesDir == null) { + updatesDir = downloadDir; + } + putenv("DOWNLOADS_DIRECTORY=" + downloadDir.getPath()); + putenv("UPDATES_DIRECTORY=" + updatesDir.getPath()); + } + catch (Exception e) { + Log.w(LOGTAG, "No download directory found.", e); + } + } + + private static void delTree(File file) { + if (file.isDirectory()) { + File children[] = file.listFiles(); + for (File child : children) { + delTree(child); + } + } + file.delete(); + } + + private static File getTmpDir(Context context) { + File tmpDir = context.getDir("tmpdir", Context.MODE_PRIVATE); + // check if the old tmp dir is there + File oldDir = new File(tmpDir.getParentFile(), "app_tmp"); + if (oldDir.exists()) { + delTree(oldDir); + } + return tmpDir; + } + + public static void setLastIntent(Intent intent) { + sIntent = intent; + } + + public static void setupGeckoEnvironment(Context context, String[] pluginDirs, String profilePath) { + // if we have an intent (we're being launched by an activity) + // read in any environmental variables from it here + final Intent intent = sIntent; + if (intent != null) { + String env = intent.getStringExtra("env0"); + Log.d(LOGTAG, "Gecko environment env0: " + env); + for (int c = 1; env != null; c++) { + putenv(env); + env = intent.getStringExtra("env" + c); + Log.d(LOGTAG, "env" + c + ": " + env); + } + } + + setupPluginEnvironment(context, pluginDirs); + setupDownloadEnvironment(context); + + // profile home path + putenv("HOME=" + profilePath); + + // setup the tmp path + File f = getTmpDir(context); + if (!f.exists()) { + f.mkdirs(); + } + putenv("TMPDIR=" + f.getPath()); + + // setup the downloads path + f = Environment.getDownloadCacheDirectory(); + putenv("EXTERNAL_STORAGE=" + f.getPath()); + + // setup the app-specific cache path + f = context.getCacheDir(); + putenv("CACHE_DIRECTORY=" + f.getPath()); + + /* We really want to use this code, but it requires bumping up the SDK to 17 so for now + we will use reflection. See https://bugzilla.mozilla.org/show_bug.cgi?id=811763#c11 + + if (Build.VERSION.SDK_INT >= 17) { + android.os.UserManager um = (android.os.UserManager)context.getSystemService(Context.USER_SERVICE); + if (um != null) { + putenv("MOZ_ANDROID_USER_SERIAL_NUMBER=" + um.getSerialNumberForUser(android.os.Process.myUserHandle())); + } else { + Log.d(LOGTAG, "Unable to obtain user manager service on a device with SDK version " + Build.VERSION.SDK_INT); + } + } + */ + try { + Object userManager = context.getSystemService("user"); + if (userManager != null) { + // if userManager is non-null that means we're running on 4.2+ and so the rest of this + // should just work + Object userHandle = android.os.Process.class.getMethod("myUserHandle", (Class[])null).invoke(null); + Object userSerial = userManager.getClass().getMethod("getSerialNumberForUser", userHandle.getClass()).invoke(userManager, userHandle); + putenv("MOZ_ANDROID_USER_SERIAL_NUMBER=" + userSerial.toString()); + } + } catch (Exception e) { + // Guard against any unexpected failures + Log.d(LOGTAG, "Unable to set the user serial number", e); + } + + setupLocaleEnvironment(); + + // We don't need this any more. + sIntent = null; + } + + private static void loadLibsSetup(Context context) { + synchronized (sLibLoadingLock) { + if (sLibsSetup) { + return; + } + sLibsSetup = true; + } + + // The package data lib directory isn't placed in ld.so's + // search path, so we have to manually load libraries that + // libxul will depend on. Not ideal. + + File cacheFile = getCacheDir(context); + putenv("GRE_HOME=" + getGREDir(context).getPath()); + + // setup the libs cache + String linkerCache = System.getenv("MOZ_LINKER_CACHE"); + if (linkerCache == null) { + linkerCache = cacheFile.getPath(); + putenv("MOZ_LINKER_CACHE=" + linkerCache); + } + + // Disable on-demand decompression of the linker on devices where it + // is known to cause crashes. + if ("HTC".equals(android.os.Build.MANUFACTURER) && + "HTC Vision".equals(android.os.Build.MODEL)) { + putenv("MOZ_LINKER_ONDEMAND=0"); + } + +#ifdef MOZ_LINKER_EXTRACT + putenv("MOZ_LINKER_EXTRACT=1"); + // Ensure that the cache dir is world-writable + File cacheDir = new File(linkerCache); + if (cacheDir.isDirectory()) { + cacheDir.setWritable(true, false); + cacheDir.setExecutable(true, false); + cacheDir.setReadable(true, false); + } +#endif + } + + @RobocopTarget + public static void loadSQLiteLibs(Context context, String apkName) { + synchronized (sLibLoadingLock) { + if (sSQLiteLibsLoaded) { + return; + } + sSQLiteLibsLoaded = true; + } + + loadMozGlue(); + // the extract libs parameter is being removed in bug 732069 + loadLibsSetup(context); + loadSQLiteLibsNative(apkName, false); + } + + public static void loadNSSLibs(Context context, String apkName) { + synchronized (sLibLoadingLock) { + if (sNSSLibsLoaded) { + return; + } + sNSSLibsLoaded = true; + } + + loadMozGlue(); + loadLibsSetup(context); + loadNSSLibsNative(apkName, false); + } + + public static void doLoadLibrary(final String lib) { + try { + System.loadLibrary(lib); + } catch (UnsatisfiedLinkError e) { + Log.wtf(LOGTAG, "Couldn't load " + lib + ". Trying /data/app-lib path."); + try { + System.load("/data/app-lib/" + ANDROID_PACKAGE_NAME + "/lib" + lib + ".so"); + } catch (Throwable ee) { + try { + Log.wtf(LOGTAG, "Couldn't load " + lib + ": " + ee + ". Trying /data/data path."); + System.load("/data/data/" + ANDROID_PACKAGE_NAME + "/lib/lib" + lib + ".so"); + } catch (Throwable eee) { + Log.wtf(LOGTAG, "Failed every attempt to load " + lib + ". Giving up."); + throw new RuntimeException("Unable to load " + lib, eee); + } + } + } + } + + public static void loadMozGlue() { + synchronized (sLibLoadingLock) { + if (sMozGlueLoaded) { + return; + } + sMozGlueLoaded = true; + } + + doLoadLibrary("mozglue"); + } + + public static void loadGeckoLibs(Context context, String apkName) { + loadLibsSetup(context); + loadGeckoLibsNative(apkName); + } + + private static void setupLocaleEnvironment() { + putenv("LANG=" + Locale.getDefault().toString()); + NumberFormat nf = NumberFormat.getInstance(); + if (nf instanceof DecimalFormat) { + DecimalFormat df = (DecimalFormat)nf; + DecimalFormatSymbols dfs = df.getDecimalFormatSymbols(); + + putenv("LOCALE_DECIMAL_POINT=" + dfs.getDecimalSeparator()); + putenv("LOCALE_THOUSANDS_SEP=" + dfs.getGroupingSeparator()); + putenv("LOCALE_GROUPING=" + (char)df.getGroupingSize()); + } + } + + // These methods are implemented in mozglue/android/nsGeckoUtils.cpp + private static native void putenv(String map); + + // These methods are implemented in mozglue/android/APKOpen.cpp + public static native void nativeRun(String args); + private static native void loadGeckoLibsNative(String apkName); + private static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract); + private static native void loadNSSLibsNative(String apkName, boolean shouldExtract); +}