Skip to content

Commit

Permalink
[Android] Rework multidex and enable multidex for unit_tests_apk.
Browse files Browse the repository at this point in the history
This allows multidex to be used in release builds.

BUG=272790

Review URL: https://codereview.chromium.org/1581563003

Cr-Commit-Position: refs/heads/master@{#369715}
  • Loading branch information
jbudorick authored and Commit bot committed Jan 15, 2016
1 parent 68f0a84 commit f743bef
Show file tree
Hide file tree
Showing 14 changed files with 244 additions and 137 deletions.
9 changes: 4 additions & 5 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -2057,9 +2057,10 @@ if (is_android) {

DEPRECATED_java_in_dir = "android/java/src"

# A new version of NativeLibraries.java (with the actual correct values)
# will be created when creating an apk.
# New versions of ChromiumMultiDex.java and NativeLibraries.java
# (with the actual correct values) will be created when creating an apk.
jar_excluded_patterns = [
"*/ChromiumMultiDex.class",
"*/NativeLibraries.class",
"*/NativeLibraries##*.class",
]
Expand Down Expand Up @@ -2094,6 +2095,7 @@ if (is_android) {
"//third_party/robolectric:android-all-4.3_r2-robolectric-0",
"//third_party/robolectric:robolectric_java",
]
srcjar_deps = [ ":base_multidex_gen" ]
}

# GYP: //base.gyp:base_junit_tests
Expand Down Expand Up @@ -2128,9 +2130,6 @@ if (is_android) {
sources = [
"android/java/templates/ChromiumMultiDex.template",
]
if (is_debug) {
defines = [ "MULTIDEX_CONFIGURATION_Debug" ]
}
package_name = "org/chromium/base/multidex"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import android.os.Bundle;
import android.view.Window;

import org.chromium.base.multidex.ChromiumMultiDex;
import org.chromium.base.multidex.ChromiumMultiDexInstaller;

/**
* Basic application functionality that should be shared among all browser applications.
Expand All @@ -31,7 +31,7 @@ protected BaseChromiumApplication(boolean shouldInitializeApplicationStatusTrack
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
ChromiumMultiDex.install(this);
ChromiumMultiDexInstaller.install(this);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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.multidex;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.multidex.MultiDex;

import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* Performs multidex installation for non-isolated processes.
*/
public class ChromiumMultiDexInstaller {

private static final String TAG = "base_multidex";

/**
* Suffix for the meta-data tag in the AndroidManifext.xml that determines whether loading
* secondary dexes should be skipped for a given process name.
*/
private static final String IGNORE_MULTIDEX_KEY = ".ignore_multidex";

/**
* Installs secondary dexes if possible/necessary.
*
* Isolated processes (e.g. renderer processes) can't load secondary dex files on
* K and below, so we don't even try in that case.
*
* In release builds of app apks (as opposed to test apks), this is a no-op because:
* - multidex isn't necessary in release builds because we run proguard there and
* thus aren't threatening to hit the dex limit; and
* - calling MultiDex.install, even in the absence of secondary dexes, causes a
* significant regression in start-up time (crbug.com/525695).
*
* @param context The application context.
*/
@VisibleForTesting
public static void install(Context context) {
if (!ChromiumMultiDex.isMultidexEnabled()) return;

// TODO(jbudorick): Back out this version check once support for K & below works.
// http://crbug.com/512357
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
&& !shouldInstallMultiDex(context)) {
Log.i(TAG, "Skipping multidex installation: not needed for process.");
} else {
MultiDex.install(context);
Log.i(TAG, "Completed multidex installation.");
}
}

private static String getProcessName(Context context) {
try {
String currentProcessName = null;
int pid = android.os.Process.myPid();

ActivityManager manager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.pid == pid) {
currentProcessName = processInfo.processName;
break;
}
}

return currentProcessName;
} catch (SecurityException ex) {
return null;
}
}

// Determines whether MultiDex should be installed for the current process. Isolated
// Processes should skip MultiDex as they can not actually access the files on disk.
// Privileged processes need ot have all of their dependencies in the MainDex for
// performance reasons.
private static boolean shouldInstallMultiDex(Context context) {
try {
Method isIsolatedMethod =
android.os.Process.class.getMethod("isIsolated");
Object retVal = isIsolatedMethod.invoke(null);
if (retVal != null && retVal instanceof Boolean && ((Boolean) retVal)) {
return false;
}
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException e) {
// Ignore and fall back to checking the app processes.
}

String currentProcessName = getProcessName(context);
if (currentProcessName == null) return true;

PackageManager packageManager = context.getPackageManager();
try {
ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(),
PackageManager.GET_META_DATA);
if (appInfo == null || appInfo.metaData == null) return true;
return !appInfo.metaData.getBoolean(currentProcessName + IGNORE_MULTIDEX_KEY, false);
} catch (PackageManager.NameNotFoundException e) {
return true;
}
}

}
113 changes: 10 additions & 103 deletions base/android/java/templates/ChromiumMultiDex.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,115 +4,22 @@

package org.chromium.base.multidex;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Process;
import android.support.multidex.MultiDex;

