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

Unified Diff: web_apks/minting_example/src/org/chromium/minting/MintingApplication.java

Issue 1710083004: Support Crazy Linker in WebAPK to load Chrome's uncompressed native libraries. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: nits 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « build/android/rezip/RezipApk.java ('k') | web_apks/minting_example/src/org/chromium/minting/Reflect.java » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: web_apks/minting_example/src/org/chromium/minting/MintingApplication.java
diff --git a/web_apks/minting_example/src/org/chromium/minting/MintingApplication.java b/web_apks/minting_example/src/org/chromium/minting/MintingApplication.java
index 0ce9de0ae2e63fd1c23fee9fe67d0655e037dcde..2e23603c310635da54815260a9cebabda1ce0eea 100644
--- a/web_apks/minting_example/src/org/chromium/minting/MintingApplication.java
+++ b/web_apks/minting_example/src/org/chromium/minting/MintingApplication.java
@@ -9,9 +9,8 @@ import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
-import java.io.File;
import java.lang.reflect.Array;
-import java.lang.reflect.Field;
+import java.util.List;
/**
* Example application for a minted APK.
@@ -25,49 +24,94 @@ public class MintingApplication extends Application {
private Context mRemoteContext = null;
private static final String TAG = "cr.MintingApplication";
- private static final String SECONDARY_FOLDER_NAME = "code_cache" + File.separator
- + "secondary-dexes";
-
- private static Field findField(Object instance, String name) throws NoSuchFieldException {
- for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
- try {
- Field field = clazz.getDeclaredField(name);
- if (!field.isAccessible()) {
- field.setAccessible(true);
- }
- return field;
- } catch (NoSuchFieldException e) {
- // ignore and search next
- }
- }
- throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
- }
-
/**
- * Copy all the objects from a specified field of the second instance to the same field of the
- * first instance. As a result, the given field of the first instance will contain all the
+ * Copy all the objects from a specified field of the hostInstance to the same field of the
+ * mintInstance. As a result, the given field of the mintInstance will contain all the
* objects from both instances.
- * @param instance the first instance which contains a field of the given fieldName.
+ * @param mintInstance the first instance which contains a field of the given fieldName.
* @param fieldName the name of field to expand to an Object array.
- * @param instance2 the second instance which has a field of the given fieldName.
+ * @param hostInstance the second instance which has a field of the given fieldName.
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
- private static void expandFieldArray(Object instance, String fieldName,
- Object instance2) throws NoSuchFieldException, IllegalArgumentException,
+ private static void expandField(Object mintInstance, String fieldName, Object hostInstance)
+ throws NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
- Field jlrField = findField(instance, fieldName);
- Object[] original = (Object[]) jlrField.get(instance);
- Object[] additional = (Object[]) jlrField.get(instance2);
- Object[] combined = (Object[]) Array.newInstance(
- original.getClass().getComponentType(), original.length + additional.length);
- System.arraycopy(original, 0, combined, 0, original.length);
- System.arraycopy(additional, 0, combined, original.length, additional.length);
- jlrField.set(instance, combined);
+ try {
+ Object hostCurrentDirs = Reflect.getField(hostInstance, fieldName);
+ Object mintCurrentDirs = Reflect.getField(mintInstance, fieldName);
+ // Switched from an array to an ArrayList in Lollipop.
+ if (mintCurrentDirs instanceof List) {
+ List<Object> mintDirsAsList = (List<Object>) mintCurrentDirs;
+ concatAndRemoveEndDuplicates(mintDirsAsList, (List<Object>) hostCurrentDirs);
+ } else {
+ Object[] mintDirsAsArray = (Object[]) mintCurrentDirs;
+ Object[] hostDirsAsArray = (Object[]) hostCurrentDirs;
+ Reflect.setField(mintInstance, fieldName,
+ concatAndRemoveEndDuplicates(mintDirsAsArray, hostDirsAsArray));
+ }
+ } catch (ReflectiveOperationException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Combines two arrays into a new array.
+ * If the two arrays end with duplicated elements, keep one copy only. For example,
+ * the first array is [A, B, C, D], and the second array is [E, F, C, D], the combined one is
+ * [A, B, E, F, C, D]. The unique elements are stored before the duplicates.
+ * The arrays must be of the same type.
+ * @param mintArrays: the array from a WebAPK
+ * @param hostArrays: the array from the host
+ * @return: a combined array consists of [WebAPK unique elements, host's unique elements,
+ * duplicated one]
+ */
+ private static Object[] concatAndRemoveEndDuplicates(Object[] mintArrays, Object[] hostArrays) {
+ int duplicate1 = mintArrays.length - 1;
+ int duplicate2 = hostArrays.length - 1;
+ int count = 0;
+ while (duplicate1 >= 0 && duplicate2 >= 0
+ && mintArrays[duplicate1].toString().equals(hostArrays[duplicate2].toString())) {
+ --duplicate1;
+ --duplicate2;
+ ++count;
+ }
+ Object[] combined = (Object[]) Array.newInstance(mintArrays.getClass().getComponentType(),
+ mintArrays.length + hostArrays.length - count);
+ if (mintArrays.length - count > 0) {
+ System.arraycopy(mintArrays, 0, combined, 0, mintArrays.length - count);
+ }
+ System.arraycopy(hostArrays, 0, combined, mintArrays.length - count, hostArrays.length);
+ return combined;
}
- private void addExternalLoader() {
+ /**
+ * Add the unique elements of the second list to the first one.
+ * If the two lists end with duplicated elements, keep one copy only. For example,
+ * the first list is [A, B, C, D], and the second list is [E, F, C, D], the combined one is
+ * [A, B, E, F, C, D]. The unique elements are store before the duplicates.
+ * The lists must be of the same type.
+ * @param mintList: the list from a WebAPK
+ * @param hostList: the list from the host
+ * @return: a combined list consists of [WebAPK unique elements, host's unique elements,
+ * duplicated one]
+ */
+ private static void concatAndRemoveEndDuplicates(List<Object> mintList, List<Object> hostList) {
+ int duplicate1 = mintList.size() - 1;
+ int duplicate2 = hostList.size() - 1;
+ while (duplicate1 >= 0 && duplicate2 >= 0
+ && mintList.get(duplicate1).toString().equals(hostList.get(duplicate2).toString()))
+ {
+ --duplicate1;
+ --duplicate2;
+ }
+ for (int i = 0; i < duplicate2 + 1; i++) {
+ mintList.add(duplicate1 + i, hostList.get(i));
+ }
+ }
+
+ private void addExternalLoader() throws ReflectiveOperationException {
if (mRemoteContext == null) {
Log.w(TAG, "Failed to add external loader since the remote context is null.");
return;
@@ -75,15 +119,28 @@ public class MintingApplication extends Application {
ClassLoader externalLoader = mRemoteContext.getClassLoader();
ClassLoader mintLoader = getClassLoader();
- try {
- Field field = findField(externalLoader, "pathList");
- Object dexOriginal = field.get(mintLoader);
- Object dexAdditional = field.get(externalLoader);
- expandFieldArray(dexOriginal, "dexElements", dexAdditional);
- } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e1) {
- e1.printStackTrace();
- throw new RuntimeException(e1);
+ Object extDexPathList = Reflect.getField(externalLoader, "pathList");
+ Object mintDexPathList = Reflect.getField(mintLoader, "pathList");
+ expandField(mintDexPathList, "dexElements", extDexPathList);
+ }
+
+ private void addNativeLibrarySearchPath() throws ReflectiveOperationException {
+ if (mRemoteContext == null) {
+ Log.w(TAG, "Failed to add external loader since the remote context is null.");
+ return;
}
+ ClassLoader externalLoader = mRemoteContext.getClassLoader();
+ ClassLoader mintLoader = getClassLoader();
+
+ // Since both WebAPK and its host have the "system/lib" and "vendor/lib" at the end of
+ // the native library directory path list, we want to add host's directories before the
+ // system directory, and only keep one copy of the system directories.
+ // This is because when System.loadLibrary() is called, we want it search the host's
+ // native library directories first. If a ".so" file doesn't exit, then search /system/lib.
+ Object extDexPathList = Reflect.getField(externalLoader, "pathList");
+ Object mintDexPathList = Reflect.getField(mintLoader, "pathList");
+ expandField(mintDexPathList, "nativeLibraryDirectories", extDexPathList);
+ expandField(mintDexPathList, "nativeLibraryPathElements", extDexPathList);
}
@Override
@@ -96,7 +153,12 @@ public class MintingApplication extends Application {
mRemoteContext = getApplicationContext().createPackageContext(
packageString,
Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
- addExternalLoader();
+ try {
+ addExternalLoader();
+ addNativeLibrarySearchPath();
+ } catch (ReflectiveOperationException e) {
+ e.printStackTrace();
+ }
Log.w(TAG, "Successfully add external loader.");
} catch (NameNotFoundException e) {
e.printStackTrace();
« no previous file with comments | « build/android/rezip/RezipApk.java ('k') | web_apks/minting_example/src/org/chromium/minting/Reflect.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698