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