Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Unified Diff: build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java

Issue 1409603002: GN Incremental Install: Add support for running with instrumentation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review 1 Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java
diff --git a/build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java b/build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java
index 81e06a23d3ea5108a1b6ea09966a6f5e2c4045b4..e932c1bde3df5e5950af64171e32a5e126891fda 100644
--- a/build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java
+++ b/build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java
@@ -5,10 +5,13 @@
package org.chromium.incrementalinstall;
import android.app.Application;
+import android.app.Instrumentation;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
import android.util.Log;
import java.io.File;
@@ -18,17 +21,23 @@ import java.util.Map;
/**
* An Application that replaces itself with another Application (as defined in
- * the "incremental-install-real-app" meta-data tag within the
- * AndroidManifest.xml). It loads the other application only after side-loading
- * its .so and .dex files from /data/local/tmp.
+ * an AndroidManifext.xml meta-data tag). It loads the other application only
+ * after side-loading its .so and .dex files from /data/local/tmp.
+ *
+ * This class is highly dependent on the private implementation details of
+ * Android's ActivityThread.java. However, it has been tested to work with
+ * JellyBean through Marshmallow.
*/
public final class BootstrapApplication extends Application {
private static final String TAG = "cr.incrementalinstall";
private static final String MANAGED_DIR_PREFIX = "/data/local/tmp/incremental-app-";
private static final String REAL_APP_META_DATA_NAME = "incremental-install-real-app";
+ private static final String REAL_INSTRUMENTATION_META_DATA_NAME =
+ "incremental-install-real-instrumentation";
private ClassLoaderPatcher mClassLoaderPatcher;
private Application mRealApplication;
+ private Instrumentation mRealInstrumentation;
private Object mStashedProviderList;
private Object mActivityThread;
@@ -65,10 +74,25 @@ public final class BootstrapApplication extends Application {
LockFile.clearInstallerLock(firstRunLockFile);
}
+ Bundle metadata = getManifestMetadata();
+ // mInstrumentationAppDir is one of a set of fields that is initialized only when
+ // instrumentation is active.
+ if (Reflect.getField(mActivityThread, "mInstrumentationAppDir") != null) {
+ initInstrumentation(metadata.getString(REAL_INSTRUMENTATION_META_DATA_NAME));
+ } else {
+ Log.i(TAG, "No instrumentation active.");
+ }
+
+ // Even when instrumentation is not enabled, ActivityThread uses a default
+ // Instrumentation instance internally. We hook it here in order to hook into the
+ // call to Instrumentation.onCreate().
+ Reflect.setField(mActivityThread, "mInstrumentation",
+ new BootstrapInstrumentation(this));
+
// attachBaseContext() is called from ActivityThread#handleBindApplication() and
// Application#mApplication is changed right after we return. Thus, we cannot swap
// the Application instances until onCreate() is called.
- String realApplicationName = getRealApplicationName();
+ String realApplicationName = metadata.getString(REAL_APP_META_DATA_NAME);
Log.i(TAG, "Instantiating " + realApplicationName);
mRealApplication =
(Application) Reflect.newInstance(Class.forName(realApplicationName));
@@ -79,7 +103,49 @@ public final class BootstrapApplication extends Application {
// class being installed, so temporarily pretend there are no providers, and then
// instantiate them explicitly within onCreate().
disableContentProviders();
- Log.i(TAG, "Waiting for onCreate");
+ Log.i(TAG, "Waiting for Instrumentation.onCreate");
+ } catch (Exception e) {
+ throw new RuntimeException("Incremental install failed.", e);
+ }
+ }
+
+ /**
+ * Instantiates and initializes mRealInstrumentation (the real Instrumentation class).
+ */
+ private void initInstrumentation(String realInstrumentationName)
+ throws ReflectiveOperationException {
+ Log.i(TAG, "Instantiating instrumentation " + realInstrumentationName);
+ mRealInstrumentation = (Instrumentation) Reflect.newInstance(
+ Class.forName(realInstrumentationName));
+ Instrumentation oldInstrumentation =
+ (Instrumentation) Reflect.getField(mActivityThread, "mInstrumentation");
+
+ // Initialize the fields that are set by Instrumentation.init().
+ String[] initFields = {"mThread", "mMessageQueue", "mInstrContext", "mAppContext",
+ "mWatcher", "mUiAutomationConnection"};
+ for (String fieldName : initFields) {
+ Reflect.setField(mRealInstrumentation, fieldName,
+ Reflect.getField(oldInstrumentation, fieldName));
+ }
+ // But make sure the correct ComponentName is used.
+ ComponentName newName = new ComponentName(
+ oldInstrumentation.getComponentName().getPackageName(), realInstrumentationName);
+ Reflect.setField(mRealInstrumentation, "mComponent", newName);
+ }
+
+ /**
+ * Called by BootstrapInstrumentation from Instrumentation.onCreate().
+ * This happens regardless of whether or not instrumentation is enabled.
+ */
+ void onInstrumentationCreate(Bundle arguments) {
+ Log.i(TAG, "Instrumentation.onCreate() called. Swapping references.");
+ try {
+ swapApplicationReferences();
+ enableContentProviders();
+ if (mRealInstrumentation != null) {
+ Reflect.setField(mActivityThread, "mInstrumentation", mRealInstrumentation);
+ mRealInstrumentation.onCreate(arguments);
+ }
} catch (Exception e) {
throw new RuntimeException("Incremental install failed.", e);
}
@@ -89,10 +155,7 @@ public final class BootstrapApplication extends Application {
public void onCreate() {
super.onCreate();
try {
- Log.i(TAG, "onCreate() called. Swapping Application references");
- swapApplicationReferences();
- enableContentProviders();
- Log.i(TAG, "Calling onCreate");
+ Log.i(TAG, "Application.onCreate() called.");
mRealApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException("Incremental install failed.", e);
@@ -103,10 +166,10 @@ public final class BootstrapApplication extends Application {
* Returns the class name of the real Application class (recorded in the
* AndroidManifest.xml)
*/
- private String getRealApplicationName() throws NameNotFoundException {
+ private Bundle getManifestMetadata() throws NameNotFoundException {
ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPackageName(),
PackageManager.GET_META_DATA);
- return appInfo.metaData.getString(REAL_APP_META_DATA_NAME);
+ return appInfo.metaData;
}
/**

Powered by Google App Engine
This is Rietveld 408576698