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 import java.io.File;
18 import java.lang.reflect.Method;
19 import java.util.Base64;
20 
21 public class Main {
main(String[] args)22   public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
23     System.loadLibrary(args[0]);
24 
25     // Run the initialization routine. This will enable hidden API checks in
26     // the runtime, in case they are not enabled by default.
27     init();
28 
29     // Load the '-ex' APK and attach it to the boot class path.
30     appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
31 
32     // All test classes contain just methods named "foo" with different return types
33     // and access flags. Check that:
34     // (a) only the non-hidden ones are returned from getDeclaredMethods
35     //     (they have return types Number and Double), and
36     // (b) getDeclaredMethod picks virtual/non-synthetic methods over direct/synthetic
37     //     (the right one always has return type Number).
38     Class<?> covariantClass = Class.forName(JAVA_CLASS_NAME, true, BOOT_CLASS_LOADER);
39     checkMethodList(covariantClass, /* expectedLength= */ 1);
40     checkMethod(covariantClass);
41 
42     String[] classes = new String[] {
43       "VirtualMethods",
44       "DirectMethods",
45       "SyntheticMethods",
46       "NonSyntheticMethods"
47     };
48     for (String className : classes) {
49       Class<?> klass = Class.forName(className, true, BOOT_CLASS_LOADER);
50       checkMethodList(klass, /* expectedLength= */ 2);
51       checkMethod(klass);
52     }
53   }
54 
checkMethodList(Class<?> klass, int expectedLength)55   private static void checkMethodList(Class<?> klass, int expectedLength) {
56     String className = klass.getName();
57     Method[] methods = klass.getDeclaredMethods();
58     if (methods.length != expectedLength) {
59       throw new RuntimeException(className + ": expected " + expectedLength +
60           " declared method(s), got " + methods.length);
61     }
62     boolean hasNumberReturnType = false;
63     boolean hasDoubleReturnType = false;
64     for (Method method : methods) {
65       if (!METHOD_NAME.equals(method.getName())) {
66         throw new RuntimeException(className + ": expected declared method name: \"" + METHOD_NAME +
67             "\", got: \"" + method.getName() + "\"");
68       }
69       if (Number.class == method.getReturnType()) {
70         hasNumberReturnType = true;
71       } else if (Double.class == method.getReturnType()) {
72         hasDoubleReturnType = true;
73       }
74     }
75     if (methods.length >= 1 && !hasNumberReturnType) {
76       throw new RuntimeException(className + ": expected a method with return type \"Number\"");
77     }
78     if (methods.length >= 2 && !hasDoubleReturnType) {
79       throw new RuntimeException(className + ": expected a method with return type \"Double\"");
80     }
81   }
82 
checkMethod(Class<?> klass)83   private static void checkMethod(Class<?> klass) throws NoSuchMethodException {
84     String className = klass.getName();
85     Method method = klass.getDeclaredMethod(METHOD_NAME);
86     if (!METHOD_NAME.equals(method.getName())) {
87       throw new RuntimeException(className + ": expected declared method name: \"" + METHOD_NAME +
88           "\", got: \"" + method.getName() + "\"");
89     } else if (Number.class != method.getReturnType()) {
90       throw new RuntimeException(className + ": expected method return type: \"Number\", got \"" +
91           method.getReturnType().toString() + "\"");
92     }
93   }
94 
95   private static final String DEX_EXTRA = new File(System.getenv("DEX_LOCATION"),
96       "690-hiddenapi-same-name-methods-ex.jar").getAbsolutePath();
97 
98   private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
99 
100   private static final String JAVA_CLASS_NAME = "SpecificClass";
101   private static final String METHOD_NAME = "foo";
102 
103   // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
appendToBootClassLoader(String dexPath, boolean isCorePlatform)104   private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
init()105   private static native void init();
106 }
107