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

Side by Side 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.incrementalinstall; 5 package org.chromium.incrementalinstall;
6 6
7 import android.app.Application; 7 import android.app.Application;
8 import android.app.Instrumentation;
9 import android.content.ComponentName;
8 import android.content.Context; 10 import android.content.Context;
9 import android.content.pm.ApplicationInfo; 11 import android.content.pm.ApplicationInfo;
10 import android.content.pm.PackageManager; 12 import android.content.pm.PackageManager;
11 import android.content.pm.PackageManager.NameNotFoundException; 13 import android.content.pm.PackageManager.NameNotFoundException;
14 import android.os.Bundle;
12 import android.util.Log; 15 import android.util.Log;
13 16
14 import java.io.File; 17 import java.io.File;
15 import java.lang.ref.WeakReference; 18 import java.lang.ref.WeakReference;
16 import java.util.List; 19 import java.util.List;
17 import java.util.Map; 20 import java.util.Map;
18 21
19 /** 22 /**
20 * An Application that replaces itself with another Application (as defined in 23 * An Application that replaces itself with another Application (as defined in
21 * the "incremental-install-real-app" meta-data tag within the 24 * an AndroidManifext.xml meta-data tag). It loads the other application only
22 * AndroidManifest.xml). It loads the other application only after side-loading 25 * after side-loading its .so and .dex files from /data/local/tmp.
23 * its .so and .dex files from /data/local/tmp. 26 *
27 * This class is highly dependent on the private implementation details of
28 * Android's ActivityThread.java. However, it has been tested to work with
29 * JellyBean through Marshmallow.
24 */ 30 */
25 public final class BootstrapApplication extends Application { 31 public final class BootstrapApplication extends Application {
26 private static final String TAG = "cr.incrementalinstall"; 32 private static final String TAG = "cr.incrementalinstall";
27 private static final String MANAGED_DIR_PREFIX = "/data/local/tmp/incrementa l-app-"; 33 private static final String MANAGED_DIR_PREFIX = "/data/local/tmp/incrementa l-app-";
28 private static final String REAL_APP_META_DATA_NAME = "incremental-install-r eal-app"; 34 private static final String REAL_APP_META_DATA_NAME = "incremental-install-r eal-app";
35 private static final String REAL_INSTRUMENTATION_META_DATA_NAME =
36 "incremental-install-real-instrumentation";
29 37
30 private ClassLoaderPatcher mClassLoaderPatcher; 38 private ClassLoaderPatcher mClassLoaderPatcher;
31 private Application mRealApplication; 39 private Application mRealApplication;
40 private Instrumentation mRealInstrumentation;
32 private Object mStashedProviderList; 41 private Object mStashedProviderList;
33 private Object mActivityThread; 42 private Object mActivityThread;
34 43
35 @Override 44 @Override
36 protected void attachBaseContext(Context context) { 45 protected void attachBaseContext(Context context) {
37 super.attachBaseContext(context); 46 super.attachBaseContext(context);
38 File incrementalRootDir = new File(MANAGED_DIR_PREFIX + context.getPacka geName()); 47 File incrementalRootDir = new File(MANAGED_DIR_PREFIX + context.getPacka geName());
39 File libDir = new File(incrementalRootDir, "lib"); 48 File libDir = new File(incrementalRootDir, "lib");
40 File dexDir = new File(incrementalRootDir, "dex"); 49 File dexDir = new File(incrementalRootDir, "dex");
41 File installLockFile = new File(incrementalRootDir, "install.lock"); 50 File installLockFile = new File(incrementalRootDir, "install.lock");
(...skipping 16 matching lines...) Expand all
58 } 67 }
59 } 68 }
60 69
61 mClassLoaderPatcher.importNativeLibs(libDir); 70 mClassLoaderPatcher.importNativeLibs(libDir);
62 mClassLoaderPatcher.loadDexFiles(dexDir); 71 mClassLoaderPatcher.loadDexFiles(dexDir);
63 72
64 if (isFirstRun && mClassLoaderPatcher.mIsPrimaryProcess) { 73 if (isFirstRun && mClassLoaderPatcher.mIsPrimaryProcess) {
65 LockFile.clearInstallerLock(firstRunLockFile); 74 LockFile.clearInstallerLock(firstRunLockFile);
66 } 75 }
67 76
77 Bundle metadata = getManifestMetadata();
78 // mInstrumentationAppDir is one of a set of fields that is initiali zed only when
79 // instrumentation is active.
80 if (Reflect.getField(mActivityThread, "mInstrumentationAppDir") != n ull) {
81 initInstrumentation(metadata.getString(REAL_INSTRUMENTATION_META _DATA_NAME));
82 } else {
83 Log.i(TAG, "No instrumentation active.");
84 }
85
86 // Even when instrumentation is not enabled, ActivityThread uses a d efault
87 // Instrumentation instance internally. We hook it here in order to hook into the
88 // call to Instrumentation.onCreate().
89 Reflect.setField(mActivityThread, "mInstrumentation",
90 new BootstrapInstrumentation(this));
91
68 // attachBaseContext() is called from ActivityThread#handleBindAppli cation() and 92 // attachBaseContext() is called from ActivityThread#handleBindAppli cation() and
69 // Application#mApplication is changed right after we return. Thus, we cannot swap 93 // Application#mApplication is changed right after we return. Thus, we cannot swap
70 // the Application instances until onCreate() is called. 94 // the Application instances until onCreate() is called.
71 String realApplicationName = getRealApplicationName(); 95 String realApplicationName = metadata.getString(REAL_APP_META_DATA_N AME);
72 Log.i(TAG, "Instantiating " + realApplicationName); 96 Log.i(TAG, "Instantiating " + realApplicationName);
73 mRealApplication = 97 mRealApplication =
74 (Application) Reflect.newInstance(Class.forName(realApplicat ionName)); 98 (Application) Reflect.newInstance(Class.forName(realApplicat ionName));
75 Reflect.invokeMethod(mRealApplication, "attachBaseContext", context) ; 99 Reflect.invokeMethod(mRealApplication, "attachBaseContext", context) ;
76 100
77 // Between attachBaseContext() and onCreate(), ActivityThread tries to instantiate 101 // Between attachBaseContext() and onCreate(), ActivityThread tries to instantiate
78 // all ContentProviders. The ContentProviders break without the corr ect Application 102 // all ContentProviders. The ContentProviders break without the corr ect Application
79 // class being installed, so temporarily pretend there are no provid ers, and then 103 // class being installed, so temporarily pretend there are no provid ers, and then
80 // instantiate them explicitly within onCreate(). 104 // instantiate them explicitly within onCreate().
81 disableContentProviders(); 105 disableContentProviders();
82 Log.i(TAG, "Waiting for onCreate"); 106 Log.i(TAG, "Waiting for Instrumentation.onCreate");
83 } catch (Exception e) { 107 } catch (Exception e) {
84 throw new RuntimeException("Incremental install failed.", e); 108 throw new RuntimeException("Incremental install failed.", e);
85 } 109 }
110 }
111
112 /**
113 * Instantiates and initializes mRealInstrumentation (the real Instrumentati on class).
114 */
115 private void initInstrumentation(String realInstrumentationName)
116 throws ReflectiveOperationException {
117 Log.i(TAG, "Instantiating instrumentation " + realInstrumentationName);
118 mRealInstrumentation = (Instrumentation) Reflect.newInstance(
119 Class.forName(realInstrumentationName));
120 Instrumentation oldInstrumentation =
121 (Instrumentation) Reflect.getField(mActivityThread, "mInstrument ation");
122
123 // Initialize the fields that are set by Instrumentation.init().
124 String[] initFields = {"mThread", "mMessageQueue", "mInstrContext", "mAp pContext",
125 "mWatcher", "mUiAutomationConnection"};
126 for (String fieldName : initFields) {
127 Reflect.setField(mRealInstrumentation, fieldName,
128 Reflect.getField(oldInstrumentation, fieldName));
129 }
130 // But make sure the correct ComponentName is used.
131 ComponentName newName = new ComponentName(
132 oldInstrumentation.getComponentName().getPackageName(), realInst rumentationName);
133 Reflect.setField(mRealInstrumentation, "mComponent", newName);
134 }
135
136 /**
137 * Called by BootstrapInstrumentation from Instrumentation.onCreate().
138 * This happens regardless of whether or not instrumentation is enabled.
139 */
140 void onInstrumentationCreate(Bundle arguments) {
141 Log.i(TAG, "Instrumentation.onCreate() called. Swapping references.");
142 try {
143 swapApplicationReferences();
144 enableContentProviders();
145 if (mRealInstrumentation != null) {
146 Reflect.setField(mActivityThread, "mInstrumentation", mRealInstr umentation);
147 mRealInstrumentation.onCreate(arguments);
148 }
149 } catch (Exception e) {
150 throw new RuntimeException("Incremental install failed.", e);
151 }
86 } 152 }
87 153
88 @Override 154 @Override
89 public void onCreate() { 155 public void onCreate() {
90 super.onCreate(); 156 super.onCreate();
91 try { 157 try {
92 Log.i(TAG, "onCreate() called. Swapping Application references"); 158 Log.i(TAG, "Application.onCreate() called.");
93 swapApplicationReferences();
94 enableContentProviders();
95 Log.i(TAG, "Calling onCreate");
96 mRealApplication.onCreate(); 159 mRealApplication.onCreate();
97 } catch (Exception e) { 160 } catch (Exception e) {
98 throw new RuntimeException("Incremental install failed.", e); 161 throw new RuntimeException("Incremental install failed.", e);
99 } 162 }
100 } 163 }
101 164
102 /** 165 /**
103 * Returns the class name of the real Application class (recorded in the 166 * Returns the class name of the real Application class (recorded in the
104 * AndroidManifest.xml) 167 * AndroidManifest.xml)
105 */ 168 */
106 private String getRealApplicationName() throws NameNotFoundException { 169 private Bundle getManifestMetadata() throws NameNotFoundException {
107 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPack ageName(), 170 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPack ageName(),
108 PackageManager.GET_META_DATA); 171 PackageManager.GET_META_DATA);
109 return appInfo.metaData.getString(REAL_APP_META_DATA_NAME); 172 return appInfo.metaData;
110 } 173 }
111 174
112 /** 175 /**
113 * Nulls out ActivityThread.mBoundApplication.providers. 176 * Nulls out ActivityThread.mBoundApplication.providers.
114 */ 177 */
115 private void disableContentProviders() throws ReflectiveOperationException { 178 private void disableContentProviders() throws ReflectiveOperationException {
116 Object data = Reflect.getField(mActivityThread, "mBoundApplication"); 179 Object data = Reflect.getField(mActivityThread, "mBoundApplication");
117 mStashedProviderList = Reflect.getField(data, "providers"); 180 mStashedProviderList = Reflect.getField(data, "providers");
118 Reflect.setField(data, "providers", null); 181 Reflect.setField(data, "providers", null);
119 } 182 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 for (Map.Entry<String, WeakReference<?>> entry : packageMap.entrySet ()) { 221 for (Map.Entry<String, WeakReference<?>> entry : packageMap.entrySet ()) {
159 Object loadedApk = entry.getValue().get(); 222 Object loadedApk = entry.getValue().get();
160 if (loadedApk != null && Reflect.getField(loadedApk, "mApplicati on") == this) { 223 if (loadedApk != null && Reflect.getField(loadedApk, "mApplicati on") == this) {
161 Reflect.setField(loadedApk, "mApplication", mRealApplication ); 224 Reflect.setField(loadedApk, "mApplication", mRealApplication );
162 Reflect.setField(mRealApplication, "mLoadedApk", loadedApk); 225 Reflect.setField(mRealApplication, "mLoadedApk", loadedApk);
163 } 226 }
164 } 227 }
165 } 228 }
166 } 229 }
167 } 230 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698