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 com.android.dx.mockito.inline; 18 19 import android.os.Build; 20 import android.os.Debug; 21 22 import java.io.IOException; 23 import java.lang.reflect.InvocationTargetException; 24 import java.security.ProtectionDomain; 25 import java.util.ArrayList; 26 27 import dalvik.system.BaseDexClassLoader; 28 29 /** 30 * Interface to the native jvmti agent in agent.cc 31 */ 32 class StaticJvmtiAgent { 33 private static final String AGENT_LIB_NAME = "libstaticjvmtiagent.so"; 34 35 private static final Object lock = new Object(); 36 37 /** 38 * Registered byte code transformers 39 */ 40 private final ArrayList<StaticClassTransformer> transformers = new ArrayList<>(); 41 42 /** 43 * Enable jvmti and load agent. 44 * <p><b>If there are more than agent transforming classes the other agent might remove 45 * transformations added by this agent.</b> 46 * 47 * @throws IOException If jvmti could not be enabled or agent could not be loaded 48 */ 49 StaticJvmtiAgent() throws IOException { 50 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { 51 throw new IOException("Requires API level " + Build.VERSION_CODES.P + ". API level is " 52 + Build.VERSION.SDK_INT); 53 } 54 55 ClassLoader cl = StaticJvmtiAgent.class.getClassLoader(); 56 if (!(cl instanceof BaseDexClassLoader)) { 57 throw new IOException("Could not load jvmti plugin as StaticJvmtiAgent class was not loaded " 58 + "by a BaseDexClassLoader"); 59 } 60 61 Debug.attachJvmtiAgent(AGENT_LIB_NAME, null, cl); 62 nativeRegisterTransformerHook(); 63 } 64 65 private native void nativeRegisterTransformerHook(); 66 67 private native void nativeUnregisterTransformerHook(); 68 69 @Override 70 protected void finalize() throws Throwable { 71 nativeUnregisterTransformerHook(); 72 } 73 74 /** 75 * Ask the agent to trigger transformation of some classes. This will extract the byte code of 76 * the classes and the call back the {@link #addTransformer(StaticClassTransformer) 77 * transformers} for each individual class. 78 * 79 * @param classes The classes to transform 80 * @throws UnmodifiableClassException If one of the classes can not be transformed 81 */ 82 void requestTransformClasses(Class<?>[] classes) throws UnmodifiableClassException { 83 synchronized (lock) { 84 try { 85 nativeRetransformClasses(classes); 86 } catch (RuntimeException e) { 87 throw new UnmodifiableClassException(e); 88 } 89 } 90 } 91 92 // called by JNI 93 @SuppressWarnings("unused") 94 public boolean shouldTransform(Class<?> classBeingRedefined) { 95 for (StaticClassTransformer transformer : transformers) { 96 if (transformer.shouldTransform(classBeingRedefined)) { 97 return true; 98 } 99 } 100 101 return false; 102 } 103 104 /** 105 * Register a transformer. These are called for each class when a transformation was triggered 106 * via {@link #requestTransformClasses(Class[])}. 107 * 108 * @param transformer the transformer to add. 109 */ 110 void addTransformer(StaticClassTransformer transformer) { 111 transformers.add(transformer); 112 } 113 114 // called by JNI 115 @SuppressWarnings("unused") 116 public byte[] runTransformers(ClassLoader loader, String className, 117 Class<?> classBeingRedefined, ProtectionDomain protectionDomain, 118 byte[] classfileBuffer) throws IllegalClassFormatException { 119 byte[] transformedByteCode = classfileBuffer; 120 for (StaticClassTransformer transformer : transformers) { 121 transformedByteCode = transformer.transform(classBeingRedefined, transformedByteCode); 122 } 123 124 return transformedByteCode; 125 } 126 127 private native void nativeRetransformClasses(Class<?>[] classes); 128 } 129