• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base;
6 
7 import android.app.Application;
8 import android.content.Context;
9 import android.content.ContextWrapper;
10 import android.content.SharedPreferences;
11 import android.content.res.AssetManager;
12 import android.os.Process;
13 import android.preference.PreferenceManager;
14 
15 import org.chromium.base.annotations.JNINamespace;
16 import org.chromium.base.annotations.MainDex;
17 
18 /**
19  * This class provides Android application context related utility methods.
20  */
21 @JNINamespace("base::android")
22 public class ContextUtils {
23     private static final String TAG = "ContextUtils";
24     private static Context sApplicationContext;
25     // TODO(agrieve): Remove sProcessName caching when we stop supporting JB.
26     private static String sProcessName;
27 
28     /**
29      * Initialization-on-demand holder. This exists for thread-safe lazy initialization.
30      */
31     private static class Holder {
32         // Not final for tests.
33         private static SharedPreferences sSharedPreferences = fetchAppSharedPreferences();
34     }
35 
36     /**
37      * Get the Android application context.
38      *
39      * Under normal circumstances there is only one application context in a process, so it's safe
40      * to treat this as a global. In WebView it's possible for more than one app using WebView to be
41      * running in a single process, but this mechanism is rarely used and this is not the only
42      * problem in that scenario, so we don't currently forbid using it as a global.
43      *
44      * Do not downcast the context returned by this method to Application (or any subclass). It may
45      * not be an Application object; it may be wrapped in a ContextWrapper. The only assumption you
46      * may make is that it is a Context whose lifetime is the same as the lifetime of the process.
47      */
getApplicationContext()48     public static Context getApplicationContext() {
49         return sApplicationContext;
50     }
51 
52     /**
53      * Initializes the java application context.
54      *
55      * This should be called exactly once early on during startup, before native is loaded and
56      * before any other clients make use of the application context through this class.
57      *
58      * @param appContext The application context.
59      */
60     @MainDex // TODO(agrieve): Could add to whole class if not for ApplicationStatus.initialize().
initApplicationContext(Context appContext)61     public static void initApplicationContext(Context appContext) {
62         // Conceding that occasionally in tests, native is loaded before the browser process is
63         // started, in which case the browser process re-sets the application context.
64         if (sApplicationContext != null && sApplicationContext != appContext) {
65             throw new RuntimeException("Attempting to set multiple global application contexts.");
66         }
67         initJavaSideApplicationContext(appContext);
68     }
69 
70     /**
71      * Only called by the static holder class and tests.
72      *
73      * @return The application-wide shared preferences.
74      */
fetchAppSharedPreferences()75     private static SharedPreferences fetchAppSharedPreferences() {
76         return PreferenceManager.getDefaultSharedPreferences(sApplicationContext);
77     }
78 
79     /**
80      * This is used to ensure that we always use the application context to fetch the default shared
81      * preferences. This avoids needless I/O for android N and above. It also makes it clear that
82      * the app-wide shared preference is desired, rather than the potentially context-specific one.
83      *
84      * @return application-wide shared preferences.
85      */
getAppSharedPreferences()86     public static SharedPreferences getAppSharedPreferences() {
87         return Holder.sSharedPreferences;
88     }
89 
90     /**
91      * Occasionally tests cannot ensure the application context doesn't change between tests (junit)
92      * and sometimes specific tests has its own special needs, initApplicationContext should be used
93      * as much as possible, but this method can be used to override it.
94      *
95      * @param appContext The new application context.
96      */
97     @VisibleForTesting
initApplicationContextForTests(Context appContext)98     public static void initApplicationContextForTests(Context appContext) {
99         // ApplicationStatus.initialize should be called to setup activity tracking for tests
100         // that use Robolectric and set the application context manually. Instead of changing all
101         // tests that do so, the call was put here instead.
102         // TODO(mheikal): Require param to be of type Application
103         // Disabled on libchrome
104         // if (appContext instanceof Application) {
105         //     ApplicationStatus.initialize((Application) appContext);
106         // }
107         initJavaSideApplicationContext(appContext);
108         Holder.sSharedPreferences = fetchAppSharedPreferences();
109     }
110 
initJavaSideApplicationContext(Context appContext)111     private static void initJavaSideApplicationContext(Context appContext) {
112         if (appContext == null) {
113             throw new RuntimeException("Global application context cannot be set to null.");
114         }
115         sApplicationContext = appContext;
116     }
117 
118     /**
119      * In most cases, {@link Context#getAssets()} can be used directly. Modified resources are
120      * used downstream and are set up on application startup, and this method provides access to
121      * regular assets before that initialization is complete.
122      *
123      * This method should ONLY be used for accessing files within the assets folder.
124      *
125      * @return Application assets.
126      */
getApplicationAssets()127     public static AssetManager getApplicationAssets() {
128         Context context = getApplicationContext();
129         while (context instanceof ContextWrapper) {
130             context = ((ContextWrapper) context).getBaseContext();
131         }
132         return context.getAssets();
133     }
134 
135     /**
136      * @return Whether the process is isolated.
137      */
isIsolatedProcess()138     public static boolean isIsolatedProcess() {
139         try {
140             return (Boolean) Process.class.getMethod("isIsolated").invoke(null);
141         } catch (Exception e) { // No multi-catch below API level 19 for reflection exceptions.
142             // If fallback logic is ever needed, refer to:
143             // https://chromium-review.googlesource.com/c/chromium/src/+/905563/1
144             throw new RuntimeException(e);
145         }
146     }
147 
148     /** @return The name of the current process. E.g. "org.chromium.chrome:privileged_process0". */
getProcessName()149     public static String getProcessName() {
150         // Once we drop support JB, this method can be simplified to not cache sProcessName and call
151         // ActivityThread.currentProcessName().
152         if (sProcessName != null) {
153             return sProcessName;
154         }
155         try {
156             // An even more convenient ActivityThread.currentProcessName() exists, but was not added
157             // until JB MR2.
158             Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
159             Object activityThread =
160                     activityThreadClazz.getMethod("currentActivityThread").invoke(null);
161             // Before JB MR2, currentActivityThread() returns null when called on a non-UI thread.
162             // Cache the name to allow other threads to access it.
163             sProcessName =
164                     (String) activityThreadClazz.getMethod("getProcessName").invoke(activityThread);
165             return sProcessName;
166         } catch (Exception e) { // No multi-catch below API level 19 for reflection exceptions.
167             // If fallback logic is ever needed, refer to:
168             // https://chromium-review.googlesource.com/c/chromium/src/+/905563/1
169             throw new RuntimeException(e);
170         }
171     }
172 
isMainProcess()173     public static boolean isMainProcess() {
174         return !getProcessName().contains(":");
175     }
176 }
177