Define a Java-side global application context.
Instead of each user of base setting the native-side global app context
separately, introduce a Java-side global app context, which is always
in sync with the native-side one. Switch most callers to setting
it on the Java side, except where this is problematic.
Callers of ApplicationStatus.getApplicationContext will be updated
incrementally in followup CLs once it's been verified that they only
require a Context and not a BaseChromiumApplication.
BUG=552419
Review URL: https://codereview.chromium.org/1407233017
Cr-Commit-Position: refs/heads/master@{#361306}
CrOS-Libchrome-Original-Commit: 961a488f6a5d8c82b576444054f5d26b3f8a6877
diff --git a/base/android/base_jni_registrar.cc b/base/android/base_jni_registrar.cc
index 752dcff..9ca3ce1 100644
--- a/base/android/base_jni_registrar.cc
+++ b/base/android/base_jni_registrar.cc
@@ -10,6 +10,7 @@
#include "base/android/build_info.h"
#include "base/android/command_line_android.h"
#include "base/android/content_uri_utils.h"
+#include "base/android/context_utils.h"
#include "base/android/cpu_features.h"
#include "base/android/event_log.h"
#include "base/android/field_trial_list.h"
@@ -39,13 +40,13 @@
static RegistrationMethod kBaseRegisteredMethods[] = {
{"AnimationFrameTimeHistogram",
base::android::RegisterAnimationFrameTimeHistogram},
- {"ApkAssets",
- base::android::RegisterApkAssets},
+ {"ApkAssets", base::android::RegisterApkAssets},
{"ApplicationStatusListener",
base::android::ApplicationStatusListener::RegisterBindings},
{"BuildInfo", base::android::BuildInfo::RegisterBindings},
{"CommandLine", base::android::RegisterCommandLine},
{"ContentUriUtils", base::RegisterContentUriUtils},
+ {"ContextUtils", base::android::RegisterContextUtils},
{"CpuFeatures", base::android::RegisterCpuFeatures},
{"EventLog", base::android::RegisterEventLog},
{"FieldTrialList", base::android::RegisterFieldTrialList},
diff --git a/base/android/context_utils.cc b/base/android/context_utils.cc
new file mode 100644
index 0000000..5e10f21
--- /dev/null
+++ b/base/android/context_utils.cc
@@ -0,0 +1,58 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/context_utils.h"
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/lazy_instance.h"
+#include "jni/ContextUtils_jni.h"
+
+using base::android::JavaRef;
+
+namespace base {
+namespace android {
+
+namespace {
+
+// Leak the global app context, as it is used from a non-joinable worker thread
+// that may still be running at shutdown. There is no harm in doing this.
+base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject>>::Leaky
+ g_application_context = LAZY_INSTANCE_INITIALIZER;
+
+void SetNativeApplicationContext(JNIEnv* env, const JavaRef<jobject>& context) {
+ if (env->IsSameObject(g_application_context.Get().obj(), context.obj())) {
+ // It's safe to set the context more than once if it's the same context.
+ return;
+ }
+ DCHECK(g_application_context.Get().is_null());
+ g_application_context.Get().Reset(context);
+}
+
+} // namespace
+
+const jobject GetApplicationContext() {
+ DCHECK(!g_application_context.Get().is_null());
+ return g_application_context.Get().obj();
+}
+
+void InitApplicationContext(JNIEnv* env, const JavaRef<jobject>& context) {
+ SetNativeApplicationContext(env, context);
+ Java_ContextUtils_initJavaSideApplicationContext(env, context.obj());
+}
+
+static void InitNativeSideApplicationContext(
+ JNIEnv* env,
+ const JavaParamRef<jclass>& clazz,
+ const JavaParamRef<jobject>& context) {
+ SetNativeApplicationContext(env, context);
+}
+
+bool RegisterContextUtils(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/base/android/context_utils.h b/base/android/context_utils.h
new file mode 100644
index 0000000..67e6335
--- /dev/null
+++ b/base/android/context_utils.h
@@ -0,0 +1,34 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_CONTEXT_UTILS_H_
+#define BASE_ANDROID_CONTEXT_UTILS_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+// Gets a global ref to the application context set with
+// InitApplicationContext(). Ownership is retained by the function - the caller
+// must NOT release it.
+const BASE_EXPORT jobject GetApplicationContext();
+
+// Initialize the global application context object.
+// Either this or the Java equivalent ContextUtils.initApplicationContext must
+// be called once during startup. JNI bindings must have been initialized, as
+// the context is stored on both sides.
+BASE_EXPORT void InitApplicationContext(
+ JNIEnv* env,
+ const base::android::JavaRef<jobject>& context);
+
+bool RegisterContextUtils(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_CONTEXT_UTILS_H_
diff --git a/base/android/java/src/org/chromium/base/ContextUtils.java b/base/android/java/src/org/chromium/base/ContextUtils.java
new file mode 100644
index 0000000..266e8eb
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/ContextUtils.java
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.content.Context;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * This class provides Android Context utility methods.
+ */
+@JNINamespace("base::android")
+public class ContextUtils {
+ private static Context sApplicationContext;
+
+ /**
+ * Get the Android application context.
+ *
+ * Under normal circumstances there is only one application context in a process, so it's safe
+ * to treat this as a global. In WebView it's possible for more than one app using WebView to be
+ * running in a single process, but this mechanism is rarely used and this is not the only
+ * problem in that scenario, so we don't currently forbid using it as a global.
+ *
+ * Do not downcast the context returned by this method to Application (or any subclass). It may
+ * not be an Application object; it may be wrapped in a ContextWrapper. The only assumption you
+ * may make is that it is a Context whose lifetime is the same as the lifetime of the process.
+ */
+ public static Context getApplicationContext() {
+ assert sApplicationContext != null;
+ return sApplicationContext;
+ }
+
+ /**
+ * Initialize the Android application context.
+ *
+ * Either this or the native equivalent base::android::InitApplicationContext must be called
+ * once during startup. JNI bindings must have been initialized, as the context is stored on
+ * both sides.
+ */
+ public static void initApplicationContext(Context appContext) {
+ initJavaSideApplicationContext(appContext);
+ nativeInitNativeSideApplicationContext(appContext);
+ }
+
+ @CalledByNative
+ private static void initJavaSideApplicationContext(Context appContext) {
+ assert appContext != null;
+ assert sApplicationContext == null || sApplicationContext == appContext;
+ sApplicationContext = appContext;
+ }
+
+ private static native void nativeInitNativeSideApplicationContext(Context appContext);
+}
diff --git a/base/android/jni_android.cc b/base/android/jni_android.cc
index 3db0fc8..d836744 100644
--- a/base/android/jni_android.cc
+++ b/base/android/jni_android.cc
@@ -20,10 +20,6 @@
bool g_disable_manual_jni_registration = false;
JavaVM* g_jvm = NULL;
-// Leak the global app context, as it is used from a non-joinable worker thread
-// that may still be running at shutdown. There is no harm in doing this.
-base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
- g_application_context = LAZY_INSTANCE_INITIALIZER;
base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
g_class_loader = LAZY_INSTANCE_INITIALIZER;
jmethodID g_class_loader_load_class_method_id = 0;
@@ -78,15 +74,6 @@
return g_jvm != NULL;
}
-void InitApplicationContext(JNIEnv* env, const JavaRef<jobject>& context) {
- if (env->IsSameObject(g_application_context.Get().obj(), context.obj())) {
- // It's safe to set the context more than once if it's the same context.
- return;
- }
- DCHECK(g_application_context.Get().is_null());
- g_application_context.Get().Reset(context);
-}
-
void InitReplacementClassLoader(JNIEnv* env,
const JavaRef<jobject>& class_loader) {
DCHECK(g_class_loader.Get().is_null());
@@ -105,11 +92,6 @@
g_class_loader.Get().Reset(class_loader);
}
-const jobject GetApplicationContext() {
- DCHECK(!g_application_context.Get().is_null());
- return g_application_context.Get().obj();
-}
-
ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
jclass clazz;
if (!g_class_loader.Get().is_null()) {
diff --git a/base/android/jni_android.h b/base/android/jni_android.h
index 9df9041..651005e 100644
--- a/base/android/jni_android.h
+++ b/base/android/jni_android.h
@@ -10,6 +10,9 @@
#include <string>
+// TODO(torne): remove this when callers of GetApplicationContext have been
+// fixed to include context_utils.h. http://crbug.com/552419
+#include "base/android/context_utils.h"
#include "base/android/scoped_java_ref.h"
#include "base/atomicops.h"
#include "base/base_export.h"
@@ -47,19 +50,12 @@
// Detaches the current thread from VM if it is attached.
BASE_EXPORT void DetachFromVM();
-// Initializes the global JVM. It is not necessarily called before
-// InitApplicationContext().
+// Initializes the global JVM.
BASE_EXPORT void InitVM(JavaVM* vm);
// Returns true if the global JVM has been initialized.
BASE_EXPORT bool IsVMInitialized();
-// Initializes the global application context object. The |context| can be any
-// valid reference to the application context. Internally holds a global ref to
-// the context. InitVM and InitApplicationContext maybe called in either order.
-BASE_EXPORT void InitApplicationContext(JNIEnv* env,
- const JavaRef<jobject>& context);
-
// Initializes the global ClassLoader used by the GetClass and LazyGetClass
// methods. This is needed because JNI will use the base ClassLoader when there
// is no Java code on the stack. The base ClassLoader doesn't know about any of
@@ -69,11 +65,6 @@
JNIEnv* env,
const JavaRef<jobject>& class_loader);
-// Gets a global ref to the application context set with
-// InitApplicationContext(). Ownership is retained by the function - the caller
-// must NOT release it.
-const BASE_EXPORT jobject GetApplicationContext();
-
// Finds the class named |class_name| and returns it.
// Use this method instead of invoking directly the JNI FindClass method (to
// prevent leaking local references).
diff --git a/base/base.gyp b/base/base.gyp
index 2f8738e..41cc36e 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -1396,6 +1396,7 @@
'android/java/src/org/chromium/base/BuildInfo.java',
'android/java/src/org/chromium/base/CommandLine.java',
'android/java/src/org/chromium/base/ContentUriUtils.java',
+ 'android/java/src/org/chromium/base/ContextUtils.java',
'android/java/src/org/chromium/base/CpuFeatures.java',
'android/java/src/org/chromium/base/EventLog.java',
'android/java/src/org/chromium/base/FieldTrialList.java',
diff --git a/base/base.gypi b/base/base.gypi
index bdaf4c7..4139688 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -34,6 +34,8 @@
'android/command_line_android.h',
'android/content_uri_utils.cc',
'android/content_uri_utils.h',
+ 'android/context_utils.cc',
+ 'android/context_utils.h',
'android/cpu_features.cc',
'android/cxa_demangle_stub.cc',
'android/event_log.cc',
diff --git a/mojo/android/javatests/mojo_test_case.cc b/mojo/android/javatests/mojo_test_case.cc
index 22a08ff..631cf97 100644
--- a/mojo/android/javatests/mojo_test_case.cc
+++ b/mojo/android/javatests/mojo_test_case.cc
@@ -30,10 +30,7 @@
namespace mojo {
namespace android {
-static void InitApplicationContext(JNIEnv* env,
- const JavaParamRef<jobject>& jcaller,
- const JavaParamRef<jobject>& context) {
- base::android::InitApplicationContext(env, context);
+static void Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller) {
base::InitAndroidTestMessageLoop();
}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
index 4e96de2..3e81b7a 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
@@ -4,9 +4,9 @@
package org.chromium.mojo;
-import android.content.Context;
import android.test.InstrumentationTestCase;
+import org.chromium.base.ContextUtils;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.LibraryProcessType;
@@ -27,7 +27,8 @@
super.setUp();
LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER)
.ensureInitialized(getInstrumentation().getTargetContext());
- nativeInitApplicationContext(getInstrumentation().getTargetContext());
+ ContextUtils.initApplicationContext(getInstrumentation().getTargetContext());
+ nativeInit();
mTestEnvironmentPointer = nativeSetupTestEnvironment();
}
@@ -54,7 +55,7 @@
nativeRunLoop(0);
}
- private native void nativeInitApplicationContext(Context context);
+ private native void nativeInit();
private native long nativeSetupTestEnvironment();
diff --git a/mojo/runner/android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java b/mojo/runner/android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java
index 2939aa5..7e148ac 100644
--- a/mojo/runner/android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java
+++ b/mojo/runner/android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java
@@ -47,9 +47,6 @@
// has no obligation to kill the application process between destroying and restarting the
// activity. If the application process is kept alive, initialization parameters sent with
// the intent will be stale.
- // TODO(qsr): We should be passing application context here as required by
- // InitApplicationContext on the native side. Currently we can't, as PlatformViewportAndroid
- // relies on this being the activity context.
ShellMain.ensureInitialized(this, parameters);
ShellMain.start();
}
diff --git a/mojo/runner/android/apk/src/org/chromium/mojo/shell/ShellMain.java b/mojo/runner/android/apk/src/org/chromium/mojo/shell/ShellMain.java
index 942c19c..d0c2b6e 100644
--- a/mojo/runner/android/apk/src/org/chromium/mojo/shell/ShellMain.java
+++ b/mojo/runner/android/apk/src/org/chromium/mojo/shell/ShellMain.java
@@ -11,6 +11,7 @@
import android.os.Bundle;
import android.util.Log;
+import org.chromium.base.ContextUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
@@ -38,24 +39,22 @@
/**
* Initializes the native system.
**/
- public static void ensureInitialized(Context applicationContext, String[] parameters) {
+ public static void ensureInitialized(Context context, String[] parameters) {
if (sInitialized) return;
- File cachedAppsDir = getCachedAppsDir(applicationContext);
+ File cachedAppsDir = getCachedAppsDir(context);
try {
- final File timestamp =
- FileHelper.prepareDirectoryForAssets(applicationContext, cachedAppsDir);
- for (String assetPath : FileHelper.getAssetsList(applicationContext)) {
- FileHelper.extractFromAssets(applicationContext, assetPath, cachedAppsDir,
- FileHelper.FileType.PERMANENT);
+ final File timestamp = FileHelper.prepareDirectoryForAssets(context, cachedAppsDir);
+ for (String assetPath : FileHelper.getAssetsList(context)) {
+ FileHelper.extractFromAssets(
+ context, assetPath, cachedAppsDir, FileHelper.FileType.PERMANENT);
}
- ApplicationInfo ai = applicationContext.getPackageManager().getApplicationInfo(
- applicationContext.getPackageName(), PackageManager.GET_META_DATA);
+ ApplicationInfo ai = context.getPackageManager().getApplicationInfo(
+ context.getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = ai.metaData;
String mojo_lib = bundle.getString(MOJO_LIB_KEY);
FileHelper.createTimestampIfNecessary(timestamp);
- File mojoShell =
- new File(applicationContext.getApplicationInfo().nativeLibraryDir, mojo_lib);
+ File mojoShell = new File(context.getApplicationInfo().nativeLibraryDir, mojo_lib);
List<String> parametersList = new ArrayList<String>();
// Program name.
@@ -63,10 +62,10 @@
parametersList.addAll(Arrays.asList(parameters));
}
- nativeInit(applicationContext, mojoShell.getAbsolutePath(),
+ ContextUtils.initApplicationContext(context.getApplicationContext());
+ nativeInit(context, mojoShell.getAbsolutePath(),
parametersList.toArray(new String[parametersList.size()]),
- cachedAppsDir.getAbsolutePath(),
- getTmpDir(applicationContext).getAbsolutePath());
+ cachedAppsDir.getAbsolutePath(), getTmpDir(context).getAbsolutePath());
sInitialized = true;
} catch (Exception e) {
Log.e(TAG, "ShellMain initialization failed.", e);
diff --git a/mojo/runner/android/main.cc b/mojo/runner/android/main.cc
index 3980c22..5e4710f 100644
--- a/mojo/runner/android/main.cc
+++ b/mojo/runner/android/main.cc
@@ -112,8 +112,6 @@
base::android::ConvertJavaStringToUTF8(env, j_tmp_dir).c_str(), 1);
DCHECK_EQ(return_value, 0);
- base::android::InitApplicationContext(env, activity);
-
std::vector<std::string> parameters;
parameters.push_back(
base::android::ConvertJavaStringToUTF8(env, mojo_shell_path));