1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.preload.check;
18 
19 import dalvik.system.DexFile;
20 
21 import java.io.BufferedInputStream;
22 import java.io.BufferedOutputStream;
23 import java.io.File;
24 import java.io.FileOutputStream;
25 import java.lang.reflect.Field;
26 import java.lang.reflect.Method;
27 import java.net.URL;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.List;
31 
32 public class Util {
33     private static Field statusField;
34 
35     static {
36         try {
37             Class<?> klass = Class.class;
38             statusField = klass.getDeclaredField("status");
39             statusField.setAccessible(true);
40         } catch (Throwable t) {
41             throw new RuntimeException(t);
42         }
43         // Reset the framework's kill handler.
44         Thread.setDefaultUncaughtExceptionHandler(null);
45     }
46 
getBootDexFiles()47     public static Collection<DexFile> getBootDexFiles() throws Exception {
48         Class<?> vmClassLoaderClass = Class.forName("java.lang.VMClassLoader");
49         Method getResources = vmClassLoaderClass.getDeclaredMethod("getResources", String.class);
50         getResources.setAccessible(true);
51         ArrayList<DexFile> res = new ArrayList<>();
52         for (int i = 1;; i++) {
53             try {
54                 String name = "classes" + (i > 1 ? String.valueOf(i) : "") + ".dex";
55                 @SuppressWarnings("unchecked")
56                 List<URL> urls = (List<URL>) getResources.invoke(null, name);
57                 if (urls.isEmpty()) {
58                     break;
59                 }
60                 for (URL url : urls) {
61                     // Make temp copy, so we can use public API. Would be nice to use in-memory, but
62                     // those are unstable.
63                     String tmp = "/data/local/tmp/tmp.dex";
64                     try (BufferedInputStream in = new BufferedInputStream(url.openStream());
65                             BufferedOutputStream out = new BufferedOutputStream(
66                                     new FileOutputStream(tmp))) {
67                         byte[] buf = new byte[4096];
68                         for (;;) {
69                             int r = in.read(buf);
70                             if (r == -1) {
71                                 break;
72                             }
73                             out.write(buf, 0, r);
74                         }
75                     }
76                     try {
77                         res.add(new DexFile(tmp));
78                     } catch (Exception dexError) {
79                         dexError.printStackTrace(System.out);
80                     }
81                     new File(tmp).delete();
82                 }
83             } catch (Exception ignored) {
84                 break;
85             }
86         }
87         return res;
88     }
89 
isInitialized(Class<?> klass)90     public static boolean isInitialized(Class<?> klass) throws Exception {
91         Object val = statusField.get(klass);
92         if (val == null || !(val instanceof Integer)) {
93             throw new IllegalStateException(String.valueOf(val));
94         }
95         int intVal = (int)val;
96         intVal = (intVal >> (32-4)) & 0xf;
97         return intVal >= 14;
98     }
99 
assertTrue(boolean val, String msg)100     public static void assertTrue(boolean val, String msg) {
101         if (!val) {
102             throw new RuntimeException(msg);
103         }
104     }
105 
assertInitializedState(String className, boolean expected, ClassLoader loader)106     public static void assertInitializedState(String className, boolean expected,
107             ClassLoader loader) {
108         boolean initialized;
109         try {
110             Class<?> klass = Class.forName(className, /* initialize */ false, loader);
111             initialized = isInitialized(klass);
112         } catch (Throwable t) {
113             throw new RuntimeException(t);
114         }
115         assertTrue(expected == initialized, className);
116     }
117 
assertNotInitialized(String className, ClassLoader loader)118     public static void assertNotInitialized(String className, ClassLoader loader) {
119         assertInitializedState(className, false, loader);
120     }
121 
assertInitialized(String className, ClassLoader loader)122     public static void assertInitialized(String className, ClassLoader loader) {
123         assertInitializedState(className, true, loader);
124     }
125 }
126