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