Commit 961a488f authored by torne's avatar torne Committed by Commit bot

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}
parent 4bc70b49
......@@ -102,6 +102,8 @@ component("base") {
"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",
......@@ -1613,6 +1615,7 @@ if (is_android) {
"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",
......
......@@ -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 @@ namespace android {
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},
......
// 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
// 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_
// 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);
}
......@@ -20,10 +20,6 @@ using base::android::ScopedJavaLocalRef;
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 @@ bool IsVMInitialized() {
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 @@ void InitReplacementClassLoader(JNIEnv* env,
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()) {
......
......@@ -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 @@ BASE_EXPORT JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name);
// 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 @@ BASE_EXPORT void InitReplacementClassLoader(
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).
......
......@@ -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',
......
......@@ -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',
......
......@@ -60,10 +60,7 @@ bool RegisterJni(JNIEnv* env) {
namespace blimp {
static jboolean InitializeBlimp(JNIEnv* env,
const JavaParamRef<jclass>& clazz,
const JavaParamRef<jobject>& jcontext) {
base::android::InitApplicationContext(env, jcontext);
const JavaParamRef<jclass>& clazz) {
// TODO(dtrainor): Start the runner?
return true;
}
......
......@@ -7,6 +7,7 @@ package org.chromium.blimp;
import android.content.Context;
import android.os.Handler;
import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList;
import org.chromium.base.ResourceExtractor;
import org.chromium.base.ThreadUtils;
......@@ -90,7 +91,8 @@ public final class BlimpLibraryLoader {
extractor.addCompletionCallback(new Runnable() {
@Override
public void run() {
final boolean initResult = nativeInitializeBlimp(context.getApplicationContext());
ContextUtils.initApplicationContext(context.getApplicationContext());
final boolean initResult = nativeInitializeBlimp();
new Handler().post(new Runnable() {
@Override
public void run() {
......@@ -126,6 +128,6 @@ public final class BlimpLibraryLoader {
}
// Native methods.
private static native boolean nativeInitializeBlimp(Context context);
private static native boolean nativeInitializeBlimp();
private static native boolean nativeStartBlimp();
}
......@@ -86,13 +86,6 @@ void CronetOnUnLoad(JavaVM* jvm, void* reserved) {
base::android::LibraryLoaderExitHook();
}
void CronetInitApplicationContext(JNIEnv* env,
const JavaParamRef<jclass>& jcaller,
const JavaParamRef<jobject>& japp_context) {
// Set application context.
base::android::InitApplicationContext(env, japp_context);
}
void CronetInitOnMainThread(JNIEnv* env, const JavaParamRef<jclass>& jcaller) {
#if !defined(USE_ICU_ALTERNATIVES_ON_ANDROID)
base::i18n::InitializeICU();
......
......@@ -8,6 +8,7 @@ import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import org.chromium.base.ContextUtils;
import org.chromium.base.annotations.JNINamespace;
/**
......@@ -39,7 +40,7 @@ class CronetLibraryLoader {
Version.CRONET_VERSION,
nativeGetCronetVersion()));
}
nativeCronetInitApplicationContext(context.getApplicationContext());
ContextUtils.initApplicationContext(context.getApplicationContext());
// Init native Chromium CronetEngine on Main UI thread.
Runnable task = new Runnable() {
public void run() {
......@@ -75,6 +76,5 @@ class CronetLibraryLoader {
// Native methods are implemented in cronet_loader.cc.
private static native void nativeCronetInitOnMainThread();
private static native void nativeCronetInitApplicationContext(Context appContext);
private static native String nativeGetCronetVersion();
}
......@@ -28,12 +28,6 @@ LazyInstance<scoped_ptr<ContentMainDelegate> > g_content_main_delegate =
} // namespace
static void InitApplicationContext(JNIEnv* env,
const JavaParamRef<jclass>& clazz,
const JavaParamRef<jobject>& context) {
base::android::InitApplicationContext(env, context);
}
static jint Start(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
TRACE_EVENT0("startup", "content::Start");
......
......@@ -6,6 +6,7 @@ package org.chromium.content.app;
import android.content.Context;
import org.chromium.base.ContextUtils;
import org.chromium.base.annotations.JNINamespace;
/**
......@@ -21,10 +22,10 @@ import org.chromium.base.annotations.JNINamespace;
@JNINamespace("content")
public class ContentMain {
/**
* Initialize application context in native side.
* Initialize global application context.
**/
public static void initApplicationContext(Context context) {
nativeInitApplicationContext(context);
ContextUtils.initApplicationContext(context);
}
/**
......@@ -34,6 +35,5 @@ public class ContentMain {
return nativeStart();
}
private static native void nativeInitApplicationContext(Context context);
private static native int nativeStart();
}
......@@ -30,10 +30,7 @@ struct TestEnvironment {
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();
}
......
......@@ -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 @@ public class MojoTestCase extends InstrumentationTestCase {
super.setUp();
LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER)
.ensureInitialized(getInstrumentation().getTargetContext());
nativeInitApplicationContext(getInstrumentation().getTargetContext());
ContextUtils.initApplicationContext(getInstrumentation().getTargetContext());
nativeInit();
mTestEnvironmentPointer = nativeSetupTestEnvironment();
}
......@@ -54,7 +55,7 @@ public class MojoTestCase extends InstrumentationTestCase {
nativeRunLoop(0);
}
private native void nativeInitApplicationContext(Context context);
private native void nativeInit();
private native long nativeSetupTestEnvironment();
......
......@@ -47,9 +47,6 @@ public class MojoShellActivity extends Activity {
// 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();
}
......
......@@ -11,6 +11,7 @@ import android.content.pm.PackageManager;
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 @@ public class ShellMain {
/**
* 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 @@ public class ShellMain {
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);
......
......@@ -112,8 +112,6 @@ static void Init(JNIEnv* env,
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));
......
......@@ -9,6 +9,7 @@ import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Looper;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
......@@ -141,21 +142,21 @@ public class JniInterface {
private static CapabilityManager sCapabilityManager = CapabilityManager.getInstance();
/**
* To be called once from the main Activity. Any subsequent calls will update the application
* context, but not reload the library. This is useful e.g. when the activity is closed and the
* user later wants to return to the application. Called on the UI thread.
* To be called once from the main Activity. Loads and initializes the native code.
* Called on the UI thread.
*/
public static void loadLibrary(Context context) {
if (sLoaded) return;
System.loadLibrary("remoting_client_jni");
nativeLoadNative(context);
ContextUtils.initApplicationContext(context.getApplicationContext());
nativeLoadNative();
sLoaded = true;
}
/** Performs the native portion of the initialization. */
private static native void nativeLoadNative(Context context);
private static native void nativeLoadNative();
/*
* API/OAuth2 keys access.
......
......@@ -38,11 +38,7 @@ bool RegisterChromotingJniRuntime(JNIEnv* env) {
// Implementation of stubs defined in JniInterface_jni.h. These are the entry
// points for JNI calls from Java into C++.
static void LoadNative(JNIEnv* env,
const JavaParamRef<jclass>& clazz,
const JavaParamRef<jobject>& context) {
base::android::InitApplicationContext(env, context);
static void LoadNative(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
// The google_apis functions check the command-line arguments to make sure no
// runtime API keys have been specified by the environment. Unfortunately, we
// neither launch Chromium nor have a command line, so we need to prevent
......
......@@ -76,8 +76,8 @@ static void RunTests(JNIEnv* env,
base::CommandLine::Init(arraysize(kInitialArgv), kInitialArgv);
// Set the application context in base.
base::android::InitApplicationContext(env, app_context);
base::android::RegisterJni(env);
base::android::InitApplicationContext(env, app_context);
std::vector<std::string> args;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment