| |
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 } |