OSDN Git Service

PreloadCheck: Add support for regex checking
authorAndreas Gampe <agampe@google.com>
Tue, 16 Apr 2019 21:49:42 +0000 (14:49 -0700)
committerAndreas Gampe <agampe@google.com>
Tue, 16 Apr 2019 21:52:14 +0000 (14:52 -0700)
Add some reflection hackery to retrieve boot classpath dex files. This
requires unstripped jars. Then use DexFile APIs to retrieve all classes,
and run a check if the given regular exception matches.

Use the new infrastructure to check that NoPreloadHolder classes are
not initialized.

Test: atest --test-mapping frameworks/base/tools/preload-check
Change-Id: Ic19e950647e5827d3c3ec4a7109553f39be7f43a

tools/preload-check/device/src/com/android/preload/check/NotInitializedRegex.java [new file with mode: 0644]
tools/preload-check/device/src/com/android/preload/check/Util.java
tools/preload-check/src/com/android/preload/check/PreloadCheck.java

diff --git a/tools/preload-check/device/src/com/android/preload/check/NotInitializedRegex.java b/tools/preload-check/device/src/com/android/preload/check/NotInitializedRegex.java
new file mode 100644 (file)
index 0000000..261024b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.preload.check;
+
+import dalvik.system.DexFile;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class NotInitializedRegex {
+    public static void main(String[] args) throws Exception {
+        Matcher m = Pattern.compile(args[0]).matcher("");
+        boolean requiresMatch = args.length > 1 ? Boolean.parseBoolean(args[1]) : false;
+
+        Collection<DexFile> dexFiles = Util.getBootDexFiles();
+        int matched = 0, notMatched = 0;
+        for (DexFile dexFile : dexFiles) {
+            Enumeration<String> entries = dexFile.entries();
+            while (entries.hasMoreElements()) {
+                String entry = entries.nextElement();
+                m.reset(entry);
+                if (m.matches()) {
+                    System.out.println(entry + ": match");
+                    matched++;
+                    check(entry);
+                } else {
+                    System.out.println(entry + ": no match");
+                    notMatched++;
+                }
+            }
+        }
+        System.out.println("Matched: " + matched + " Not-Matched: " + notMatched);
+        if (requiresMatch && matched == 0) {
+            throw new RuntimeException("Did not find match");
+        }
+        System.out.println("OK");
+    }
+
+    private static void check(String name) {
+        Util.assertNotInitialized(name, null);
+    }
+}
index 662f67a..19cc5ab 100644 (file)
 
 package com.android.preload.check;
 
+import dalvik.system.DexFile;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
 
 public class Util {
     private static Field statusField;
@@ -31,6 +42,49 @@ public class Util {
         }
     }
 
+    public static Collection<DexFile> getBootDexFiles() throws Exception {
+        Class<?> vmClassLoaderClass = Class.forName("java.lang.VMClassLoader");
+        Method getResources = vmClassLoaderClass.getDeclaredMethod("getResources", String.class);
+        getResources.setAccessible(true);
+        LinkedList<DexFile> res = new LinkedList<>();
+        for (int i = 1;; i++) {
+            try {
+                String name = "classes" + (i > 1 ? String.valueOf(i) : "") + ".dex";
+                @SuppressWarnings("unchecked")
+                List<URL> urls = (List<URL>) getResources.invoke(null, name);
+                if (urls.isEmpty()) {
+                    break;
+                }
+                for (URL url : urls) {
+                    // Make temp copy, so we can use public API. Would be nice to use in-memory, but
+                    // those are unstable.
+                    String tmp = "/data/local/tmp/tmp.dex";
+                    try (BufferedInputStream in = new BufferedInputStream(url.openStream());
+                            BufferedOutputStream out = new BufferedOutputStream(
+                                    new FileOutputStream(tmp))) {
+                        byte[] buf = new byte[4096];
+                        for (;;) {
+                            int r = in.read(buf);
+                            if (r == -1) {
+                                break;
+                            }
+                            out.write(buf, 0, r);
+                        }
+                    }
+                    try {
+                        res.add(new DexFile(tmp));
+                    } catch (Exception dexError) {
+                        dexError.printStackTrace(System.out);
+                    }
+                    new File(tmp).delete();
+                }
+            } catch (Exception ignored) {
+                break;
+            }
+        }
+        return res;
+    }
+
     public static boolean isInitialized(Class<?> klass) throws Exception {
         Object val = statusField.get(klass);
         if (val == null || !(val instanceof Integer)) {
index 1fde402..00fd414 100644 (file)
@@ -16,7 +16,7 @@
 
 package com.android.preload.check;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -97,6 +97,14 @@ public class PreloadCheck implements IDeviceTest {
         }
     }
 
+    /**
+     * Test the classes ending in NoPreloadHolder are not initialized.
+     */
+    @Test
+    public void testNoPreloadHolder() throws Exception {
+        run("com.android.preload.check.NotInitializedRegex", ".*NoPreloadHolder$", "true");
+    }
+
     private void run(String cmd, String... args) throws Exception {
         StringBuilder sb = new StringBuilder();
         sb.append("app_process ")
@@ -107,7 +115,7 @@ public class PreloadCheck implements IDeviceTest {
             sb.append(' ').append(escape(arg));
         }
         String res = mTestDevice.executeShellCommand(sb.toString());
-        assertEquals(sb.toString(), "OK", res.trim());
+        assertTrue(sb.toString() + "\n===\n" + res, res.trim().endsWith("OK"));
     }
 
     private static String escape(String input) {