|
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/. */ |
|
6 |
|
7 package org.mozilla.gecko.mozglue; |
|
8 |
|
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; |
|
14 |
|
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; |
|
20 |
|
21 public final class GeckoLoader { |
|
22 private static final String LOGTAG = "GeckoLoader"; |
|
23 |
|
24 // This matches AppConstants, but we're built earlier. |
|
25 private static final String ANDROID_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@"; |
|
26 |
|
27 private static volatile Intent sIntent; |
|
28 private static File sCacheFile; |
|
29 private static File sGREDir; |
|
30 |
|
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; |
|
37 |
|
38 private GeckoLoader() { |
|
39 // prevent instantiation |
|
40 } |
|
41 |
|
42 public static File getCacheDir(Context context) { |
|
43 if (sCacheFile == null) { |
|
44 sCacheFile = context.getCacheDir(); |
|
45 } |
|
46 return sCacheFile; |
|
47 } |
|
48 |
|
49 public static File getGREDir(Context context) { |
|
50 if (sGREDir == null) { |
|
51 sGREDir = new File(context.getApplicationInfo().dataDir); |
|
52 } |
|
53 return sGREDir; |
|
54 } |
|
55 |
|
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 } |
|
65 |
|
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); |
|
72 |
|
73 File pluginDataDir = context.getDir("plugins", 0); |
|
74 putenv("ANDROID_PLUGIN_DATADIR=" + pluginDataDir.getPath()); |
|
75 |
|
76 File pluginPrivateDataDir = context.getDir("plugins_private", 0); |
|
77 putenv("ANDROID_PLUGIN_DATADIR_PRIVATE=" + pluginPrivateDataDir.getPath()); |
|
78 |
|
79 } catch (Exception ex) { |
|
80 Log.w(LOGTAG, "Caught exception getting plugin dirs.", ex); |
|
81 } |
|
82 } |
|
83 |
|
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 } |
|
105 |
|
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 } |
|
115 |
|
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 } |
|
125 |
|
126 public static void setLastIntent(Intent intent) { |
|
127 sIntent = intent; |
|
128 } |
|
129 |
|
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 } |
|
143 |
|
144 setupPluginEnvironment(context, pluginDirs); |
|
145 setupDownloadEnvironment(context); |
|
146 |
|
147 // profile home path |
|
148 putenv("HOME=" + profilePath); |
|
149 |
|
150 // setup the tmp path |
|
151 File f = getTmpDir(context); |
|
152 if (!f.exists()) { |
|
153 f.mkdirs(); |
|
154 } |
|
155 putenv("TMPDIR=" + f.getPath()); |
|
156 |
|
157 // setup the downloads path |
|
158 f = Environment.getDownloadCacheDirectory(); |
|
159 putenv("EXTERNAL_STORAGE=" + f.getPath()); |
|
160 |
|
161 // setup the app-specific cache path |
|
162 f = context.getCacheDir(); |
|
163 putenv("CACHE_DIRECTORY=" + f.getPath()); |
|
164 |
|
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 |
|
167 |
|
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 } |
|
190 |
|
191 setupLocaleEnvironment(); |
|
192 |
|
193 // We don't need this any more. |
|
194 sIntent = null; |
|
195 } |
|
196 |
|
197 private static void loadLibsSetup(Context context) { |
|
198 synchronized (sLibLoadingLock) { |
|
199 if (sLibsSetup) { |
|
200 return; |
|
201 } |
|
202 sLibsSetup = true; |
|
203 } |
|
204 |
|
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. |
|
208 |
|
209 File cacheFile = getCacheDir(context); |
|
210 putenv("GRE_HOME=" + getGREDir(context).getPath()); |
|
211 |
|
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 } |
|
218 |
|
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 } |
|
225 |
|
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 } |
|
237 |
|
238 @RobocopTarget |
|
239 public static void loadSQLiteLibs(Context context, String apkName) { |
|
240 synchronized (sLibLoadingLock) { |
|
241 if (sSQLiteLibsLoaded) { |
|
242 return; |
|
243 } |
|
244 sSQLiteLibsLoaded = true; |
|
245 } |
|
246 |
|
247 loadMozGlue(); |
|
248 // the extract libs parameter is being removed in bug 732069 |
|
249 loadLibsSetup(context); |
|
250 loadSQLiteLibsNative(apkName, false); |
|
251 } |
|
252 |
|
253 public static void loadNSSLibs(Context context, String apkName) { |
|
254 synchronized (sLibLoadingLock) { |
|
255 if (sNSSLibsLoaded) { |
|
256 return; |
|
257 } |
|
258 sNSSLibsLoaded = true; |
|
259 } |
|
260 |
|
261 loadMozGlue(); |
|
262 loadLibsSetup(context); |
|
263 loadNSSLibsNative(apkName, false); |
|
264 } |
|
265 |
|
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 } |
|
284 |
|
285 public static void loadMozGlue() { |
|
286 synchronized (sLibLoadingLock) { |
|
287 if (sMozGlueLoaded) { |
|
288 return; |
|
289 } |
|
290 sMozGlueLoaded = true; |
|
291 } |
|
292 |
|
293 doLoadLibrary("mozglue"); |
|
294 } |
|
295 |
|
296 public static void loadGeckoLibs(Context context, String apkName) { |
|
297 loadLibsSetup(context); |
|
298 loadGeckoLibsNative(apkName); |
|
299 } |
|
300 |
|
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(); |
|
307 |
|
308 putenv("LOCALE_DECIMAL_POINT=" + dfs.getDecimalSeparator()); |
|
309 putenv("LOCALE_THOUSANDS_SEP=" + dfs.getGroupingSeparator()); |
|
310 putenv("LOCALE_GROUPING=" + (char)df.getGroupingSize()); |
|
311 } |
|
312 } |
|
313 |
|
314 // These methods are implemented in mozglue/android/nsGeckoUtils.cpp |
|
315 private static native void putenv(String map); |
|
316 |
|
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 } |