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.api; 18 19 import android.os.Debug; 20 import android.util.Log; 21 import android.signature.cts.ClassProvider; 22 import android.signature.cts.DexField; 23 import android.signature.cts.DexMethod; 24 import android.signature.cts.DexMember; 25 import dalvik.system.BaseDexClassLoader; 26 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.FileOutputStream; 30 import java.io.InputStream; 31 import java.io.OutputStream; 32 import java.util.Arrays; 33 import java.util.Objects; 34 import java.util.stream.Stream; 35 36 @SuppressWarnings("deprecation") 37 public class BootClassPathClassesProvider extends ClassProvider { 38 private static final String TAG = "BootClassPathClassesProvider"; 39 40 private static boolean sJvmtiAttached = false; 41 42 @Override getAllClasses()43 public Stream<Class<?>> getAllClasses() { 44 maybeAttachJvmtiAgent(); 45 return (Stream<Class<?>>) 46 Arrays.stream(getClassloaderDescriptors(Object.class.getClassLoader())) 47 .map(descriptor -> { 48 String classname = descriptor.replace('/', '.'); 49 // omit L and ; at the front and at the end 50 return classname.substring(1, classname.length() - 1); 51 }) 52 .map(classname -> { 53 try { 54 return getClass(classname); 55 } catch (ClassNotFoundException e) { 56 // It could be that a class failed to verify. 57 // No process will be able to load it, so it's ok to silently ignore. 58 return null; 59 } catch (NoClassDefFoundError e) { 60 Log.w(TAG, "Could not load class " + classname, e); 61 return null; 62 } 63 }) 64 .filter(Objects::nonNull); 65 } 66 67 @Override getAllMembers(Class<?> klass)68 public Stream<DexMember> getAllMembers(Class<?> klass) { 69 maybeAttachJvmtiAgent(); 70 71 String[][] field_infos = getClassMemberNamesAndTypes(klass, /* fields */ true); 72 String[][] method_infos = getClassMemberNamesAndTypes(klass, /* fields */ false); 73 if (field_infos.length != 2 || field_infos[0].length != field_infos[1].length || 74 method_infos.length != 2 || method_infos[0].length != method_infos[1].length) { 75 throw new RuntimeException("Invalid result from getClassMemberNamesAndTypes"); 76 } 77 78 String klass_desc = "L" + klass.getName().replace('.', '/') + ";"; 79 DexMember[] members = new DexMember[field_infos[0].length + method_infos[0].length]; 80 for (int i = 0; i < field_infos[0].length; i++) { 81 members[i] = new DexField(klass_desc, field_infos[0][i], field_infos[1][i], null); 82 } 83 for (int i = 0; i < method_infos[0].length; i++) { 84 members[i + field_infos[0].length] = 85 new DexMethod(klass_desc, method_infos[0][i], method_infos[1][i], null); 86 } 87 return Arrays.stream(members); 88 } 89 maybeAttachJvmtiAgent()90 private static void maybeAttachJvmtiAgent() { 91 if (!sJvmtiAttached) { 92 try { 93 Debug.attachJvmtiAgent(copyAgentToFile("classdescriptors").getAbsolutePath(), null, 94 BootClassPathClassesProvider.class.getClassLoader()); 95 sJvmtiAttached = true; 96 initialize(); 97 } catch (Exception e) { 98 throw new RuntimeException("Error while attaching JVMTI agent", e); 99 } 100 } 101 } 102 copyAgentToFile(String lib)103 private static File copyAgentToFile(String lib) throws Exception { 104 ClassLoader cl = BootClassPathClassesProvider.class.getClassLoader(); 105 106 File copiedAgent = File.createTempFile("agent", ".so"); 107 try (InputStream is = new FileInputStream( 108 ((BaseDexClassLoader) cl).findLibrary(lib))) { 109 try (OutputStream os = new FileOutputStream(copiedAgent)) { 110 byte[] buffer = new byte[64 * 1024]; 111 112 while (true) { 113 int numRead = is.read(buffer); 114 if (numRead == -1) { 115 break; 116 } 117 os.write(buffer, 0, numRead); 118 } 119 } 120 } 121 return copiedAgent; 122 } 123 initialize()124 private static native void initialize(); 125 getClassloaderDescriptors(ClassLoader loader)126 private static native String[] getClassloaderDescriptors(ClassLoader loader); getClassMemberNamesAndTypes(Class<?> klass, boolean getFields)127 private static native String[][] getClassMemberNamesAndTypes(Class<?> klass, boolean getFields); 128 } 129