Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 #filter substitution
2 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 package org.mozilla.gecko.mozglue;
9 import android.content.Context;
10 import android.content.Intent;
11 import android.os.Build;
12 import android.os.Environment;
13 import android.util.Log;
15 import java.io.File;
16 import java.text.DecimalFormat;
17 import java.text.DecimalFormatSymbols;
18 import java.text.NumberFormat;
19 import java.util.Locale;
21 public final class GeckoLoader {
22 private static final String LOGTAG = "GeckoLoader";
24 // This matches AppConstants, but we're built earlier.
25 private static final String ANDROID_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@";
27 private static volatile Intent sIntent;
28 private static File sCacheFile;
29 private static File sGREDir;
31 private static final Object sLibLoadingLock = new Object();
32 // Must hold sLibLoadingLock while accessing the following boolean variables.
33 private static boolean sSQLiteLibsLoaded;
34 private static boolean sNSSLibsLoaded;
35 private static boolean sMozGlueLoaded;
36 private static boolean sLibsSetup;
38 private GeckoLoader() {
39 // prevent instantiation
40 }
42 public static File getCacheDir(Context context) {
43 if (sCacheFile == null) {
44 sCacheFile = context.getCacheDir();
45 }
46 return sCacheFile;
47 }
49 public static File getGREDir(Context context) {
50 if (sGREDir == null) {
51 sGREDir = new File(context.getApplicationInfo().dataDir);
52 }
53 return sGREDir;
54 }
56 private static void setupPluginEnvironment(Context context, String[] pluginDirs) {
57 // setup plugin path directories
58 try {
59 // Check to see if plugins were blocked.
60 if (pluginDirs == null) {
61 putenv("MOZ_PLUGINS_BLOCKED=1");
62 putenv("MOZ_PLUGIN_PATH=");
63 return;
64 }
66 StringBuilder pluginSearchPath = new StringBuilder();
67 for (int i = 0; i < pluginDirs.length; i++) {
68 pluginSearchPath.append(pluginDirs[i]);
69 pluginSearchPath.append(":");
70 }
71 putenv("MOZ_PLUGIN_PATH="+pluginSearchPath);
73 File pluginDataDir = context.getDir("plugins", 0);
74 putenv("ANDROID_PLUGIN_DATADIR=" + pluginDataDir.getPath());
76 File pluginPrivateDataDir = context.getDir("plugins_private", 0);
77 putenv("ANDROID_PLUGIN_DATADIR_PRIVATE=" + pluginPrivateDataDir.getPath());
79 } catch (Exception ex) {
80 Log.w(LOGTAG, "Caught exception getting plugin dirs.", ex);
81 }
82 }
84 private static void setupDownloadEnvironment(Context context) {
85 try {
86 File downloadDir = null;
87 File updatesDir = null;
88 if (Build.VERSION.SDK_INT >= 8) {
89 downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
90 updatesDir = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
91 }
92 if (downloadDir == null) {
93 downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download");
94 }
95 if (updatesDir == null) {
96 updatesDir = downloadDir;
97 }
98 putenv("DOWNLOADS_DIRECTORY=" + downloadDir.getPath());
99 putenv("UPDATES_DIRECTORY=" + updatesDir.getPath());
100 }
101 catch (Exception e) {
102 Log.w(LOGTAG, "No download directory found.", e);
103 }
104 }
106 private static void delTree(File file) {
107 if (file.isDirectory()) {
108 File children[] = file.listFiles();
109 for (File child : children) {
110 delTree(child);
111 }
112 }
113 file.delete();
114 }
116 private static File getTmpDir(Context context) {
117 File tmpDir = context.getDir("tmpdir", Context.MODE_PRIVATE);
118 // check if the old tmp dir is there
119 File oldDir = new File(tmpDir.getParentFile(), "app_tmp");
120 if (oldDir.exists()) {
121 delTree(oldDir);
122 }
123 return tmpDir;
124 }
126 public static void setLastIntent(Intent intent) {
127 sIntent = intent;
128 }
130 public static void setupGeckoEnvironment(Context context, String[] pluginDirs, String profilePath) {
131 // if we have an intent (we're being launched by an activity)
132 // read in any environmental variables from it here
133 final Intent intent = sIntent;
134 if (intent != null) {
135 String env = intent.getStringExtra("env0");
136 Log.d(LOGTAG, "Gecko environment env0: " + env);
137 for (int c = 1; env != null; c++) {
138 putenv(env);
139 env = intent.getStringExtra("env" + c);
140 Log.d(LOGTAG, "env" + c + ": " + env);
141 }
142 }
144 setupPluginEnvironment(context, pluginDirs);
145 setupDownloadEnvironment(context);
147 // profile home path
148 putenv("HOME=" + profilePath);
150 // setup the tmp path
151 File f = getTmpDir(context);
152 if (!f.exists()) {
153 f.mkdirs();
154 }
155 putenv("TMPDIR=" + f.getPath());
157 // setup the downloads path
158 f = Environment.getDownloadCacheDirectory();
159 putenv("EXTERNAL_STORAGE=" + f.getPath());
161 // setup the app-specific cache path
162 f = context.getCacheDir();
163 putenv("CACHE_DIRECTORY=" + f.getPath());
165 /* We really want to use this code, but it requires bumping up the SDK to 17 so for now
166 we will use reflection. See https://bugzilla.mozilla.org/show_bug.cgi?id=811763#c11
168 if (Build.VERSION.SDK_INT >= 17) {
169 android.os.UserManager um = (android.os.UserManager)context.getSystemService(Context.USER_SERVICE);
170 if (um != null) {
171 putenv("MOZ_ANDROID_USER_SERIAL_NUMBER=" + um.getSerialNumberForUser(android.os.Process.myUserHandle()));
172 } else {
173 Log.d(LOGTAG, "Unable to obtain user manager service on a device with SDK version " + Build.VERSION.SDK_INT);
174 }
175 }
176 */
177 try {
178 Object userManager = context.getSystemService("user");
179 if (userManager != null) {
180 // if userManager is non-null that means we're running on 4.2+ and so the rest of this
181 // should just work
182 Object userHandle = android.os.Process.class.getMethod("myUserHandle", (Class[])null).invoke(null);
183 Object userSerial = userManager.getClass().getMethod("getSerialNumberForUser", userHandle.getClass()).invoke(userManager, userHandle);
184 putenv("MOZ_ANDROID_USER_SERIAL_NUMBER=" + userSerial.toString());
185 }
186 } catch (Exception e) {
187 // Guard against any unexpected failures
188 Log.d(LOGTAG, "Unable to set the user serial number", e);
189 }
191 setupLocaleEnvironment();
193 // We don't need this any more.
194 sIntent = null;
195 }
197 private static void loadLibsSetup(Context context) {
198 synchronized (sLibLoadingLock) {
199 if (sLibsSetup) {
200 return;
201 }
202 sLibsSetup = true;
203 }
205 // The package data lib directory isn't placed in ld.so's
206 // search path, so we have to manually load libraries that
207 // libxul will depend on. Not ideal.
209 File cacheFile = getCacheDir(context);
210 putenv("GRE_HOME=" + getGREDir(context).getPath());
212 // setup the libs cache
213 String linkerCache = System.getenv("MOZ_LINKER_CACHE");
214 if (linkerCache == null) {
215 linkerCache = cacheFile.getPath();
216 putenv("MOZ_LINKER_CACHE=" + linkerCache);
217 }
219 // Disable on-demand decompression of the linker on devices where it
220 // is known to cause crashes.
221 if ("HTC".equals(android.os.Build.MANUFACTURER) &&
222 "HTC Vision".equals(android.os.Build.MODEL)) {
223 putenv("MOZ_LINKER_ONDEMAND=0");
224 }
226 #ifdef MOZ_LINKER_EXTRACT
227 putenv("MOZ_LINKER_EXTRACT=1");
228 // Ensure that the cache dir is world-writable
229 File cacheDir = new File(linkerCache);
230 if (cacheDir.isDirectory()) {
231 cacheDir.setWritable(true, false);
232 cacheDir.setExecutable(true, false);
233 cacheDir.setReadable(true, false);
234 }
235 #endif
236 }
238 @RobocopTarget
239 public static void loadSQLiteLibs(Context context, String apkName) {
240 synchronized (sLibLoadingLock) {
241 if (sSQLiteLibsLoaded) {
242 return;
243 }
244 sSQLiteLibsLoaded = true;
245 }
247 loadMozGlue();
248 // the extract libs parameter is being removed in bug 732069
249 loadLibsSetup(context);
250 loadSQLiteLibsNative(apkName, false);
251 }
253 public static void loadNSSLibs(Context context, String apkName) {
254 synchronized (sLibLoadingLock) {
255 if (sNSSLibsLoaded) {
256 return;
257 }
258 sNSSLibsLoaded = true;
259 }
261 loadMozGlue();
262 loadLibsSetup(context);
263 loadNSSLibsNative(apkName, false);
264 }
266 public static void doLoadLibrary(final String lib) {
267 try {
268 System.loadLibrary(lib);
269 } catch (UnsatisfiedLinkError e) {
270 Log.wtf(LOGTAG, "Couldn't load " + lib + ". Trying /data/app-lib path.");
271 try {
272 System.load("/data/app-lib/" + ANDROID_PACKAGE_NAME + "/lib" + lib + ".so");
273 } catch (Throwable ee) {
274 try {
275 Log.wtf(LOGTAG, "Couldn't load " + lib + ": " + ee + ". Trying /data/data path.");
276 System.load("/data/data/" + ANDROID_PACKAGE_NAME + "/lib/lib" + lib + ".so");
277 } catch (Throwable eee) {
278 Log.wtf(LOGTAG, "Failed every attempt to load " + lib + ". Giving up.");
279 throw new RuntimeException("Unable to load " + lib, eee);
280 }
281 }
282 }
283 }
285 public static void loadMozGlue() {
286 synchronized (sLibLoadingLock) {
287 if (sMozGlueLoaded) {
288 return;
289 }
290 sMozGlueLoaded = true;
291 }
293 doLoadLibrary("mozglue");
294 }
296 public static void loadGeckoLibs(Context context, String apkName) {
297 loadLibsSetup(context);
298 loadGeckoLibsNative(apkName);
299 }
301 private static void setupLocaleEnvironment() {
302 putenv("LANG=" + Locale.getDefault().toString());
303 NumberFormat nf = NumberFormat.getInstance();
304 if (nf instanceof DecimalFormat) {
305 DecimalFormat df = (DecimalFormat)nf;
306 DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
308 putenv("LOCALE_DECIMAL_POINT=" + dfs.getDecimalSeparator());
309 putenv("LOCALE_THOUSANDS_SEP=" + dfs.getGroupingSeparator());
310 putenv("LOCALE_GROUPING=" + (char)df.getGroupingSize());
311 }
312 }
314 // These methods are implemented in mozglue/android/nsGeckoUtils.cpp
315 private static native void putenv(String map);
317 // These methods are implemented in mozglue/android/APKOpen.cpp
318 public static native void nativeRun(String args);
319 private static native void loadGeckoLibsNative(String apkName);
320 private static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
321 private static native void loadNSSLibsNative(String apkName, boolean shouldExtract);
322 }