• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Authors
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.Activity;
8 import android.app.Application;
9 import android.content.BroadcastReceiver;
10 import android.content.Context;
11 import android.content.ContextWrapper;
12 import android.content.Intent;
13 import android.content.IntentFilter;
14 import android.content.SharedPreferences;
15 import android.content.res.AssetManager;
16 import android.os.Build;
17 import android.os.Handler;
18 import android.os.Process;
19 import android.preference.PreferenceManager;
20 
21 import androidx.annotation.Nullable;
22 
23 import org.jni_zero.JNINamespace;
24 
25 import org.chromium.base.compat.ApiHelperForM;
26 import org.chromium.base.compat.ApiHelperForO;
27 import org.chromium.build.BuildConfig;
28 
29 /** This class provides Android application context related utility methods. */
30 @JNINamespace("base::android")
31 public class ContextUtils {
32     private static final String TAG = "ContextUtils";
33     private static Context sApplicationContext;
34 
35     /**
36      * Flag for {@link Context#registerReceiver}: The receiver can receive broadcasts from other
37      * Apps. Has the same behavior as marking a statically registered receiver with "exported=true".
38      *
39      * TODO(mthiesse): Move to ApiHelperForT when we build against T SDK.
40      */
41     public static final int RECEIVER_EXPORTED = 0x2;
42 
43     public static final int RECEIVER_NOT_EXPORTED = 0x4;
44 
45     /** Initialization-on-demand holder. This exists for thread-safe lazy initialization. */
46     private static class Holder {
47         // Not final for tests.
48         private static SharedPreferences sSharedPreferences = fetchAppSharedPreferences();
49     }
50 
51     /**
52      * Get the Android application context.
53      *
54      * Under normal circumstances there is only one application context in a process, so it's safe
55      * to treat this as a global. In WebView it's possible for more than one app using WebView to be
56      * running in a single process, but this mechanism is rarely used and this is not the only
57      * problem in that scenario, so we don't currently forbid using it as a global.
58      *
59      * Do not downcast the context returned by this method to Application (or any subclass). It may
60      * not be an Application object; it may be wrapped in a ContextWrapper. The only assumption you
61      * may make is that it is a Context whose lifetime is the same as the lifetime of the process.
62      */
getApplicationContext()63     public static Context getApplicationContext() {
64         return sApplicationContext;
65     }
66 
67     /**
68      * Initializes the java application context.
69      *
70      * This should be called exactly once early on during startup, before native is loaded and
71      * before any other clients make use of the application context through this class.
72      *
73      * @param appContext The application context.
74      */
initApplicationContext(Context appContext)75     public static void initApplicationContext(Context appContext) {
76         // Conceding that occasionally in tests, native is loaded before the browser process is
77         // started, in which case the browser process re-sets the application context.
78         assert sApplicationContext == null
79                 || sApplicationContext == appContext
80                 || ((ContextWrapper) sApplicationContext).getBaseContext() == appContext;
81         initJavaSideApplicationContext(appContext);
82     }
83 
84     /**
85      * Only called by the static holder class and tests.
86      *
87      * @return The application-wide shared preferences.
88      */
89     @SuppressWarnings("DefaultSharedPreferencesCheck")
fetchAppSharedPreferences()90     private static SharedPreferences fetchAppSharedPreferences() {
91         // This may need to create the prefs directory if we've never used shared prefs before, so
92         // allow disk writes. This is rare but can happen if code used early in startup reads prefs.
93         try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
94             return PreferenceManager.getDefaultSharedPreferences(sApplicationContext);
95         }
96     }
97 
98     /**
99      * This is used to ensure that we always use the application context to fetch the default shared
100      * preferences. This avoids needless I/O for android N and above. It also makes it clear that
101      * the app-wide shared preference is desired, rather than the potentially context-specific one.
102      *
103      * @return application-wide shared preferences.
104      */
getAppSharedPreferences()105     public static SharedPreferences getAppSharedPreferences() {
106         return Holder.sSharedPreferences;
107     }
108 
109     /**
110      * Occasionally tests cannot ensure the application context doesn't change between tests (junit)
111      * and sometimes specific tests has its own special needs, initApplicationContext should be used
112      * as much as possible, but this method can be used to override it.
113      *
114      * @param appContext The new application context.
115      */
initApplicationContextForTests(Context appContext)116     public static void initApplicationContextForTests(Context appContext) {
117         initJavaSideApplicationContext(appContext);
118         Holder.sSharedPreferences = fetchAppSharedPreferences();
119     }
120 
121     /**
122      * Tests that use the applicationContext may unintentionally use the Context
123      * set by a previously run test.
124      */
clearApplicationContextForTests()125     public static void clearApplicationContextForTests() {
126         sApplicationContext = null;
127         Holder.sSharedPreferences = null;
128     }
129 
initJavaSideApplicationContext(Context appContext)130     private static void initJavaSideApplicationContext(Context appContext) {
131         assert appContext != null;
132         // Guard against anyone trying to downcast.
133         if (BuildConfig.ENABLE_ASSERTS && appContext instanceof Application) {
134             appContext = new ContextWrapper(appContext);
135         }
136         sApplicationContext = appContext;
137     }
138 
139     /**
140      * In most cases, {@link Context#getAssets()} can be used directly. Modified resources are
141      * used downstream and are set up on application startup, and this method provides access to
142      * regular assets before that initialization is complete.
143      *
144      * This method should ONLY be used for accessing files within the assets folder.
145      *
146      * @return Application assets.
147      */
getApplicationAssets()148     public static AssetManager getApplicationAssets() {
149         Context context = getApplicationContext();
150         while (context instanceof ContextWrapper) {
151             context = ((ContextWrapper) context).getBaseContext();
152         }
153         return context.getAssets();
154     }
155 
156     /**
157      * @return Whether the process is isolated.
158      */
159     @SuppressWarnings("NewApi")
isIsolatedProcess()160     public static boolean isIsolatedProcess() {
161         // Was not made visible until Android P, but the method has always been there.
162         return Process.isIsolated();
163     }
164 
165     /**
166      * @return if current process is SdkSandbox process.
167      */
isSdkSandboxProcess()168     public static boolean isSdkSandboxProcess() {
169         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
170             return Process.isSdkSandbox();
171         } else {
172             return false;
173         }
174     }
175 
176     /** @return The name of the current process. E.g. "org.chromium.chrome:privileged_process0". */
getProcessName()177     public static String getProcessName() {
178         return ApiCompatibilityUtils.getProcessName();
179     }
180 
181     /** @return Whether the current process is 64-bit. */
isProcess64Bit()182     public static boolean isProcess64Bit() {
183         return ApiHelperForM.isProcess64Bit();
184     }
185 
186     /**
187      * Extract the {@link Activity} if the given {@link Context} either is or wraps one.
188      *
189      * @param context The context to check.
190      * @return Extracted activity if it exists, otherwise null.
191      */
activityFromContext(@ullable Context context)192     public static @Nullable Activity activityFromContext(@Nullable Context context) {
193         // Only retrieves the base context if the supplied context is a ContextWrapper but not an
194         // Activity, because Activity is a subclass of ContextWrapper.
195         while (context instanceof ContextWrapper) {
196             if (context instanceof Activity) return (Activity) context;
197 
198             context = ((ContextWrapper) context).getBaseContext();
199         }
200 
201         return null;
202     }
203 
204     /**
205      * Register a broadcast receiver that may only accept protected broadcasts.
206      *
207      * You should (only) use this method when:
208      * <p><ul>
209      * <li>You need to receive protected broadcasts.
210      * </ul><p>
211      * This method does not presently verify that the provided IntentFilter covers only protected
212      * broadcasts, so you should make sure that the broadcasts you register for are in fact
213      * protected broadcasts. The Android platform's <a
214      * href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/AndroidManifest.xml">
215      * AndroidManifest.xml</a> contains a list of broadcasts which should be common to all devices.
216      * You should be careful about using broadcasts which appear to be protected, but are not listed
217      * in the platform's manifest, as they may not be protected on all devices. Different versions
218      * or builds of Android may have different sets of protected broadcasts, so add appropriate
219      * guards if needed.
220      * <p>
221      * You can unregister receivers using the normal {@link Context#unregisterReceiver} method.
222      */
registerProtectedBroadcastReceiver( Context context, BroadcastReceiver receiver, IntentFilter filter)223     public static Intent registerProtectedBroadcastReceiver(
224             Context context, BroadcastReceiver receiver, IntentFilter filter) {
225         return registerBroadcastReceiver(
226                 context, receiver, filter, /* permission= */ null, /* scheduler= */ null, 0);
227     }
228 
registerProtectedBroadcastReceiver( Context context, BroadcastReceiver receiver, IntentFilter filter, Handler scheduler)229     public static Intent registerProtectedBroadcastReceiver(
230             Context context, BroadcastReceiver receiver, IntentFilter filter, Handler scheduler) {
231         return registerBroadcastReceiver(
232                 context, receiver, filter, /* permission= */ null, scheduler, 0);
233     }
234 
235     /**
236      * Register a broadcast receiver that may accept broadcasts from any UID.
237      *
238      * You should (only) use exported receivers when:
239      * <p><ul>
240      * <li>You need to receive unprotected broadcasts from other applications.
241      * <li>Using unprotected sticky broadcasts - either from this application or another.
242      * </ul><p>
243      * Broadcasts received by exported receivers are untrustworthy and must be treated with caution.
244      * <p>
245      * You can unregister receivers using the normal {@link Context#unregisterReceiver} method.
246      */
registerExportedBroadcastReceiver( Context context, BroadcastReceiver receiver, IntentFilter filter, String permission)247     public static Intent registerExportedBroadcastReceiver(
248             Context context, BroadcastReceiver receiver, IntentFilter filter, String permission) {
249         return registerBroadcastReceiver(
250                 context, receiver, filter, permission, /* scheduler= */ null, RECEIVER_EXPORTED);
251     }
252 
253     /**
254      * Register a broadcast receiver that may only accept broadcasts coming from the root, system,
255      * or this app's own UIDs.
256      *
257      * You should generally prefer using this over the exported counterpart,
258      * {@link #registerExportedBroadcastReceiver(Context, BroadcastReceiver, IntentFilter, String)},
259      * unless you meet a specific requirement specified in that method's documentation.
260      * <p>
261      * Even though most protected broadcasts come from the system UID, and could thus be received by
262      * a non-exported receiver, you should instead use registerProtectedBroadcastReceiver for all
263      * protected broadcasts.
264      * <p>
265      * You should (only) use non-exported receivers when:
266      * <p><ul>
267      * <li>You want to send and receive (non-sticky) broadcasts solely within the same application.
268      * <li>You want to receive the result of a PendingIntent that you have provided to some other
269      * application or service.
270      * </ul><p>
271      * Note that older versions of Android do not enforce non-exported receivers, so you should
272      * still not trust received Intents without some additional authentication mechanism. Note that
273      * you generally cannot use Android permissions for this because embedded WebViews will only
274      * inherit the permissions of the embedding application. Consider using
275      * {@link org.chromium.base.IntentUtils#addTrustedIntentExtras} and
276      * {@link org.chromium.base.IntentUtils#isTrustedIntentFromSelf} to verify the Intent's sender.
277      * <p>
278      * Usually, when working with non-exported receivers, you should also make sure that any related
279      * Intents that you send are not broadcast to other apps. This can be done using
280      * {@link Intent#setPackage} with {@link Context#getPackageName}, and must be done before
281      * calling {@link org.chromium.base.IntentUtils#addTrustedIntentExtras}.
282      * <p>
283      * You can unregister receivers using the normal {@link Context#unregisterReceiver} method.
284      */
registerNonExportedBroadcastReceiver( Context context, BroadcastReceiver receiver, IntentFilter filter)285     public static Intent registerNonExportedBroadcastReceiver(
286             Context context, BroadcastReceiver receiver, IntentFilter filter) {
287         return registerBroadcastReceiver(
288                 context,
289                 receiver,
290                 filter,
291                 /* permission= */ null,
292                 /* scheduler= */ null,
293                 RECEIVER_NOT_EXPORTED);
294     }
295 
registerNonExportedBroadcastReceiver( Context context, BroadcastReceiver receiver, IntentFilter filter, Handler scheduler)296     public static Intent registerNonExportedBroadcastReceiver(
297             Context context, BroadcastReceiver receiver, IntentFilter filter, Handler scheduler) {
298         return registerBroadcastReceiver(
299                 context,
300                 receiver,
301                 filter,
302                 /* permission= */ null,
303                 scheduler,
304                 RECEIVER_NOT_EXPORTED);
305     }
306 
registerBroadcastReceiver( Context context, BroadcastReceiver receiver, IntentFilter filter, String permission, Handler scheduler, int flags)307     private static Intent registerBroadcastReceiver(
308             Context context,
309             BroadcastReceiver receiver,
310             IntentFilter filter,
311             String permission,
312             Handler scheduler,
313             int flags) {
314         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
315             return ApiHelperForO.registerReceiver(
316                     context, receiver, filter, permission, scheduler, flags);
317         } else {
318             return context.registerReceiver(receiver, filter, permission, scheduler);
319         }
320     }
321 }
322