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