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.content.Context; 8 import android.content.SharedPreferences; 9 import android.preference.PreferenceManager; 10 11 import org.chromium.base.annotations.JNINamespace; 12 import org.chromium.base.annotations.MainDex; 13 14 /** 15 * This class provides Android application context related utility methods. 16 */ 17 @JNINamespace("base::android") 18 @MainDex 19 public class ContextUtils { 20 private static final String TAG = "ContextUtils"; 21 private static Context sApplicationContext; 22 23 /** 24 * Initialization-on-demand holder. This exists for thread-safe lazy initialization. 25 */ 26 private static class Holder { 27 // Not final for tests. 28 private static SharedPreferences sSharedPreferences = fetchAppSharedPreferences(); 29 } 30 31 /** 32 * Get the Android application context. 33 * 34 * Under normal circumstances there is only one application context in a process, so it's safe 35 * to treat this as a global. In WebView it's possible for more than one app using WebView to be 36 * running in a single process, but this mechanism is rarely used and this is not the only 37 * problem in that scenario, so we don't currently forbid using it as a global. 38 * 39 * Do not downcast the context returned by this method to Application (or any subclass). It may 40 * not be an Application object; it may be wrapped in a ContextWrapper. The only assumption you 41 * may make is that it is a Context whose lifetime is the same as the lifetime of the process. 42 */ getApplicationContext()43 public static Context getApplicationContext() { 44 return sApplicationContext; 45 } 46 47 /** 48 * Initializes the java application context. 49 * 50 * This should be called exactly once early on during startup, before native is loaded and 51 * before any other clients make use of the application context through this class. 52 * 53 * @param appContext The application context. 54 */ initApplicationContext(Context appContext)55 public static void initApplicationContext(Context appContext) { 56 // Conceding that occasionally in tests, native is loaded before the browser process is 57 // started, in which case the browser process re-sets the application context. 58 if (sApplicationContext != null && sApplicationContext != appContext) { 59 throw new RuntimeException("Attempting to set multiple global application contexts."); 60 } 61 initJavaSideApplicationContext(appContext); 62 } 63 64 /** 65 * Initialize the native Android application context to be the same as the java counter-part. 66 */ initApplicationContextForNative()67 public static void initApplicationContextForNative() { 68 if (sApplicationContext == null) { 69 throw new RuntimeException("Cannot have native global application context be null."); 70 } 71 nativeInitNativeSideApplicationContext(sApplicationContext); 72 } 73 74 /** 75 * Only called by the static holder class and tests. 76 * 77 * @return The application-wide shared preferences. 78 */ fetchAppSharedPreferences()79 private static SharedPreferences fetchAppSharedPreferences() { 80 return PreferenceManager.getDefaultSharedPreferences(sApplicationContext); 81 } 82 83 /** 84 * This is used to ensure that we always use the application context to fetch the default shared 85 * preferences. This avoids needless I/O for android N and above. It also makes it clear that 86 * the app-wide shared preference is desired, rather than the potentially context-specific one. 87 * 88 * @return application-wide shared preferences. 89 */ getAppSharedPreferences()90 public static SharedPreferences getAppSharedPreferences() { 91 return Holder.sSharedPreferences; 92 } 93 94 /** 95 * Occasionally tests cannot ensure the application context doesn't change between tests (junit) 96 * and sometimes specific tests has its own special needs, initApplicationContext should be used 97 * as much as possible, but this method can be used to override it. 98 * 99 * @param appContext The new application context. 100 */ 101 @VisibleForTesting initApplicationContextForTests(Context appContext)102 public static void initApplicationContextForTests(Context appContext) { 103 initJavaSideApplicationContext(appContext); 104 Holder.sSharedPreferences = fetchAppSharedPreferences(); 105 } 106 initJavaSideApplicationContext(Context appContext)107 private static void initJavaSideApplicationContext(Context appContext) { 108 if (appContext == null) { 109 throw new RuntimeException("Global application context cannot be set to null."); 110 } 111 sApplicationContext = appContext; 112 } 113 nativeInitNativeSideApplicationContext(Context appContext)114 private static native void nativeInitNativeSideApplicationContext(Context appContext); 115 } 116