xref: /aosp_15_r20/art/test/1950-unprepared-transform/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2016 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker import art.Redefinition;
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.*;
20*795d594fSAndroid Build Coastguard Worker import java.util.Base64;
21*795d594fSAndroid Build Coastguard Worker import java.util.concurrent.CountDownLatch;
22*795d594fSAndroid Build Coastguard Worker import java.util.function.Consumer;
23*795d594fSAndroid Build Coastguard Worker 
24*795d594fSAndroid Build Coastguard Worker class Main {
25*795d594fSAndroid Build Coastguard Worker   public static String TEST_NAME = "1950-unprepared-transform";
26*795d594fSAndroid Build Coastguard Worker 
27*795d594fSAndroid Build Coastguard Worker   // Base 64 encoding of the following class:
28*795d594fSAndroid Build Coastguard Worker   //
29*795d594fSAndroid Build Coastguard Worker   // public class Transform {
30*795d594fSAndroid Build Coastguard Worker   //   public String toString() {
31*795d594fSAndroid Build Coastguard Worker   //     return "Transformed object!";
32*795d594fSAndroid Build Coastguard Worker   //   }
33*795d594fSAndroid Build Coastguard Worker   // }
34*795d594fSAndroid Build Coastguard Worker   public static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
35*795d594fSAndroid Build Coastguard Worker     "yv66vgAAADQAEQoABAANCAAOBwAPBwAQAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1i" +
36*795d594fSAndroid Build Coastguard Worker     "ZXJUYWJsZQEACHRvU3RyaW5nAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAO" +
37*795d594fSAndroid Build Coastguard Worker     "VHJhbnNmb3JtLmphdmEMAAUABgEAE1RyYW5zZm9ybWVkIG9iamVjdCEBAAlUcmFuc2Zvcm0BABBq" +
38*795d594fSAndroid Build Coastguard Worker     "YXZhL2xhbmcvT2JqZWN0ACEAAwAEAAAAAAACAAEABQAGAAEABwAAAB0AAQABAAAABSq3AAGxAAAA" +
39*795d594fSAndroid Build Coastguard Worker     "AQAIAAAABgABAAAAEgABAAkACgABAAcAAAAbAAEAAQAAAAMSArAAAAABAAgAAAAGAAEAAAAUAAEA" +
40*795d594fSAndroid Build Coastguard Worker     "CwAAAAIADA==");
41*795d594fSAndroid Build Coastguard Worker 
42*795d594fSAndroid Build Coastguard Worker   public static final byte[] DEX_BYTES = Base64.getDecoder().decode(
43*795d594fSAndroid Build Coastguard Worker     "ZGV4CjAzOACaXU/P8oJOECPrdN1Cu9/ob2cUb2vOKxqYAgAAcAAAAHhWNBIAAAAAAAAAABACAAAK" +
44*795d594fSAndroid Build Coastguard Worker     "AAAAcAAAAAQAAACYAAAAAgAAAKgAAAAAAAAAAAAAAAMAAADAAAAAAQAAANgAAACgAQAA+AAAADAB" +
45*795d594fSAndroid Build Coastguard Worker     "AAA4AQAAOwEAAEgBAABcAQAAcAEAAIABAACVAQAAmAEAAKIBAAACAAAAAwAAAAQAAAAHAAAAAQAA" +
46*795d594fSAndroid Build Coastguard Worker     "AAIAAAAAAAAABwAAAAMAAAAAAAAAAAABAAAAAAAAAAAACAAAAAEAAQAAAAAAAAAAAAEAAAABAAAA" +
47*795d594fSAndroid Build Coastguard Worker     "AAAAAAUAAAAAAAAAAAIAAAAAAAACAAEAAAAAACwBAAADAAAAGgAGABEAAAABAAEAAQAAACgBAAAE" +
48*795d594fSAndroid Build Coastguard Worker     "AAAAcBACAAAADgASAA4AFAAOAAY8aW5pdD4AAUwAC0xUcmFuc2Zvcm07ABJMamF2YS9sYW5nL09i" +
49*795d594fSAndroid Build Coastguard Worker     "amVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAOVHJhbnNmb3JtLmphdmEAE1RyYW5zZm9ybWVkIG9i" +
50*795d594fSAndroid Build Coastguard Worker     "amVjdCEAAVYACHRvU3RyaW5nAFx+fkQ4eyJtaW4tYXBpIjoyNywic2hhLTEiOiI3YTdjNDlhY2Nj" +
51*795d594fSAndroid Build Coastguard Worker     "NTkzNTIyNzY4MTY3MThhNGM3YWU1MmY5NjgzZjk5IiwidmVyc2lvbiI6InYxLjIuNC1kZXYifQAA" +
52*795d594fSAndroid Build Coastguard Worker     "AAEBAIGABJACAQH4AQAACwAAAAAAAAABAAAAAAAAAAEAAAAKAAAAcAAAAAIAAAAEAAAAmAAAAAMA" +
53*795d594fSAndroid Build Coastguard Worker     "AAACAAAAqAAAAAUAAAADAAAAwAAAAAYAAAABAAAA2AAAAAEgAAACAAAA+AAAAAMgAAACAAAAKAEA" +
54*795d594fSAndroid Build Coastguard Worker     "AAIgAAAKAAAAMAEAAAAgAAABAAAAAAIAAAAQAAABAAAAEAIAAA==");
55*795d594fSAndroid Build Coastguard Worker 
setupClassLoadHook(Thread target)56*795d594fSAndroid Build Coastguard Worker   public static native void setupClassLoadHook(Thread target);
clearClassLoadHook(Thread target)57*795d594fSAndroid Build Coastguard Worker   public static native void clearClassLoadHook(Thread target);
58*795d594fSAndroid Build Coastguard Worker   private static Consumer<Class<?>> doRedefine = null;
59*795d594fSAndroid Build Coastguard Worker 
doClassLoad(Class<?> c)60*795d594fSAndroid Build Coastguard Worker   public static void doClassLoad(Class<?> c) {
61*795d594fSAndroid Build Coastguard Worker     try {
62*795d594fSAndroid Build Coastguard Worker       if (c.getName().equals("Transform")) {
63*795d594fSAndroid Build Coastguard Worker         Redefinition.addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES);
64*795d594fSAndroid Build Coastguard Worker         doRedefine.accept(c);
65*795d594fSAndroid Build Coastguard Worker         System.out.println("retransformClasses on an unprepared class succeeded");
66*795d594fSAndroid Build Coastguard Worker       }
67*795d594fSAndroid Build Coastguard Worker     } catch (Throwable e) {
68*795d594fSAndroid Build Coastguard Worker       System.out.println("Trying to redefine: " + c + ". " +
69*795d594fSAndroid Build Coastguard Worker           "Caught error " + e.getClass() + ": " + e.getMessage());
70*795d594fSAndroid Build Coastguard Worker     }
71*795d594fSAndroid Build Coastguard Worker   }
72*795d594fSAndroid Build Coastguard Worker 
getClassLoaderFor(String location)73*795d594fSAndroid Build Coastguard Worker   public static ClassLoader getClassLoaderFor(String location) throws Exception {
74*795d594fSAndroid Build Coastguard Worker     try {
75*795d594fSAndroid Build Coastguard Worker       Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
76*795d594fSAndroid Build Coastguard Worker       Constructor<?> ctor = class_loader_class.getConstructor(String.class, ClassLoader.class);
77*795d594fSAndroid Build Coastguard Worker       /* on Dalvik, this is a DexFile; otherwise, it's null */
78*795d594fSAndroid Build Coastguard Worker       return (ClassLoader)ctor.newInstance(location + "/" + TEST_NAME + "-ex.jar",
79*795d594fSAndroid Build Coastguard Worker                                            Main.class.getClassLoader());
80*795d594fSAndroid Build Coastguard Worker     } catch (ClassNotFoundException e) {
81*795d594fSAndroid Build Coastguard Worker       // Running on RI. Use URLClassLoader.
82*795d594fSAndroid Build Coastguard Worker       return new java.net.URLClassLoader(
83*795d594fSAndroid Build Coastguard Worker           new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") });
84*795d594fSAndroid Build Coastguard Worker     }
85*795d594fSAndroid Build Coastguard Worker   }
86*795d594fSAndroid Build Coastguard Worker 
testCurrentThread()87*795d594fSAndroid Build Coastguard Worker   public static void testCurrentThread() throws Throwable {
88*795d594fSAndroid Build Coastguard Worker     System.out.println("Redefine in ClassLoad on current thread.");
89*795d594fSAndroid Build Coastguard Worker     doRedefine = (c) -> { Redefinition.doCommonClassRetransformation(c); };
90*795d594fSAndroid Build Coastguard Worker     ClassLoader new_loader = getClassLoaderFor(System.getenv("DEX_LOCATION"));
91*795d594fSAndroid Build Coastguard Worker     Class<?> klass = (Class<?>)new_loader.loadClass("Transform");
92*795d594fSAndroid Build Coastguard Worker     if (klass == null) {
93*795d594fSAndroid Build Coastguard Worker       throw new AssertionError("loadClass failed");
94*795d594fSAndroid Build Coastguard Worker     }
95*795d594fSAndroid Build Coastguard Worker     Object o = klass.newInstance();
96*795d594fSAndroid Build Coastguard Worker     System.out.println("Object out is: " + o);
97*795d594fSAndroid Build Coastguard Worker   }
98*795d594fSAndroid Build Coastguard Worker 
testRemoteThread()99*795d594fSAndroid Build Coastguard Worker   public static void testRemoteThread() throws Throwable {
100*795d594fSAndroid Build Coastguard Worker     System.out.println("Redefine during ClassLoad on another thread.");
101*795d594fSAndroid Build Coastguard Worker     final Class[] loaded = new Class[] { null, };
102*795d594fSAndroid Build Coastguard Worker     final CountDownLatch gotClass = new CountDownLatch(1);
103*795d594fSAndroid Build Coastguard Worker     final CountDownLatch wokeUp = new CountDownLatch(1);
104*795d594fSAndroid Build Coastguard Worker     Thread redef_thread = new Thread(() -> {
105*795d594fSAndroid Build Coastguard Worker       try {
106*795d594fSAndroid Build Coastguard Worker         gotClass.await();
107*795d594fSAndroid Build Coastguard Worker         wokeUp.countDown();
108*795d594fSAndroid Build Coastguard Worker         // This will wait until the otehr thread returns so we need to wake up the other thread
109*795d594fSAndroid Build Coastguard Worker         // first.
110*795d594fSAndroid Build Coastguard Worker         Redefinition.doCommonClassRetransformation(loaded[0]);
111*795d594fSAndroid Build Coastguard Worker       } catch (Exception e) {
112*795d594fSAndroid Build Coastguard Worker         throw new Error("Failed to do redef!", e);
113*795d594fSAndroid Build Coastguard Worker       }
114*795d594fSAndroid Build Coastguard Worker     });
115*795d594fSAndroid Build Coastguard Worker     redef_thread.start();
116*795d594fSAndroid Build Coastguard Worker     doRedefine = (c) -> {
117*795d594fSAndroid Build Coastguard Worker       try {
118*795d594fSAndroid Build Coastguard Worker         loaded[0] = c;
119*795d594fSAndroid Build Coastguard Worker         gotClass.countDown();
120*795d594fSAndroid Build Coastguard Worker         wokeUp.await();
121*795d594fSAndroid Build Coastguard Worker         // Let the other thread do some stuff.
122*795d594fSAndroid Build Coastguard Worker         Thread.sleep(5000);
123*795d594fSAndroid Build Coastguard Worker       } catch (Exception e) {
124*795d594fSAndroid Build Coastguard Worker         throw new Error("Failed to do redef!", e);
125*795d594fSAndroid Build Coastguard Worker       }
126*795d594fSAndroid Build Coastguard Worker     };
127*795d594fSAndroid Build Coastguard Worker     ClassLoader new_loader = getClassLoaderFor(System.getenv("DEX_LOCATION"));
128*795d594fSAndroid Build Coastguard Worker     Class<?> klass = (Class<?>)new_loader.loadClass("Transform");
129*795d594fSAndroid Build Coastguard Worker     if (klass == null) {
130*795d594fSAndroid Build Coastguard Worker       throw new AssertionError("loadClass failed");
131*795d594fSAndroid Build Coastguard Worker     }
132*795d594fSAndroid Build Coastguard Worker     Object o = klass.newInstance();
133*795d594fSAndroid Build Coastguard Worker     System.out.println("Object out is: " + o);
134*795d594fSAndroid Build Coastguard Worker     redef_thread.join();
135*795d594fSAndroid Build Coastguard Worker     System.out.println("Redefinition thread finished.");
136*795d594fSAndroid Build Coastguard Worker   }
137*795d594fSAndroid Build Coastguard Worker 
main(String[] args)138*795d594fSAndroid Build Coastguard Worker   public static void main(String[] args) {
139*795d594fSAndroid Build Coastguard Worker     // make sure we can do the transform.
140*795d594fSAndroid Build Coastguard Worker     Redefinition.setTestConfiguration(Redefinition.Config.COMMON_RETRANSFORM);
141*795d594fSAndroid Build Coastguard Worker     Redefinition.setPopRetransformations(false);
142*795d594fSAndroid Build Coastguard Worker     Redefinition.enableCommonRetransformation(true);
143*795d594fSAndroid Build Coastguard Worker     setupClassLoadHook(Thread.currentThread());
144*795d594fSAndroid Build Coastguard Worker     try {
145*795d594fSAndroid Build Coastguard Worker       testCurrentThread();
146*795d594fSAndroid Build Coastguard Worker       testRemoteThread();
147*795d594fSAndroid Build Coastguard Worker     } catch (Throwable e) {
148*795d594fSAndroid Build Coastguard Worker       System.out.println(e.toString());
149*795d594fSAndroid Build Coastguard Worker       e.printStackTrace(System.out);
150*795d594fSAndroid Build Coastguard Worker     }
151*795d594fSAndroid Build Coastguard Worker     clearClassLoadHook(Thread.currentThread());
152*795d594fSAndroid Build Coastguard Worker   }
153*795d594fSAndroid Build Coastguard Worker }
154