• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 android.signature.cts;
18 
19 import android.util.Log;
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.Executable;
22 import java.lang.reflect.Field;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.util.List;
26 
27 public class DexMemberChecker {
28     public static final String TAG = "DexMemberChecker";
29 
30     public interface Observer {
classAccessible(boolean accessible, DexMember member)31         void classAccessible(boolean accessible, DexMember member);
fieldAccessibleViaReflection(boolean accessible, DexField field)32         void fieldAccessibleViaReflection(boolean accessible, DexField field);
fieldAccessibleViaJni(boolean accessible, DexField field)33         void fieldAccessibleViaJni(boolean accessible, DexField field);
methodAccessibleViaReflection(boolean accessible, DexMethod method)34         void methodAccessibleViaReflection(boolean accessible, DexMethod method);
methodAccessibleViaJni(boolean accessible, DexMethod method)35         void methodAccessibleViaJni(boolean accessible, DexMethod method);
36     }
37 
init()38     public static void init() {
39         System.loadLibrary("cts_dexchecker");
40     }
41 
call_VMDebug_allowHiddenApiReflectionFrom(Class<?> klass)42     private static void call_VMDebug_allowHiddenApiReflectionFrom(Class<?> klass) throws Exception {
43       Method method = null;
44 
45       try {
46         Class<?> vmdebug = Class.forName("dalvik.system.VMDebug");
47         method = vmdebug.getDeclaredMethod("allowHiddenApiReflectionFrom", Class.class);
48       } catch (Exception ex) {
49         // Could not find the method. Report the problem as a RuntimeException.
50         throw new RuntimeException(ex);
51       }
52 
53       try {
54         method.invoke(null, klass);
55       } catch (InvocationTargetException ex) {
56         // Rethrow the original exception.
57         Throwable cause = ex.getCause();
58         // Please the compiler's 'throws' static analysis.
59         if (cause instanceof Exception) {
60           throw (Exception) cause;
61         } else {
62           throw (Error) cause;
63         }
64       }
65     }
66 
requestExemptionFromHiddenApiChecks()67     public static boolean requestExemptionFromHiddenApiChecks() throws Exception {
68       try {
69         call_VMDebug_allowHiddenApiReflectionFrom(DexMemberChecker.class);
70         return true;
71       } catch (SecurityException ex) {
72         return false;
73       }
74     }
75 
checkSingleMember(DexMember dexMember, DexMemberChecker.Observer observer)76     public static void checkSingleMember(DexMember dexMember, DexMemberChecker.Observer observer) {
77         checkSingleMember(dexMember, /* reflection= */ true, /* jni= */ true, observer);
78     }
79 
checkSingleMember(DexMember dexMember, boolean reflection, boolean jni, DexMemberChecker.Observer observer)80     public static void checkSingleMember(DexMember dexMember, boolean reflection, boolean jni,
81             DexMemberChecker.Observer observer) {
82         Class<?> klass = findClass(dexMember);
83         if (klass == null) {
84             // Class not found. Therefore its members are not visible.
85             observer.classAccessible(false, dexMember);
86             return;
87         }
88         observer.classAccessible(true, dexMember);
89 
90         if (dexMember instanceof DexField) {
91             DexField field = (DexField) dexMember;
92             if (reflection) {
93                 observer.fieldAccessibleViaReflection(
94                         hasMatchingField_Reflection(klass, field),
95                         field);
96             }
97             if (jni) {
98                 try {
99                     observer.fieldAccessibleViaJni(hasMatchingField_JNI(klass, field), field);
100                 } catch (ExceptionInInitializerError | UnsatisfiedLinkError
101                         | NoClassDefFoundError e) {
102                     if ((e instanceof NoClassDefFoundError)
103                             && !(e.getCause() instanceof ExceptionInInitializerError)
104                             && !(e.getCause() instanceof UnsatisfiedLinkError)) {
105                         throw e;
106                     }
107 
108                     // Could not initialize the class. Skip JNI test.
109                     Log.w(TAG, "JNI failed for " + dexMember.toString(), e);
110                 }
111             }
112         } else if (dexMember instanceof DexMethod) {
113             DexMethod method = (DexMethod) dexMember;
114             if (reflection) {
115                 observer.methodAccessibleViaReflection(hasMatchingMethod_Reflection(klass, method),
116                         method);
117             }
118             if (jni) {
119                 try {
120                     observer.methodAccessibleViaJni(hasMatchingMethod_JNI(klass, method), method);
121                 } catch (ExceptionInInitializerError | UnsatisfiedLinkError
122                         | NoClassDefFoundError e) {
123                     if ((e instanceof NoClassDefFoundError)
124                             && !(e.getCause() instanceof ExceptionInInitializerError)
125                             && !(e.getCause() instanceof UnsatisfiedLinkError)) {
126                         throw e;
127                     }
128 
129                     // Could not initialize the class. Skip JNI test.
130                     Log.w(TAG, "JNI failed for " + dexMember.toString(), e);
131                 }
132             }
133         } else {
134             throw new IllegalStateException("Unexpected type of dex member");
135         }
136     }
137 
typesMatch(Class<?>[] classes, List<String> typeNames)138     private static boolean typesMatch(Class<?>[] classes, List<String> typeNames) {
139         if (classes.length != typeNames.size()) {
140             return false;
141         }
142         for (int i = 0; i < classes.length; ++i) {
143             if (!classes[i].getTypeName().equals(typeNames.get(i))) {
144                 return false;
145             }
146         }
147         return true;
148     }
149 
findClass(DexMember dexMember)150     private static Class<?> findClass(DexMember dexMember) {
151         try {
152             // Try to find the class. Do not initialize it - we do not want to run
153             // static initializers.
154             return Class.forName(dexMember.getJavaClassName(), /* initialize */ false,
155                 DexMemberChecker.class.getClassLoader());
156         } catch (ClassNotFoundException ex) {
157             return null;
158         }
159     }
160 
hasMatchingField_Reflection(Class<?> klass, DexField dexField)161     private static boolean hasMatchingField_Reflection(Class<?> klass, DexField dexField) {
162         try {
163             klass.getDeclaredField(dexField.getName());
164             return true;
165         } catch (NoSuchFieldException ex) {
166             return false;
167         }
168     }
169 
hasMatchingField_JNI(Class<?> klass, DexField dexField)170     private static boolean hasMatchingField_JNI(Class<?> klass, DexField dexField) {
171         try {
172             Field ifield = getField_JNI(klass, dexField.getName(), dexField.getDexType());
173             if (ifield.getDeclaringClass() == klass) {
174               return true;
175             }
176         } catch (NoSuchFieldError e) {
177             // Not found.
178         }
179 
180         try {
181             Field sfield = getStaticField_JNI(klass, dexField.getName(), dexField.getDexType());
182             if (sfield.getDeclaringClass() == klass) {
183               return true;
184             }
185         } catch (NoSuchFieldError e) {
186             // Not found.
187         }
188 
189         return false;
190     }
191 
hasMatchingMethod_Reflection(Class<?> klass, DexMethod dexMethod)192     private static boolean hasMatchingMethod_Reflection(Class<?> klass, DexMethod dexMethod) {
193         List<String> methodParams = dexMethod.getJavaParameterTypes();
194 
195         if (dexMethod.isConstructor()) {
196             for (Constructor<?> constructor : klass.getDeclaredConstructors()) {
197                 if (typesMatch(constructor.getParameterTypes(), methodParams)) {
198                     return true;
199                 }
200             }
201         } else {
202             String methodReturnType = dexMethod.getJavaType();
203             for (Method method : klass.getDeclaredMethods()) {
204                 if (method.getName().equals(dexMethod.getName())
205                         && method.getReturnType().getTypeName().equals(methodReturnType)
206                         && typesMatch(method.getParameterTypes(), methodParams)) {
207                     return true;
208                 }
209             }
210         }
211         return false;
212     }
213 
hasMatchingMethod_JNI(Class<?> klass, DexMethod dexMethod)214     private static boolean hasMatchingMethod_JNI(Class<?> klass, DexMethod dexMethod) {
215         try {
216             Executable imethod = getMethod_JNI(
217                 klass, dexMethod.getName(), dexMethod.getDexSignature());
218             if (imethod.getDeclaringClass() == klass) {
219                 return true;
220             }
221         } catch (NoSuchMethodError e) {
222             // Not found.
223         }
224 
225         if (!dexMethod.isConstructor()) {
226             try {
227                 Executable smethod =
228                     getStaticMethod_JNI(klass, dexMethod.getName(), dexMethod.getDexSignature());
229                 if (smethod.getDeclaringClass() == klass) {
230                     return true;
231                 }
232             } catch (NoSuchMethodError e) {
233                 // Not found.
234             }
235         }
236 
237         return false;
238     }
239 
getField_JNI(Class<?> klass, String name, String type)240     private static native Field getField_JNI(Class<?> klass, String name, String type);
getStaticField_JNI(Class<?> klass, String name, String type)241     private static native Field getStaticField_JNI(Class<?> klass, String name, String type);
getMethod_JNI(Class<?> klass, String name, String signature)242     private static native Executable getMethod_JNI(Class<?> klass, String name, String signature);
getStaticMethod_JNI(Class<?> klass, String name, String signature)243     private static native Executable getStaticMethod_JNI(Class<?> klass, String name,
244             String signature);
245 
246 }
247