import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* Performs multidex installation for non-isolated processes.
* Multidex configuration. Generated on a per-target basis.
*/
public class ChromiumMultiDex {
class ChromiumMultiDex {

private static final String TAG = "base_multidex";

/**
* Suffix for the meta-data tag in the AndroidManifext.xml that determines whether loading
* secondary dexes should be skipped for a given process name.
*/
private static final String IGNORE_MULTIDEX_KEY = ".ignore_multidex";

/**
* Installs secondary dexes if possible/necessary.
*
* Isolated processes (e.g. renderer processes) can't load secondary dex files on
* K and below, so we don't even try in that case.
/** Whether multidex is enabled for this target.
*
* In release builds, this is a no-op because:
* - multidex isn't necessary in release builds because we run proguard there and
* thus aren't threatening to hit the dex limit; and
* - calling MultiDex.install, even in the absence of secondary dexes, causes a
* significant regression in start-up time (crbug.com/525695).
*
* @param context The application context.
* This has to be a function instead of a static final boolean s.t. the initial false value
* doesn't get optimized into {@link ChromiumMultiDexInstaller} at base_java compile time.
*/
@VisibleForTesting
#if defined(MULTIDEX_CONFIGURATION_Debug)
public static void install(Context context) {
// TODO(jbudorick): Back out this version check once support for K & below works.
// http://crbug.com/512357
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
&& !shouldInstallMultiDex(context)) {
Log.i(TAG, "Skipping multidex installation: not needed for process.");
} else {
MultiDex.install(context);
Log.i(TAG, "Completed multidex installation.");
}
}

private static String getProcessName(Context context) {
try {
String currentProcessName = null;
int pid = android.os.Process.myPid();

ActivityManager manager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.pid == pid) {
currentProcessName = processInfo.processName;
break;
}
}

return currentProcessName;
} catch (SecurityException ex) {
return null;
}
}

// Determines whether MultiDex should be installed for the current process. Isolated
// Processes should skip MultiDex as they can not actually access the files on disk.
// Privileged processes need ot have all of their dependencies in the MainDex for
// performance reasons.
private static boolean shouldInstallMultiDex(Context context) {
try {
Method isIsolatedMethod =
android.os.Process.class.getMethod("isIsolated");
Object retVal = isIsolatedMethod.invoke(null);
if (retVal != null && retVal instanceof Boolean && ((Boolean) retVal)) {
return false;
}
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException e) {
// Ignore and fall back to checking the app processes.
}

String currentProcessName = getProcessName(context);
if (currentProcessName == null) return true;

PackageManager packageManager = context.getPackageManager();
try {
ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(),
PackageManager.GET_META_DATA);
if (appInfo == null || appInfo.metaData == null) return true;
return !appInfo.metaData.getBoolean(currentProcessName + IGNORE_MULTIDEX_KEY, false);
} catch (PackageManager.NameNotFoundException e) {
return true;
}
}
static boolean isMultidexEnabled() {
#if defined(ENABLE_MULTIDEX)
return true;
#else
public static void install(Context context) {
}
return false;
#endif
}

}
14 changes: 10 additions & 4 deletions base/base.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -1489,9 +1489,6 @@
'variables': {
'package_name': 'org/chromium/base/multidex',
'template_deps': [],
'additional_gcc_preprocess_options': [
'--defines', 'MULTIDEX_CONFIGURATION_<(CONFIGURATION_NAME)',
],
},
'includes': ['../build/android/java_cpp_template.gypi'],
},
Expand All @@ -1510,7 +1507,10 @@
'type': 'none',
'variables': {
'java_in_dir': 'android/java',
'jar_excluded_classes': [ '*/NativeLibraries.class' ],
'jar_excluded_classes': [
'*/ChromiumMultiDex.class',
'*/NativeLibraries.class',
],
},
'dependencies': [
'base_java_application_state',
Expand All @@ -1522,6 +1522,11 @@
'../third_party/android_tools/android_tools.gyp:android_support_multidex_javalib',
'../third_party/jsr-305/jsr-305.gyp:jsr_305_javalib',
],
'all_dependent_settings': {
'variables': {
'generate_multidex_config': 1,
},
},
'includes': [ '../build/java.gypi' ],
},
{
Expand Down Expand Up @@ -1583,6 +1588,7 @@
'target_name': 'base_junit_test_support',
'type': 'none',
'dependencies': [
'base_multidex_gen',
'../testing/android/junit/junit_test.gyp:junit_test_support',
'../third_party/android_tools/android_tools.gyp:android_support_multidex_javalib',
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import org.chromium.base.Log;
import org.chromium.base.SysUtils;
import org.chromium.base.multidex.ChromiumMultiDex;
import org.chromium.base.multidex.ChromiumMultiDexInstaller;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisableIfSkipCheck;
import org.chromium.base.test.util.MinAndroidSdkLevel;
Expand All @@ -37,7 +37,7 @@ public class BaseInstrumentationTestRunner extends InstrumentationTestRunner {

@Override
public void onCreate(Bundle arguments) {
ChromiumMultiDex.install(getTargetContext());
ChromiumMultiDexInstaller.install(getTargetContext());
super.onCreate(arguments);
}

Expand Down
Loading

0 comments on commit f743bef

Please sign in to comment.