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