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

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

Issue 1684583003: Add java-side support for _incremental instrumentation tests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review comments Created 4 years, 10 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; 8 import android.app.Instrumentation;
9 import android.content.ComponentName; 9 import android.content.ComponentName;
10 import android.content.Context; 10 import android.content.Context;
(...skipping 19 matching lines...) Expand all
30 */ 30 */
31 public final class BootstrapApplication extends Application { 31 public final class BootstrapApplication extends Application {
32 private static final String TAG = "cr.incrementalinstall"; 32 private static final String TAG = "cr.incrementalinstall";
33 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-";
34 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 = 35 private static final String REAL_INSTRUMENTATION_META_DATA_NAME =
36 "incremental-install-real-instrumentation"; 36 "incremental-install-real-instrumentation";
37 37
38 private ClassLoaderPatcher mClassLoaderPatcher; 38 private ClassLoaderPatcher mClassLoaderPatcher;
39 private Application mRealApplication; 39 private Application mRealApplication;
40 private Instrumentation mOrigInstrumentation;
40 private Instrumentation mRealInstrumentation; 41 private Instrumentation mRealInstrumentation;
41 private Object mStashedProviderList; 42 private Object mStashedProviderList;
42 private Object mActivityThread; 43 private Object mActivityThread;
43 44
44 @Override 45 @Override
45 protected void attachBaseContext(Context context) { 46 protected void attachBaseContext(Context context) {
46 super.attachBaseContext(context); 47 super.attachBaseContext(context);
47 File incrementalRootDir = new File(MANAGED_DIR_PREFIX + context.getPacka geName());
48 File libDir = new File(incrementalRootDir, "lib");
49 File dexDir = new File(incrementalRootDir, "dex");
50 File installLockFile = new File(incrementalRootDir, "install.lock");
51 File firstRunLockFile = new File(incrementalRootDir, "firstrun.lock");
52
53 try { 48 try {
54 mActivityThread = Reflect.invokeMethod(Class.forName("android.app.Ac tivityThread"), 49 mActivityThread = Reflect.invokeMethod(Class.forName("android.app.Ac tivityThread"),
55 "currentActivityThread"); 50 "currentActivityThread");
56 mClassLoaderPatcher = new ClassLoaderPatcher(context); 51 mClassLoaderPatcher = new ClassLoaderPatcher(context);
57 52
58 boolean isFirstRun = LockFile.installerLockExists(firstRunLockFile); 53 mOrigInstrumentation =
54 (Instrumentation) Reflect.getField(mActivityThread, "mInstru mentation");
55 Context instContext = mOrigInstrumentation.getContext();
56 if (instContext == null) {
57 instContext = context;
58 }
59
60 // When running with an instrumentation that lives in a different pa ckage from the
61 // application, we must load the dex files and native libraries from both pacakges.
62 // This logic likely won't work when the instrumentation is incremen tal, but the app is
63 // non-incremental. This configuration isn't used right now though.
64 String appPackageName = getPackageName();
65 String instPackageName = instContext.getPackageName();
66 boolean instPackageNameDiffers = !appPackageName.equals(instPackageN ame);
67 Log.i(TAG, "App PackageName: " + appPackageName);
68 if (instPackageNameDiffers) {
69 Log.i(TAG, "Inst PackageName: " + instPackageName);
70 }
71
72 File appIncrementalRootDir = new File(MANAGED_DIR_PREFIX + appPackag eName);
73 File appLibDir = new File(appIncrementalRootDir, "lib");
74 File appDexDir = new File(appIncrementalRootDir, "dex");
75 File appInstallLockFile = new File(appIncrementalRootDir, "install.l ock");
76 File appFirstRunLockFile = new File(appIncrementalRootDir, "firstrun .lock");
77 File instIncrementalRootDir = new File(MANAGED_DIR_PREFIX + instPack ageName);
78 File instLibDir = new File(instIncrementalRootDir, "lib");
79 File instDexDir = new File(instIncrementalRootDir, "dex");
80 File instInstallLockFile = new File(instIncrementalRootDir, "install .lock");
81 File instFirstRunLockFile = new File(instIncrementalRootDir , "first run.lock");
82
83 boolean isFirstRun = LockFile.installerLockExists(appFirstRunLockFil e)
84 || (instPackageNameDiffers
85 && LockFile.installerLockExists(instFirstRunLockF ile));
59 if (isFirstRun) { 86 if (isFirstRun) {
60 if (mClassLoaderPatcher.mIsPrimaryProcess) { 87 if (mClassLoaderPatcher.mIsPrimaryProcess) {
61 // Wait for incremental_install.py to finish. 88 // Wait for incremental_install.py to finish.
62 LockFile.waitForInstallerLock(installLockFile, 30 * 1000); 89 LockFile.waitForInstallerLock(appInstallLockFile, 30 * 1000) ;
90 LockFile.waitForInstallerLock(instInstallLockFile, 30 * 1000 );
63 } else { 91 } else {
64 // Wait for the browser process to create the optimized dex files 92 // Wait for the browser process to create the optimized dex files
65 // and copy the library files. 93 // and copy the library files.
66 LockFile.waitForInstallerLock(firstRunLockFile, 60 * 1000); 94 LockFile.waitForInstallerLock(appFirstRunLockFile, 60 * 1000 );
95 LockFile.waitForInstallerLock(instFirstRunLockFile, 60 * 100 0);
67 } 96 }
68 } 97 }
69 98
70 mClassLoaderPatcher.importNativeLibs(libDir); 99 mClassLoaderPatcher.importNativeLibs(instLibDir);
71 mClassLoaderPatcher.loadDexFiles(dexDir); 100 mClassLoaderPatcher.loadDexFiles(instDexDir);
101 if (instPackageNameDiffers) {
102 mClassLoaderPatcher.importNativeLibs(appLibDir);
103 mClassLoaderPatcher.loadDexFiles(appDexDir);
104 }
72 105
73 if (isFirstRun && mClassLoaderPatcher.mIsPrimaryProcess) { 106 if (isFirstRun && mClassLoaderPatcher.mIsPrimaryProcess) {
74 LockFile.clearInstallerLock(firstRunLockFile); 107 LockFile.clearInstallerLock(appFirstRunLockFile);
108 if (instPackageNameDiffers) {
109 LockFile.clearInstallerLock(instFirstRunLockFile);
110 }
75 } 111 }
76 112
77 // mInstrumentationAppDir is one of a set of fields that is initiali zed only when 113 // mInstrumentationAppDir is one of a set of fields that is initiali zed only when
78 // instrumentation is active. 114 // instrumentation is active.
79 if (Reflect.getField(mActivityThread, "mInstrumentationAppDir") != n ull) { 115 if (Reflect.getField(mActivityThread, "mInstrumentationAppDir") != n ull) {
80 String realInstrumentationName = 116 String realInstrumentationName =
81 getClassNameFromMetadata(REAL_INSTRUMENTATION_META_DATA_ NAME); 117 getClassNameFromMetadata(REAL_INSTRUMENTATION_META_DATA_ NAME, instContext);
82 initInstrumentation(realInstrumentationName); 118 initInstrumentation(realInstrumentationName);
83 } else { 119 } else {
84 Log.i(TAG, "No instrumentation active."); 120 Log.i(TAG, "No instrumentation active.");
85 } 121 }
86 122
87 // Even when instrumentation is not enabled, ActivityThread uses a d efault 123 // Even when instrumentation is not enabled, ActivityThread uses a d efault
88 // Instrumentation instance internally. We hook it here in order to hook into the 124 // Instrumentation instance internally. We hook it here in order to hook into the
89 // call to Instrumentation.onCreate(). 125 // call to Instrumentation.onCreate().
90 Reflect.setField(mActivityThread, "mInstrumentation", 126 Reflect.setField(mActivityThread, "mInstrumentation",
91 new BootstrapInstrumentation(this)); 127 new BootstrapInstrumentation(this));
92 128
93 // attachBaseContext() is called from ActivityThread#handleBindAppli cation() and 129 // attachBaseContext() is called from ActivityThread#handleBindAppli cation() and
94 // Application#mApplication is changed right after we return. Thus, we cannot swap 130 // Application#mApplication is changed right after we return. Thus, we cannot swap
95 // the Application instances until onCreate() is called. 131 // the Application instances until onCreate() is called.
96 String realApplicationName = getClassNameFromMetadata(REAL_APP_META_ DATA_NAME); 132 String realApplicationName = getClassNameFromMetadata(REAL_APP_META_ DATA_NAME, context);
97 Log.i(TAG, "Instantiating " + realApplicationName); 133 Log.i(TAG, "Instantiating " + realApplicationName);
98 mRealApplication = 134 mRealApplication =
99 (Application) Reflect.newInstance(Class.forName(realApplicat ionName)); 135 (Application) Reflect.newInstance(Class.forName(realApplicat ionName));
100 Reflect.invokeMethod(mRealApplication, "attachBaseContext", context) ; 136 Reflect.invokeMethod(mRealApplication, "attachBaseContext", context) ;
101 137
102 // Between attachBaseContext() and onCreate(), ActivityThread tries to instantiate 138 // Between attachBaseContext() and onCreate(), ActivityThread tries to instantiate
103 // all ContentProviders. The ContentProviders break without the corr ect Application 139 // all ContentProviders. The ContentProviders break without the corr ect Application
104 // class being installed, so temporarily pretend there are no provid ers, and then 140 // class being installed, so temporarily pretend there are no provid ers, and then
105 // instantiate them explicitly within onCreate(). 141 // instantiate them explicitly within onCreate().
106 disableContentProviders(); 142 disableContentProviders();
107 Log.i(TAG, "Waiting for Instrumentation.onCreate"); 143 Log.i(TAG, "Waiting for Instrumentation.onCreate");
108 } catch (Exception e) { 144 } catch (Exception e) {
109 throw new RuntimeException("Incremental install failed.", e); 145 throw new RuntimeException("Incremental install failed.", e);
110 } 146 }
111 } 147 }
112 148
113 /** 149 /**
114 * Returns the fully-qualified class name for the given key, stored in a 150 * Returns the fully-qualified class name for the given key, stored in a
115 * <meta> witin the manifest. 151 * <meta> witin the manifest.
116 */ 152 */
117 private String getClassNameFromMetadata(String key) throws NameNotFoundExcep tion { 153 private static String getClassNameFromMetadata(String key, Context context)
118 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPack ageName(), 154 throws NameNotFoundException {
155 String pkgName = context.getPackageName();
156 ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo (pkgName,
119 PackageManager.GET_META_DATA); 157 PackageManager.GET_META_DATA);
120 String value = appInfo.metaData.getString(key); 158 String value = appInfo.metaData.getString(key);
121 if (value != null && !value.contains(".")) { 159 if (value != null && !value.contains(".")) {
122 value = getPackageName() + "." + value; 160 value = pkgName + "." + value;
123 } 161 }
124 return value; 162 return value;
125 } 163 }
126 164
127 /** 165 /**
128 * Instantiates and initializes mRealInstrumentation (the real Instrumentati on class). 166 * Instantiates and initializes mRealInstrumentation (the real Instrumentati on class).
129 */ 167 */
130 private void initInstrumentation(String realInstrumentationName) 168 private void initInstrumentation(String realInstrumentationName)
131 throws ReflectiveOperationException { 169 throws ReflectiveOperationException {
132 Instrumentation oldInstrumentation =
133 (Instrumentation) Reflect.getField(mActivityThread, "mInstrument ation");
134 if (realInstrumentationName == null) { 170 if (realInstrumentationName == null) {
135 // This is the case when an incremental app is used as a target for an instrumentation 171 // This is the case when an incremental app is used as a target for an instrumentation
136 // test. In this case, ActivityThread can instantiate the proper cla ss just fine since 172 // test. In this case, ActivityThread can instantiate the proper cla ss just fine since
137 // it exists within the test apk (as opposed to the incremental apk- under-test). 173 // it exists within the test apk (as opposed to the incremental apk- under-test).
138 Log.i(TAG, "Running with external instrumentation"); 174 Log.i(TAG, "Running with external instrumentation");
139 mRealInstrumentation = oldInstrumentation; 175 mRealInstrumentation = mOrigInstrumentation;
140 return; 176 return;
141 } 177 }
142 // For unit tests, the instrumentation class is replaced in the manifest by a build step 178 // For unit tests, the instrumentation class is replaced in the manifest by a build step
143 // because ActivityThread tries to instantiate it before we get a chance to load the 179 // because ActivityThread tries to instantiate it before we get a chance to load the
144 // incremental dex files. 180 // incremental dex files.
145 Log.i(TAG, "Instantiating instrumentation " + realInstrumentationName); 181 Log.i(TAG, "Instantiating instrumentation " + realInstrumentationName);
146 mRealInstrumentation = (Instrumentation) Reflect.newInstance( 182 mRealInstrumentation = (Instrumentation) Reflect.newInstance(
147 Class.forName(realInstrumentationName)); 183 Class.forName(realInstrumentationName));
148 184
149 // Initialize the fields that are set by Instrumentation.init(). 185 // Initialize the fields that are set by Instrumentation.init().
150 String[] initFields = {"mThread", "mMessageQueue", "mInstrContext", "mAp pContext", 186 String[] initFields = {"mThread", "mMessageQueue", "mInstrContext", "mAp pContext",
151 "mWatcher", "mUiAutomationConnection"}; 187 "mWatcher", "mUiAutomationConnection"};
152 for (String fieldName : initFields) { 188 for (String fieldName : initFields) {
153 Reflect.setField(mRealInstrumentation, fieldName, 189 Reflect.setField(mRealInstrumentation, fieldName,
154 Reflect.getField(oldInstrumentation, fieldName)); 190 Reflect.getField(mOrigInstrumentation, fieldName));
155 } 191 }
156 // But make sure the correct ComponentName is used. 192 // But make sure the correct ComponentName is used.
157 ComponentName newName = new ComponentName( 193 ComponentName newName = new ComponentName(
158 oldInstrumentation.getComponentName().getPackageName(), realInst rumentationName); 194 mOrigInstrumentation.getComponentName().getPackageName(), realIn strumentationName);
159 Reflect.setField(mRealInstrumentation, "mComponent", newName); 195 Reflect.setField(mRealInstrumentation, "mComponent", newName);
160 } 196 }
161 197
162 /** 198 /**
163 * Called by BootstrapInstrumentation from Instrumentation.onCreate(). 199 * Called by BootstrapInstrumentation from Instrumentation.onCreate().
164 * This happens regardless of whether or not instrumentation is enabled. 200 * This happens regardless of whether or not instrumentation is enabled.
165 */ 201 */
166 void onInstrumentationCreate(Bundle arguments) { 202 void onInstrumentationCreate(Bundle arguments) {
167 Log.i(TAG, "Instrumentation.onCreate() called. Swapping references."); 203 Log.i(TAG, "Instrumentation.onCreate() called. Swapping references.");
168 try { 204 try {
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 for (Map.Entry<String, WeakReference<?>> entry : packageMap.entrySet ()) { 273 for (Map.Entry<String, WeakReference<?>> entry : packageMap.entrySet ()) {
238 Object loadedApk = entry.getValue().get(); 274 Object loadedApk = entry.getValue().get();
239 if (loadedApk != null && Reflect.getField(loadedApk, "mApplicati on") == this) { 275 if (loadedApk != null && Reflect.getField(loadedApk, "mApplicati on") == this) {
240 Reflect.setField(loadedApk, "mApplication", mRealApplication ); 276 Reflect.setField(loadedApk, "mApplication", mRealApplication );
241 Reflect.setField(mRealApplication, "mLoadedApk", loadedApk); 277 Reflect.setField(mRealApplication, "mLoadedApk", loadedApk);
242 } 278 }
243 } 279 }
244 } 280 }
245 } 281 }
246 } 282 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698