1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/mozglue/GeckoLoader.java.in Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,322 @@ 1.4 +#filter substitution 1.5 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +package org.mozilla.gecko.mozglue; 1.11 + 1.12 +import android.content.Context; 1.13 +import android.content.Intent; 1.14 +import android.os.Build; 1.15 +import android.os.Environment; 1.16 +import android.util.Log; 1.17 + 1.18 +import java.io.File; 1.19 +import java.text.DecimalFormat; 1.20 +import java.text.DecimalFormatSymbols; 1.21 +import java.text.NumberFormat; 1.22 +import java.util.Locale; 1.23 + 1.24 +public final class GeckoLoader { 1.25 + private static final String LOGTAG = "GeckoLoader"; 1.26 + 1.27 + // This matches AppConstants, but we're built earlier. 1.28 + private static final String ANDROID_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@"; 1.29 + 1.30 + private static volatile Intent sIntent; 1.31 + private static File sCacheFile; 1.32 + private static File sGREDir; 1.33 + 1.34 + private static final Object sLibLoadingLock = new Object(); 1.35 + // Must hold sLibLoadingLock while accessing the following boolean variables. 1.36 + private static boolean sSQLiteLibsLoaded; 1.37 + private static boolean sNSSLibsLoaded; 1.38 + private static boolean sMozGlueLoaded; 1.39 + private static boolean sLibsSetup; 1.40 + 1.41 + private GeckoLoader() { 1.42 + // prevent instantiation 1.43 + } 1.44 + 1.45 + public static File getCacheDir(Context context) { 1.46 + if (sCacheFile == null) { 1.47 + sCacheFile = context.getCacheDir(); 1.48 + } 1.49 + return sCacheFile; 1.50 + } 1.51 + 1.52 + public static File getGREDir(Context context) { 1.53 + if (sGREDir == null) { 1.54 + sGREDir = new File(context.getApplicationInfo().dataDir); 1.55 + } 1.56 + return sGREDir; 1.57 + } 1.58 + 1.59 + private static void setupPluginEnvironment(Context context, String[] pluginDirs) { 1.60 + // setup plugin path directories 1.61 + try { 1.62 + // Check to see if plugins were blocked. 1.63 + if (pluginDirs == null) { 1.64 + putenv("MOZ_PLUGINS_BLOCKED=1"); 1.65 + putenv("MOZ_PLUGIN_PATH="); 1.66 + return; 1.67 + } 1.68 + 1.69 + StringBuilder pluginSearchPath = new StringBuilder(); 1.70 + for (int i = 0; i < pluginDirs.length; i++) { 1.71 + pluginSearchPath.append(pluginDirs[i]); 1.72 + pluginSearchPath.append(":"); 1.73 + } 1.74 + putenv("MOZ_PLUGIN_PATH="+pluginSearchPath); 1.75 + 1.76 + File pluginDataDir = context.getDir("plugins", 0); 1.77 + putenv("ANDROID_PLUGIN_DATADIR=" + pluginDataDir.getPath()); 1.78 + 1.79 + File pluginPrivateDataDir = context.getDir("plugins_private", 0); 1.80 + putenv("ANDROID_PLUGIN_DATADIR_PRIVATE=" + pluginPrivateDataDir.getPath()); 1.81 + 1.82 + } catch (Exception ex) { 1.83 + Log.w(LOGTAG, "Caught exception getting plugin dirs.", ex); 1.84 + } 1.85 + } 1.86 + 1.87 + private static void setupDownloadEnvironment(Context context) { 1.88 + try { 1.89 + File downloadDir = null; 1.90 + File updatesDir = null; 1.91 + if (Build.VERSION.SDK_INT >= 8) { 1.92 + downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); 1.93 + updatesDir = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); 1.94 + } 1.95 + if (downloadDir == null) { 1.96 + downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download"); 1.97 + } 1.98 + if (updatesDir == null) { 1.99 + updatesDir = downloadDir; 1.100 + } 1.101 + putenv("DOWNLOADS_DIRECTORY=" + downloadDir.getPath()); 1.102 + putenv("UPDATES_DIRECTORY=" + updatesDir.getPath()); 1.103 + } 1.104 + catch (Exception e) { 1.105 + Log.w(LOGTAG, "No download directory found.", e); 1.106 + } 1.107 + } 1.108 + 1.109 + private static void delTree(File file) { 1.110 + if (file.isDirectory()) { 1.111 + File children[] = file.listFiles(); 1.112 + for (File child : children) { 1.113 + delTree(child); 1.114 + } 1.115 + } 1.116 + file.delete(); 1.117 + } 1.118 + 1.119 + private static File getTmpDir(Context context) { 1.120 + File tmpDir = context.getDir("tmpdir", Context.MODE_PRIVATE); 1.121 + // check if the old tmp dir is there 1.122 + File oldDir = new File(tmpDir.getParentFile(), "app_tmp"); 1.123 + if (oldDir.exists()) { 1.124 + delTree(oldDir); 1.125 + } 1.126 + return tmpDir; 1.127 + } 1.128 + 1.129 + public static void setLastIntent(Intent intent) { 1.130 + sIntent = intent; 1.131 + } 1.132 + 1.133 + public static void setupGeckoEnvironment(Context context, String[] pluginDirs, String profilePath) { 1.134 + // if we have an intent (we're being launched by an activity) 1.135 + // read in any environmental variables from it here 1.136 + final Intent intent = sIntent; 1.137 + if (intent != null) { 1.138 + String env = intent.getStringExtra("env0"); 1.139 + Log.d(LOGTAG, "Gecko environment env0: " + env); 1.140 + for (int c = 1; env != null; c++) { 1.141 + putenv(env); 1.142 + env = intent.getStringExtra("env" + c); 1.143 + Log.d(LOGTAG, "env" + c + ": " + env); 1.144 + } 1.145 + } 1.146 + 1.147 + setupPluginEnvironment(context, pluginDirs); 1.148 + setupDownloadEnvironment(context); 1.149 + 1.150 + // profile home path 1.151 + putenv("HOME=" + profilePath); 1.152 + 1.153 + // setup the tmp path 1.154 + File f = getTmpDir(context); 1.155 + if (!f.exists()) { 1.156 + f.mkdirs(); 1.157 + } 1.158 + putenv("TMPDIR=" + f.getPath()); 1.159 + 1.160 + // setup the downloads path 1.161 + f = Environment.getDownloadCacheDirectory(); 1.162 + putenv("EXTERNAL_STORAGE=" + f.getPath()); 1.163 + 1.164 + // setup the app-specific cache path 1.165 + f = context.getCacheDir(); 1.166 + putenv("CACHE_DIRECTORY=" + f.getPath()); 1.167 + 1.168 + /* We really want to use this code, but it requires bumping up the SDK to 17 so for now 1.169 + we will use reflection. See https://bugzilla.mozilla.org/show_bug.cgi?id=811763#c11 1.170 + 1.171 + if (Build.VERSION.SDK_INT >= 17) { 1.172 + android.os.UserManager um = (android.os.UserManager)context.getSystemService(Context.USER_SERVICE); 1.173 + if (um != null) { 1.174 + putenv("MOZ_ANDROID_USER_SERIAL_NUMBER=" + um.getSerialNumberForUser(android.os.Process.myUserHandle())); 1.175 + } else { 1.176 + Log.d(LOGTAG, "Unable to obtain user manager service on a device with SDK version " + Build.VERSION.SDK_INT); 1.177 + } 1.178 + } 1.179 + */ 1.180 + try { 1.181 + Object userManager = context.getSystemService("user"); 1.182 + if (userManager != null) { 1.183 + // if userManager is non-null that means we're running on 4.2+ and so the rest of this 1.184 + // should just work 1.185 + Object userHandle = android.os.Process.class.getMethod("myUserHandle", (Class[])null).invoke(null); 1.186 + Object userSerial = userManager.getClass().getMethod("getSerialNumberForUser", userHandle.getClass()).invoke(userManager, userHandle); 1.187 + putenv("MOZ_ANDROID_USER_SERIAL_NUMBER=" + userSerial.toString()); 1.188 + } 1.189 + } catch (Exception e) { 1.190 + // Guard against any unexpected failures 1.191 + Log.d(LOGTAG, "Unable to set the user serial number", e); 1.192 + } 1.193 + 1.194 + setupLocaleEnvironment(); 1.195 + 1.196 + // We don't need this any more. 1.197 + sIntent = null; 1.198 + } 1.199 + 1.200 + private static void loadLibsSetup(Context context) { 1.201 + synchronized (sLibLoadingLock) { 1.202 + if (sLibsSetup) { 1.203 + return; 1.204 + } 1.205 + sLibsSetup = true; 1.206 + } 1.207 + 1.208 + // The package data lib directory isn't placed in ld.so's 1.209 + // search path, so we have to manually load libraries that 1.210 + // libxul will depend on. Not ideal. 1.211 + 1.212 + File cacheFile = getCacheDir(context); 1.213 + putenv("GRE_HOME=" + getGREDir(context).getPath()); 1.214 + 1.215 + // setup the libs cache 1.216 + String linkerCache = System.getenv("MOZ_LINKER_CACHE"); 1.217 + if (linkerCache == null) { 1.218 + linkerCache = cacheFile.getPath(); 1.219 + putenv("MOZ_LINKER_CACHE=" + linkerCache); 1.220 + } 1.221 + 1.222 + // Disable on-demand decompression of the linker on devices where it 1.223 + // is known to cause crashes. 1.224 + if ("HTC".equals(android.os.Build.MANUFACTURER) && 1.225 + "HTC Vision".equals(android.os.Build.MODEL)) { 1.226 + putenv("MOZ_LINKER_ONDEMAND=0"); 1.227 + } 1.228 + 1.229 +#ifdef MOZ_LINKER_EXTRACT 1.230 + putenv("MOZ_LINKER_EXTRACT=1"); 1.231 + // Ensure that the cache dir is world-writable 1.232 + File cacheDir = new File(linkerCache); 1.233 + if (cacheDir.isDirectory()) { 1.234 + cacheDir.setWritable(true, false); 1.235 + cacheDir.setExecutable(true, false); 1.236 + cacheDir.setReadable(true, false); 1.237 + } 1.238 +#endif 1.239 + } 1.240 + 1.241 + @RobocopTarget 1.242 + public static void loadSQLiteLibs(Context context, String apkName) { 1.243 + synchronized (sLibLoadingLock) { 1.244 + if (sSQLiteLibsLoaded) { 1.245 + return; 1.246 + } 1.247 + sSQLiteLibsLoaded = true; 1.248 + } 1.249 + 1.250 + loadMozGlue(); 1.251 + // the extract libs parameter is being removed in bug 732069 1.252 + loadLibsSetup(context); 1.253 + loadSQLiteLibsNative(apkName, false); 1.254 + } 1.255 + 1.256 + public static void loadNSSLibs(Context context, String apkName) { 1.257 + synchronized (sLibLoadingLock) { 1.258 + if (sNSSLibsLoaded) { 1.259 + return; 1.260 + } 1.261 + sNSSLibsLoaded = true; 1.262 + } 1.263 + 1.264 + loadMozGlue(); 1.265 + loadLibsSetup(context); 1.266 + loadNSSLibsNative(apkName, false); 1.267 + } 1.268 + 1.269 + public static void doLoadLibrary(final String lib) { 1.270 + try { 1.271 + System.loadLibrary(lib); 1.272 + } catch (UnsatisfiedLinkError e) { 1.273 + Log.wtf(LOGTAG, "Couldn't load " + lib + ". Trying /data/app-lib path."); 1.274 + try { 1.275 + System.load("/data/app-lib/" + ANDROID_PACKAGE_NAME + "/lib" + lib + ".so"); 1.276 + } catch (Throwable ee) { 1.277 + try { 1.278 + Log.wtf(LOGTAG, "Couldn't load " + lib + ": " + ee + ". Trying /data/data path."); 1.279 + System.load("/data/data/" + ANDROID_PACKAGE_NAME + "/lib/lib" + lib + ".so"); 1.280 + } catch (Throwable eee) { 1.281 + Log.wtf(LOGTAG, "Failed every attempt to load " + lib + ". Giving up."); 1.282 + throw new RuntimeException("Unable to load " + lib, eee); 1.283 + } 1.284 + } 1.285 + } 1.286 + } 1.287 + 1.288 + public static void loadMozGlue() { 1.289 + synchronized (sLibLoadingLock) { 1.290 + if (sMozGlueLoaded) { 1.291 + return; 1.292 + } 1.293 + sMozGlueLoaded = true; 1.294 + } 1.295 + 1.296 + doLoadLibrary("mozglue"); 1.297 + } 1.298 + 1.299 + public static void loadGeckoLibs(Context context, String apkName) { 1.300 + loadLibsSetup(context); 1.301 + loadGeckoLibsNative(apkName); 1.302 + } 1.303 + 1.304 + private static void setupLocaleEnvironment() { 1.305 + putenv("LANG=" + Locale.getDefault().toString()); 1.306 + NumberFormat nf = NumberFormat.getInstance(); 1.307 + if (nf instanceof DecimalFormat) { 1.308 + DecimalFormat df = (DecimalFormat)nf; 1.309 + DecimalFormatSymbols dfs = df.getDecimalFormatSymbols(); 1.310 + 1.311 + putenv("LOCALE_DECIMAL_POINT=" + dfs.getDecimalSeparator()); 1.312 + putenv("LOCALE_THOUSANDS_SEP=" + dfs.getGroupingSeparator()); 1.313 + putenv("LOCALE_GROUPING=" + (char)df.getGroupingSize()); 1.314 + } 1.315 + } 1.316 + 1.317 + // These methods are implemented in mozglue/android/nsGeckoUtils.cpp 1.318 + private static native void putenv(String map); 1.319 + 1.320 + // These methods are implemented in mozglue/android/APKOpen.cpp 1.321 + public static native void nativeRun(String args); 1.322 + private static native void loadGeckoLibsNative(String apkName); 1.323 + private static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract); 1.324 + private static native void loadNSSLibsNative(String apkName, boolean shouldExtract); 1.325 +}