11package com .surcumference .fingerprint .xposed .loader ;
22
33import android .content .Context ;
4- import android .content .pm .ApplicationInfo ;
5- import android .content .pm .PackageManager ;
6- import android .database .Cursor ;
7- import android .net .Uri ;
84
9- import com .surcumference .fingerprint .BuildConfig ;
10- import com .surcumference .fingerprint .util .ReflectionUtils ;
11- import com .surcumference .fingerprint .util .log .L ;
12-
13- import java .io .File ;
14- import java .io .IOException ;
15- import java .lang .reflect .Field ;
165import java .lang .reflect .InvocationTargetException ;
176import java .lang .reflect .Method ;
18- import java .util .ArrayList ;
19- import java .util .Enumeration ;
207import java .util .HashMap ;
21- import java .util .List ;
228import java .util .Map ;
239
24- import dalvik .system .BaseDexClassLoader ;
25- import dalvik .system .DexFile ;
2610import de .robv .android .xposed .callbacks .XC_LoadPackage ;
2711
2812/**
3115
3216public class XposedPluginLoader {
3317
34- //TODO 受Xposed機制影響 這玩意好像是廢的, 待檢查
3518 private static Map <Class , Object > sPluginCache = new HashMap <>();
3619
3720 public static void load (Class pluginClz , Context context , XC_LoadPackage .LoadPackageParam lpparam ) throws Exception {
3821 Object pluginObj ;
39- if (BuildConfig .DEBUG ) {
40- pluginObj = loadFromDex (context , pluginClz );
41- } else {
42- if ((pluginObj = sPluginCache .get (pluginClz )) == null ) {
43- synchronized (pluginClz ) {
44- if ((pluginObj = sPluginCache .get (pluginClz )) == null ) {
45- pluginObj = loadFromLocal (pluginClz );
46- sPluginCache .put (pluginClz , pluginObj );
47- }
22+ if ((pluginObj = sPluginCache .get (pluginClz )) == null ) {
23+ synchronized (pluginClz ) {
24+ if ((pluginObj = sPluginCache .get (pluginClz )) == null ) {
25+ pluginObj = loadFromLocal (pluginClz );
26+ sPluginCache .put (pluginClz , pluginObj );
4827 }
4928 }
5029 }
5130 callPluginMain (pluginObj , context , lpparam );
5231 }
5332
54- private static Object loadFromDex (Context context , Class pluginClz ) throws Exception {
55- File apkFile = getModuleApkFile (context );
56- File odexDir = context .getCacheDir ();
57- ClassLoader xposedClassLoader = XposedPluginLoader .class .getClassLoader ();
58- hijackDexElements (xposedClassLoader , apkFile , odexDir );
59- Method findClzMethod = BaseDexClassLoader .class .getDeclaredMethod ("findClass" , String .class );
60- findClzMethod .setAccessible (true );
61- Class <?> clz = (Class <?>) findClzMethod .invoke (xposedClassLoader , pluginClz .getName ());
62- return clz .newInstance ();
63- }
64-
65- private static File getModuleApkFile (Context context ) {
66- try {
67- ApplicationInfo info = context .getApplicationContext ().getPackageManager ().getApplicationInfo (BuildConfig .APPLICATION_ID , 0 );
68- return new File (info .sourceDir );
69- } catch (PackageManager .NameNotFoundException e ) {
70- L .e (e );
71- }
72- return new File ("/data/local/tmp/" + BuildConfig .APPLICATION_ID + ".apk" );
73- }
74-
75- public static final String COLUMN_KEY = "key" ;
76- public static final String COLUMN_TYPE = "type" ;
77- public static final String COLUMN_VALUE = "value" ;
78-
79- private static Map <String , Object > queryAll (Uri baseUri , Context context ) {
80- Uri uri = baseUri .buildUpon ().appendPath ("" ).build ();
81- String [] columns = {COLUMN_KEY , COLUMN_TYPE , COLUMN_VALUE };
82- Cursor cursor = query (context , uri , columns );
83- try {
84- HashMap <String , Object > map = new HashMap <String , Object >();
85- if (cursor == null ) {
86- return map ;
87- }
88- while (cursor .moveToNext ()) {
89- String name = cursor .getString (0 );
90- map .put (name , getValue (cursor , 1 , 2 ));
91- }
92- return map ;
93- } finally {
94- if (cursor != null ) {
95- cursor .close ();
96- }
97- }
98- }
99-
100- private static Object getValue (Cursor cursor , int typeCol , int valueCol ) {
101- int expectedType = cursor .getInt (typeCol );
102- switch (expectedType ) {
103- case RemoteContract .TYPE_STRING :
104- return cursor .getString (valueCol );
105- case RemoteContract .TYPE_STRING_SET :
106- return cursor .getString (valueCol );
107- case RemoteContract .TYPE_INT :
108- return cursor .getInt (valueCol );
109- case RemoteContract .TYPE_LONG :
110- return cursor .getLong (valueCol );
111- case RemoteContract .TYPE_FLOAT :
112- return cursor .getFloat (valueCol );
113- case RemoteContract .TYPE_BOOLEAN :
114- return cursor .getInt (valueCol ) != 0 ;
115- default :
116- throw new AssertionError ("Invalid expected type: " + expectedType );
117- }
118- }
119- public static class RemoteContract {
120- public static final String COLUMN_KEY = "key" ;
121- public static final String COLUMN_TYPE = "type" ;
122- public static final String COLUMN_VALUE = "value" ;
123- public static final String [] COLUMN_ALL = {
124- RemoteContract .COLUMN_KEY ,
125- RemoteContract .COLUMN_TYPE ,
126- RemoteContract .COLUMN_VALUE
127- };
128-
129- public static final int TYPE_NULL = 0 ;
130- public static final int TYPE_STRING = 1 ;
131- public static final int TYPE_STRING_SET = 2 ;
132- public static final int TYPE_INT = 3 ;
133- public static final int TYPE_LONG = 4 ;
134- public static final int TYPE_FLOAT = 5 ;
135- public static final int TYPE_BOOLEAN = 6 ;
136- }
137-
138-
139-
140- private static Cursor query (Context context , Uri uri , String [] columns ) {
141- Cursor cursor = null ;
142- try {
143- cursor = context .getContentResolver ().query (uri , columns , null , null , null );
144- } catch (Exception e ) {
145- L .e (e );
146- }
147- return cursor ;
148- }
149- private static void hijackDexElements (ClassLoader classLoader , File apkFile , File odexFile ) {
150- try {
151- Field pathListField = BaseDexClassLoader .class .getDeclaredField ("pathList" );
152- pathListField .setAccessible (true );
153- Object pathList = pathListField .get (classLoader );
154- Class DexPathListClass = pathList .getClass ();
155- Field dexElementsField = DexPathListClass .getDeclaredField ("dexElements" );
156- dexElementsField .setAccessible (true );
157- Object [] dexElements = (Object [])dexElementsField .get (pathList );
158- for (Object dexElement : dexElements ) {
159- L .d ("dexElement" , dexElement );
160- }
161- ArrayList <IOException > suppressedExceptions = new ArrayList <IOException >();
162- ArrayList <File > apkFileList = new ArrayList <>();
163- apkFileList .add (apkFile );
164- Object [] apkDexElements = makePathElements (pathList , apkFileList , odexFile , suppressedExceptions );
165- if (suppressedExceptions .size () > 0 ) {
166- for (IOException e : suppressedExceptions ) {
167- L .e ("Exception in makePathElements" , e );
168- }
169- }
170- dexElementsField .set (pathList , apkDexElements );
171- } catch (Exception e ){
172- L .e (e );
173- }
174- }
175-
176- private static Object [] makePathElements (
177- Object dexPathList , ArrayList <File > files , File optimizedDirectory ,
178- ArrayList <IOException > suppressedExceptions )
179- throws IllegalAccessException , InvocationTargetException ,
180- NoSuchMethodException {
181- Method makeDexElements =
182- ReflectionUtils .findMethod (dexPathList , "makePathElements" , List .class , File .class ,
183- List .class );
184- return (Object []) makeDexElements .invoke (dexPathList , files , optimizedDirectory ,
185- suppressedExceptions );
186- }
187-
18833 private static Object loadFromLocal (Class pluginClz ) throws ClassNotFoundException , NoSuchMethodException , InvocationTargetException , IllegalAccessException , InstantiationException {
18934 return pluginClz .newInstance ();
19035 }
@@ -194,27 +39,4 @@ private static void callPluginMain(Object pluginObj, Context context, XC_LoadPac
19439 method .invoke (pluginObj , context , lpparam );
19540 }
19641
197- private static boolean forceClassLoaderReloadClasses (ClassLoader classLoader , String packageNameStartWith , String apkPath ) {
198- try {
199- Method findClzMethod = BaseDexClassLoader .class .getDeclaredMethod ("findClass" , String .class );
200- findClzMethod .setAccessible (true );
201- packageNameStartWith = packageNameStartWith + "." ;
202- DexFile dexFile = new DexFile (apkPath );
203- Enumeration <String > classNames = dexFile .entries ();
204- while (classNames .hasMoreElements ()) {
205- String className = classNames .nextElement ();
206- if (className .startsWith (packageNameStartWith )) {
207- try {
208- findClzMethod .invoke (classLoader , className );
209- } catch (Exception e ) {
210- L .d (e );
211- }
212- }
213- }
214- return true ;
215- } catch (Exception e ) {
216- L .e (e );
217- }
218- return false ;
219- }
22042}
0 commit comments