Home | History | Annotate | Download | only in api
      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
     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
     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]);
     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]);
     75         }
     76         return Arrays.stream(members);
     77     }
     78 
     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 
     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 
    113     private static native void initialize();
    114 
    115     private static native String[] getClassloaderDescriptors(ClassLoader loader);
    116     private static native String[][] getClassMemberNamesAndTypes(Class<?> klass, boolean getFields);
    117 }
    118