OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.webapk.shell_apk; | |
6 | |
7 import android.app.Application; | |
8 import android.content.Context; | |
9 import android.util.Log; | |
10 | |
11 import org.chromium.webapk.lib.common.WebApkUtils; | |
12 | |
13 import java.lang.reflect.Array; | |
14 import java.util.List; | |
15 | |
16 /** | |
17 * Example application for a minted APK. | |
18 */ | |
19 public class WebApkApplication extends Application { | |
20 // Context of Chrome. | |
21 private Context mRemoteContext = null; | |
22 | |
23 private static final String TAG = "cr.WebApkApplication"; | |
24 /** | |
25 * Copy all the objects from a specified field of the hostInstance to the sa
me field of the | |
26 * mintInstance. As a result, the given field of the mintInstance will conta
in all the | |
27 * objects from both instances. | |
28 * @param mintInstance the first instance which contains a field of the give
n fieldName. | |
29 * @param fieldName the name of field to expand to an Object array. | |
30 * @param hostInstance the second instance which has a field of the given fi
eldName. | |
31 * @throws NoSuchFieldException | |
32 * @throws IllegalArgumentException | |
33 * @throws IllegalAccessException | |
34 */ | |
35 private static void expandField(Object mintInstance, String fieldName, Objec
t hostInstance) | |
36 throws NoSuchFieldException, IllegalArgumentException, | |
37 IllegalAccessException { | |
38 try { | |
39 Object hostCurrentDirs = Reflect.getField(hostInstance, fieldName); | |
40 Object mintCurrentDirs = Reflect.getField(mintInstance, fieldName); | |
41 // Switched from an array to an ArrayList in Lollipop. | |
42 if (mintCurrentDirs instanceof List) { | |
43 List<Object> mintDirsAsList = (List<Object>) mintCurrentDirs; | |
44 concatAndRemoveEndDuplicates(mintDirsAsList, (List<Object>) host
CurrentDirs); | |
45 } else { | |
46 Object[] mintDirsAsArray = (Object[]) mintCurrentDirs; | |
47 Object[] hostDirsAsArray = (Object[]) hostCurrentDirs; | |
48 Reflect.setField(mintInstance, fieldName, | |
49 concatAndRemoveEndDuplicates(mintDirsAsArray, hostDirsAs
Array)); | |
50 } | |
51 } catch (ReflectiveOperationException e) { | |
52 e.printStackTrace(); | |
53 } | |
54 } | |
55 | |
56 /** | |
57 * Combines two arrays into a new array. | |
58 * If the two arrays end with duplicated elements, keep one copy only. For e
xample, | |
59 * the first array is [A, B, C, D], and the second array is [E, F, C, D], th
e combined one is | |
60 * [A, B, E, F, C, D]. The unique elements are stored before the duplicates. | |
61 * The arrays must be of the same type. | |
62 * @param mintArrays: the array from a WebAPK | |
63 * @param hostArrays: the array from the host | |
64 * @return: a combined array consists of [WebAPK unique elements, host's uni
que elements, | |
65 * duplicated one] | |
66 */ | |
67 private static Object[] concatAndRemoveEndDuplicates(Object[] mintArrays, Ob
ject[] hostArrays) { | |
68 int duplicate1 = mintArrays.length - 1; | |
69 int duplicate2 = hostArrays.length - 1; | |
70 int count = 0; | |
71 while (duplicate1 >= 0 && duplicate2 >= 0 | |
72 && mintArrays[duplicate1].toString().equals(hostArrays[duplicate
2].toString())) { | |
73 --duplicate1; | |
74 --duplicate2; | |
75 ++count; | |
76 } | |
77 Object[] combined = (Object[]) Array.newInstance(mintArrays.getClass().g
etComponentType(), | |
78 mintArrays.length + hostArrays.length - count); | |
79 if (mintArrays.length - count > 0) { | |
80 System.arraycopy(mintArrays, 0, combined, 0, mintArrays.length - cou
nt); | |
81 } | |
82 System.arraycopy(hostArrays, 0, combined, mintArrays.length - count, hos
tArrays.length); | |
83 return combined; | |
84 } | |
85 | |
86 /** | |
87 * Add the unique elements of the second list to the first one. | |
88 * If the two lists end with duplicated elements, keep one copy only. For ex
ample, | |
89 * the first list is [A, B, C, D], and the second list is [E, F, C, D], the
combined one is | |
90 * [A, B, E, F, C, D]. The unique elements are store before the duplicates. | |
91 * The lists must be of the same type. | |
92 * @param mintList: the list from a WebAPK | |
93 * @param hostList: the list from the host | |
94 * @return: a combined list consists of [WebAPK unique elements, host's uniq
ue elements, | |
95 * duplicated one] | |
96 */ | |
97 private static void concatAndRemoveEndDuplicates(List<Object> mintList, List
<Object> hostList) { | |
98 int duplicate1 = mintList.size() - 1; | |
99 int duplicate2 = hostList.size() - 1; | |
100 while (duplicate1 >= 0 && duplicate2 >= 0 | |
101 && mintList.get(duplicate1).toString().equals(hostList.get(dupli
cate2).toString())) | |
102 { | |
103 --duplicate1; | |
104 --duplicate2; | |
105 } | |
106 for (int i = 0; i < duplicate2 + 1; i++) { | |
107 mintList.add(duplicate1 + i, hostList.get(i)); | |
108 } | |
109 } | |
110 | |
111 private void addExternalLoader() throws ReflectiveOperationException { | |
112 if (mRemoteContext == null) { | |
113 Log.w(TAG, "Failed to add external loader since the remote context i
s null."); | |
114 return; | |
115 } | |
116 ClassLoader externalLoader = mRemoteContext.getClassLoader(); | |
117 ClassLoader mintLoader = getClassLoader(); | |
118 | |
119 Object extDexPathList = Reflect.getField(externalLoader, "pathList"); | |
120 Object mintDexPathList = Reflect.getField(mintLoader, "pathList"); | |
121 expandField(mintDexPathList, "dexElements", extDexPathList); | |
122 } | |
123 | |
124 private void addNativeLibrarySearchPath() throws ReflectiveOperationExceptio
n { | |
125 if (mRemoteContext == null) { | |
126 Log.w(TAG, "Failed to add external loader since the remote context i
s null."); | |
127 return; | |
128 } | |
129 ClassLoader externalLoader = mRemoteContext.getClassLoader(); | |
130 ClassLoader mintLoader = getClassLoader(); | |
131 | |
132 // Since both WebAPK and its host have the "system/lib" and "vendor/lib"
at the end of | |
133 // the native library directory path list, we want to add host's directo
ries before the | |
134 // system directory, and only keep one copy of the system directories. | |
135 // This is because when System.loadLibrary() is called, we want it searc
h the host's | |
136 // native library directories first. If a ".so" file doesn't exit, then
search /system/lib. | |
137 Object extDexPathList = Reflect.getField(externalLoader, "pathList"); | |
138 Object mintDexPathList = Reflect.getField(mintLoader, "pathList"); | |
139 expandField(mintDexPathList, "nativeLibraryDirectories", extDexPathList)
; | |
140 expandField(mintDexPathList, "nativeLibraryPathElements", extDexPathList
); | |
141 } | |
142 | |
143 @Override | |
144 public void onCreate() { | |
145 super.onCreate(); | |
146 mRemoteContext = WebApkUtils.getHostBrowserContext(this); | |
147 try { | |
148 addExternalLoader(); | |
149 addNativeLibrarySearchPath(); | |
150 } catch (ReflectiveOperationException e) { | |
151 e.printStackTrace(); | |
152 } | |
153 Log.w(TAG, "Successfully add external loader."); | |
154 } | |
155 } | |
OLD | NEW |