summaryrefslogtreecommitdiffstats
path: root/build
diff options
context:
space:
mode:
authoragrieve <agrieve@chromium.org>2016-02-10 11:07:55 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-10 19:10:58 +0000
commit3bbba07e116e2c5a10303387de6bce832b191023 (patch)
tree93b662d3e04c9fa0dbfa3e231d83ee89de753886 /build
parenta0bbcbad97cffb9121dc42769c18150a0f032e0e (diff)
downloadchromium_src-3bbba07e116e2c5a10303387de6bce832b191023.zip
chromium_src-3bbba07e116e2c5a10303387de6bce832b191023.tar.gz
chromium_src-3bbba07e116e2c5a10303387de6bce832b191023.tar.bz2
Add java-side support for _incremental instrumentation tests
Build rule and test runner changes to follow. BUG=575975 Review URL: https://codereview.chromium.org/1684583003 Cr-Commit-Position: refs/heads/master@{#374695}
Diffstat (limited to 'build')
-rwxr-xr-xbuild/android/incremental_install/installer.py12
-rw-r--r--build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java80
-rw-r--r--build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java38
3 files changed, 93 insertions, 37 deletions
diff --git a/build/android/incremental_install/installer.py b/build/android/incremental_install/installer.py
index feebadb..b5b1d05 100755
--- a/build/android/incremental_install/installer.py
+++ b/build/android/incremental_install/installer.py
@@ -13,6 +13,7 @@ import os
import posixpath
import shutil
import sys
+import zipfile
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)))
@@ -63,6 +64,12 @@ def _GetDeviceIncrementalDir(package):
return '/data/local/tmp/incremental-app-%s' % package
+def _HasClasses(jar_path):
+ """Returns whether the given jar contains classes.dex."""
+ with zipfile.ZipFile(jar_path) as jar:
+ return 'classes.dex' in jar.namelist()
+
+
def Uninstall(device, package, enable_device_cache=False):
"""Uninstalls and removes all incremental files for the given package."""
main_timer = time_profile.TimeProfile()
@@ -138,7 +145,10 @@ def Install(device, apk, split_globs=None, native_libs=None, dex_files=None,
# Ensure no two files have the same name.
transformed_names = _TransformDexPaths(dex_files)
for src_path, dest_name in zip(dex_files, transformed_names):
- shutil.copy(src_path, os.path.join(temp_dir, dest_name))
+ # Binary targets with no extra classes create .dex.jar without a
+ # classes.dex (which Android chokes on).
+ if _HasClasses(src_path):
+ shutil.copy(src_path, os.path.join(temp_dir, dest_name))
device.PushChangedFiles([(temp_dir, device_dex_dir)],
delete_device_stale=True)
push_dex_timer.Stop(log=False)
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 7c9bcca..1fb5e40 100644
--- a/build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java
+++ b/build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java
@@ -37,6 +37,7 @@ public final class BootstrapApplication extends Application {
private ClassLoaderPatcher mClassLoaderPatcher;
private Application mRealApplication;
+ private Instrumentation mOrigInstrumentation;
private Instrumentation mRealInstrumentation;
private Object mStashedProviderList;
private Object mActivityThread;
@@ -44,41 +45,76 @@ public final class BootstrapApplication extends Application {
@Override
protected void attachBaseContext(Context context) {
super.attachBaseContext(context);
- File incrementalRootDir = new File(MANAGED_DIR_PREFIX + context.getPackageName());
- File libDir = new File(incrementalRootDir, "lib");
- File dexDir = new File(incrementalRootDir, "dex");
- File installLockFile = new File(incrementalRootDir, "install.lock");
- File firstRunLockFile = new File(incrementalRootDir, "firstrun.lock");
-
try {
mActivityThread = Reflect.invokeMethod(Class.forName("android.app.ActivityThread"),
"currentActivityThread");
mClassLoaderPatcher = new ClassLoaderPatcher(context);
- boolean isFirstRun = LockFile.installerLockExists(firstRunLockFile);
+ mOrigInstrumentation =
+ (Instrumentation) Reflect.getField(mActivityThread, "mInstrumentation");
+ Context instContext = mOrigInstrumentation.getContext();
+ if (instContext == null) {
+ instContext = context;
+ }
+
+ // When running with an instrumentation that lives in a different package from the
+ // application, we must load the dex files and native libraries from both pacakges.
+ // This logic likely won't work when the instrumentation is incremental, but the app is
+ // non-incremental. This configuration isn't used right now though.
+ String appPackageName = getPackageName();
+ String instPackageName = instContext.getPackageName();
+ boolean instPackageNameDiffers = !appPackageName.equals(instPackageName);
+ Log.i(TAG, "App PackageName: " + appPackageName);
+ if (instPackageNameDiffers) {
+ Log.i(TAG, "Inst PackageName: " + instPackageName);
+ }
+
+ File appIncrementalRootDir = new File(MANAGED_DIR_PREFIX + appPackageName);
+ File appLibDir = new File(appIncrementalRootDir, "lib");
+ File appDexDir = new File(appIncrementalRootDir, "dex");
+ File appInstallLockFile = new File(appIncrementalRootDir, "install.lock");
+ File appFirstRunLockFile = new File(appIncrementalRootDir, "firstrun.lock");
+ File instIncrementalRootDir = new File(MANAGED_DIR_PREFIX + instPackageName);
+ File instLibDir = new File(instIncrementalRootDir, "lib");
+ File instDexDir = new File(instIncrementalRootDir, "dex");
+ File instInstallLockFile = new File(instIncrementalRootDir, "install.lock");
+ File instFirstRunLockFile = new File(instIncrementalRootDir , "firstrun.lock");
+
+ boolean isFirstRun = LockFile.installerLockExists(appFirstRunLockFile)
+ || (instPackageNameDiffers
+ && LockFile.installerLockExists(instFirstRunLockFile));
if (isFirstRun) {
if (mClassLoaderPatcher.mIsPrimaryProcess) {
// Wait for incremental_install.py to finish.
- LockFile.waitForInstallerLock(installLockFile, 30 * 1000);
+ LockFile.waitForInstallerLock(appInstallLockFile, 30 * 1000);
+ LockFile.waitForInstallerLock(instInstallLockFile, 30 * 1000);
} else {
// Wait for the browser process to create the optimized dex files
// and copy the library files.
- LockFile.waitForInstallerLock(firstRunLockFile, 60 * 1000);
+ LockFile.waitForInstallerLock(appFirstRunLockFile, 60 * 1000);
+ LockFile.waitForInstallerLock(instFirstRunLockFile, 60 * 1000);
}
}
- mClassLoaderPatcher.importNativeLibs(libDir);
- mClassLoaderPatcher.loadDexFiles(dexDir);
+ mClassLoaderPatcher.importNativeLibs(instLibDir);
+ mClassLoaderPatcher.loadDexFiles(instDexDir);
+ if (instPackageNameDiffers) {
+ mClassLoaderPatcher.importNativeLibs(appLibDir);
+ mClassLoaderPatcher.loadDexFiles(appDexDir);
+ }
if (isFirstRun && mClassLoaderPatcher.mIsPrimaryProcess) {
- LockFile.clearInstallerLock(firstRunLockFile);
+ LockFile.clearInstallerLock(appFirstRunLockFile);
+ if (instPackageNameDiffers) {
+ LockFile.clearInstallerLock(instFirstRunLockFile);
+ }
}
// mInstrumentationAppDir is one of a set of fields that is initialized only when
// instrumentation is active.
if (Reflect.getField(mActivityThread, "mInstrumentationAppDir") != null) {
String realInstrumentationName =
- getClassNameFromMetadata(REAL_INSTRUMENTATION_META_DATA_NAME);
+ getClassNameFromMetadata(REAL_INSTRUMENTATION_META_DATA_NAME, instContext);
initInstrumentation(realInstrumentationName);
} else {
Log.i(TAG, "No instrumentation active.");
@@ -93,7 +129,7 @@ public final class BootstrapApplication extends Application {
// 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 = getClassNameFromMetadata(REAL_APP_META_DATA_NAME);
+ String realApplicationName = getClassNameFromMetadata(REAL_APP_META_DATA_NAME, context);
Log.i(TAG, "Instantiating " + realApplicationName);
mRealApplication =
(Application) Reflect.newInstance(Class.forName(realApplicationName));
@@ -114,12 +150,14 @@ public final class BootstrapApplication extends Application {
* Returns the fully-qualified class name for the given key, stored in a
* &lt;meta&gt; witin the manifest.
*/
- private String getClassNameFromMetadata(String key) throws NameNotFoundException {
- ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPackageName(),
+ private static String getClassNameFromMetadata(String key, Context context)
+ throws NameNotFoundException {
+ String pkgName = context.getPackageName();
+ ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(pkgName,
PackageManager.GET_META_DATA);
String value = appInfo.metaData.getString(key);
if (value != null && !value.contains(".")) {
- value = getPackageName() + "." + value;
+ value = pkgName + "." + value;
}
return value;
}
@@ -129,14 +167,12 @@ public final class BootstrapApplication extends Application {
*/
private void initInstrumentation(String realInstrumentationName)
throws ReflectiveOperationException {
- Instrumentation oldInstrumentation =
- (Instrumentation) Reflect.getField(mActivityThread, "mInstrumentation");
if (realInstrumentationName == null) {
// This is the case when an incremental app is used as a target for an instrumentation
// test. In this case, ActivityThread can instantiate the proper class just fine since
// it exists within the test apk (as opposed to the incremental apk-under-test).
Log.i(TAG, "Running with external instrumentation");
- mRealInstrumentation = oldInstrumentation;
+ mRealInstrumentation = mOrigInstrumentation;
return;
}
// For unit tests, the instrumentation class is replaced in the manifest by a build step
@@ -151,11 +187,11 @@ public final class BootstrapApplication extends Application {
"mWatcher", "mUiAutomationConnection"};
for (String fieldName : initFields) {
Reflect.setField(mRealInstrumentation, fieldName,
- Reflect.getField(oldInstrumentation, fieldName));
+ Reflect.getField(mOrigInstrumentation, fieldName));
}
// But make sure the correct ComponentName is used.
ComponentName newName = new ComponentName(
- oldInstrumentation.getComponentName().getPackageName(), realInstrumentationName);
+ mOrigInstrumentation.getComponentName().getPackageName(), realInstrumentationName);
Reflect.setField(mRealInstrumentation, "mComponent", newName);
}
diff --git a/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java b/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java
index c1682e8..c382799 100644
--- a/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java
+++ b/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java
@@ -103,6 +103,10 @@ final class ClassLoaderPatcher {
*/
void importNativeLibs(File libDir) throws ReflectiveOperationException, IOException {
Log.i(TAG, "Importing native libraries from: " + libDir);
+ if (!libDir.exists()) {
+ Log.i(TAG, "No native libs exist.");
+ return;
+ }
// The library copying is not necessary on older devices, but we do it anyways to
// simplify things (it's fast compared to dexing).
// https://code.google.com/p/android/issues/detail?id=79480
@@ -169,28 +173,34 @@ final class ClassLoaderPatcher {
private static void copyChangedFiles(File srcDir, File dstDir) throws IOException {
// No need to delete stale libs since libraries are loaded explicitly.
+ int numNotChanged = 0;
for (File f : srcDir.listFiles()) {
// Note: Tried using hardlinks, but resulted in EACCES exceptions.
File dest = new File(dstDir, f.getName());
- copyIfModified(f, dest);
+ if (!copyIfModified(f, dest)) {
+ numNotChanged++;
+ }
+ }
+ if (numNotChanged > 0) {
+ Log.i(TAG, numNotChanged + " libs already up-to-date.");
}
}
- private static void copyIfModified(File src, File dest) throws IOException {
+ private static boolean copyIfModified(File src, File dest) throws IOException {
long lastModified = src.lastModified();
- if (!dest.exists() || dest.lastModified() != lastModified) {
- Log.i(TAG, "Copying " + src + " -> " + dest);
- FileInputStream istream = new FileInputStream(src);
- FileOutputStream ostream = new FileOutputStream(dest);
- ostream.getChannel().transferFrom(istream.getChannel(), 0, istream.getChannel().size());
- istream.close();
- ostream.close();
- dest.setReadable(true, false);
- dest.setExecutable(true, false);
- dest.setLastModified(lastModified);
- } else {
- Log.i(TAG, "Up-to-date: " + dest);
+ if (dest.exists() && dest.lastModified() == lastModified) {
+ return false;
}
+ Log.i(TAG, "Copying " + src + " -> " + dest);
+ FileInputStream istream = new FileInputStream(src);
+ FileOutputStream ostream = new FileOutputStream(dest);
+ ostream.getChannel().transferFrom(istream.getChannel(), 0, istream.getChannel().size());
+ istream.close();
+ ostream.close();
+ dest.setReadable(true, false);
+ dest.setExecutable(true, false);
+ dest.setLastModified(lastModified);
+ return true;
}
private void ensureAppFilesSubDirExists() {