1 /*
2  * Copyright (C) 2016 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 art;
18 
19 import java.lang.reflect.Field;
20 import java.util.Base64;
21 import java.nio.ByteBuffer;
22 
23 import dalvik.system.ClassExt;
24 import dalvik.system.InMemoryDexClassLoader;
25 
26 public class Test981 {
27 
28   static class Transform {
sayHi()29     public void sayHi() {
30       System.out.println("hello");
31     }
32   }
33 
34   static class Transform2 {
sayHi()35     public void sayHi() {
36       System.out.println("hello2");
37     }
38   }
39 
40   /**
41    * base64 encoded class/dex file for
42    * static class Transform {
43    *   public void sayHi() {
44    *    System.out.println("Goodbye");
45    *   }
46    * }
47    */
48   private static final byte[] DEX_BYTES_1 = Base64.getDecoder().decode(
49     "ZGV4CjAzNQB+giqQAAAAAAAAAAAAAAAAAAAAAAAAAAC4AwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAU" +
50     "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB0AgAARAEAAEQB" +
51     "AABMAQAAVQEAAG4BAAB9AQAAoQEAAMEBAADYAQAA7AEAAAACAAAUAgAAIgIAAC0CAAAwAgAANAIA" +
52     "AEECAABHAgAATAIAAFUCAABcAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" +
53     "DAAAAAgAAAAAAAAADQAAAAgAAABkAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
54     "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA5AIAALgCAAAAAAAABjxpbml0PgAHR29vZGJ5ZQAX" +
55     "TGFydC9UZXN0OTgxJFRyYW5zZm9ybTsADUxhcnQvVGVzdDk4MTsAIkxkYWx2aWsvYW5ub3RhdGlv" +
56     "bi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEv" +
57     "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" +
58     "TGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTgxLmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vz" +
59     "c0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAQAAAAYAAAAFAAcOAAcA" +
60     "Bw4BCA8AAAAAAQABAAEAAABsAgAABAAAAHAQAwAAAA4AAwABAAIAAABxAgAACQAAAGIAAAAbAQEA" +
61     "AABuIAIAEAAOAAAAAAABAQCAgAT8BAEBlAUAAAICARMYAQIDAg4ECA8XCwACAAAAyAIAAM4CAADY" +
62     "AgAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAJAAAAwAAAAAMA" +
63     "AAACAAAA5AAAAAQAAAABAAAA/AAAAAUAAAAEAAAABAEAAAYAAAABAAAAJAEAAAIgAAAUAAAARAEA" +
64     "AAEQAAABAAAAZAIAAAMgAAACAAAAbAIAAAEgAAACAAAAfAIAAAAgAAABAAAAuAIAAAQgAAACAAAA" +
65     "yAIAAAMQAAABAAAA2AIAAAYgAAABAAAA5AIAAAAQAAABAAAA9AIAAA==");
66 
67   /**
68    * base64 encoded class/dex file for
69    * static class Transform2 {
70    *   public void sayHi() {
71    *    System.out.println("Goodbye2");
72    *   }
73    * }
74    */
75   private static final byte[] DEX_BYTES_2 = Base64.getDecoder().decode(
76     "ZGV4CjAzNQAhg+RVAAAAAAAAAAAAAAAAAAAAAAAAAAC8AwAAcAAAAHhWNBIAAAAAAAAAAPgCAAAU" +
77     "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB4AgAARAEAAEQB" +
78     "AABMAQAAVgEAAHABAAB/AQAAowEAAMMBAADaAQAA7gEAAAICAAAWAgAAJAIAADACAAAzAgAANwIA" +
79     "AEQCAABKAgAATwIAAFgCAABfAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" +
80     "DAAAAAgAAAAAAAAADQAAAAgAAABoAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
81     "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA6AIAALwCAAAAAAAABjxpbml0PgAIR29vZGJ5ZTIA" +
82     "GExhcnQvVGVzdDk4MSRUcmFuc2Zvcm0yOwANTGFydC9UZXN0OTgxOwAiTGRhbHZpay9hbm5vdGF0" +
83     "aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2" +
84     "YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7" +
85     "ABJMamF2YS9sYW5nL1N5c3RlbTsADFRlc3Q5ODEuamF2YQAKVHJhbnNmb3JtMgABVgACVkwAC2Fj" +
86     "Y2Vzc0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAAEAAAAGAAAACgAH" +
87     "DgAMAAcOAQgPAAAAAAEAAQABAAAAcAIAAAQAAABwEAMAAAAOAAMAAQACAAAAdQIAAAkAAABiAAAA" +
88     "GwEBAAAAbiACABAADgAAAAAAAQEAgIAEgAUBAZgFAAACAgETGAECAwIOBAgPFwsAAgAAAMwCAADS" +
89     "AgAA3AIAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAFAAAAHAAAAACAAAACQAAAMAA" +
90     "AAADAAAAAgAAAOQAAAAEAAAAAQAAAPwAAAAFAAAABAAAAAQBAAAGAAAAAQAAACQBAAACIAAAFAAA" +
91     "AEQBAAABEAAAAQAAAGgCAAADIAAAAgAAAHACAAABIAAAAgAAAIACAAAAIAAAAQAAALwCAAAEIAAA" +
92     "AgAAAMwCAAADEAAAAQAAANwCAAAGIAAAAQAAAOgCAAAAEAAAAQAAAPgCAAA=");
93 
94   /**
95    * base64 encoded class/dex file for
96    * class Transform3 {
97    *   public void sayHi() {
98    *    System.out.println("hello3");
99    *   }
100    * }
101    */
102   private static final byte[] DEX_BYTES_3_INITIAL = Base64.getDecoder().decode(
103     "ZGV4CjAzNQC2W2fBsAeLNAwWYlG8FVigzfsV7nBWITzQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
104     "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
105     "AABqAQAAeAEAAI8BAACjAQAAtwEAAMsBAADcAQAA3wEAAOMBAAD3AQAA/wEAAAQCAAANAgAAAQAA" +
106     "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
107     "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAAB8CAAAA" +
108     "AAAAAQABAAEAAAAUAgAABAAAAHAQAwAAAA4AAwABAAIAAAAZAgAACQAAAGIAAAAbAQoAAABuIAIA" +
109     "EAAOAAAAAQAAAAMABjxpbml0PgAMTFRyYW5zZm9ybTM7ABVMamF2YS9pby9QcmludFN0cmVhbTsA" +
110     "EkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3Rl" +
111     "bTsAD1RyYW5zZm9ybTMuamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2stNC4zMAAGaGVsbG8zAANv" +
112     "dXQAB3ByaW50bG4ABXNheUhpAAIABw4ABAAHDocAAAABAQCAgASgAgEBuAIAAAANAAAAAAAAAAEA" +
113     "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
114     "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
115     "AyAAAAIAAAAUAgAAACAAAAEAAAAfAgAAABAAAAEAAAAwAgAA");
116 
117   /**
118    * base64 encoded class/dex file for
119    * class Transform3 {
120    *   public void sayHi() {
121    *    System.out.println("Goodbye3");
122    *   }
123    * }
124    */
125   private static final byte[] DEX_BYTES_3_FINAL = Base64.getDecoder().decode(
126     "ZGV4CjAzNQBAXE5GthgMydaFBuinf+ZBfXcBYIw2UlXQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
127     "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
128     "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" +
129     "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
130     "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" +
131     "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" +
132     "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTMADExUcmFuc2Zvcm0zOwAVTGphdmEvaW8vUHJp" +
133     "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" +
134     "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0zLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMzAA" +
135     "A291dAAHcHJpbnRsbgAFc2F5SGkAAgAHDgAEAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" +
136     "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
137     "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
138     "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA");
139 
run()140   public static void run() throws Exception {
141     Redefinition.setTestConfiguration(Redefinition.Config.COMMON_RETRANSFORM);
142     doTest();
143   }
144 
assertSame(Object a, Object b)145   private static void assertSame(Object a, Object b) throws Exception {
146     if (a != b) {
147       throw new AssertionError("'" + (a != null ? a.toString() : "null") + "' is not the same as " +
148                                "'" + (b != null ? b.toString() : "null") + "'");
149     }
150   }
151 
getObjectField(Object o, String name)152   private static Object getObjectField(Object o, String name) throws Exception {
153     return getObjectField(o, o.getClass(), name);
154   }
155 
getObjectField(Object o, Class<?> type, String name)156   private static Object getObjectField(Object o, Class<?> type, String name) throws Exception {
157     Field f = type.getDeclaredField(name);
158     f.setAccessible(true);
159     return f.get(o);
160   }
161 
getOriginalDexFile(Class<?> k)162   private static Object getOriginalDexFile(Class<?> k) throws Exception {
163     ClassExt ext_data_object = (ClassExt) getObjectField(k, "extData");
164     if (ext_data_object == null) {
165       return null;
166     }
167 
168     return getObjectField(ext_data_object, "originalDexFile");
169   }
170 
doTest()171   public static void doTest() throws Exception {
172     // Make sure both of these are loaded prior to transformations being added so they have the same
173     // original dex files.
174     Transform t1 = new Transform();
175     Transform2 t2 = new Transform2();
176 
177     assertSame(null, getOriginalDexFile(t1.getClass()));
178     assertSame(null, getOriginalDexFile(t2.getClass()));
179     assertSame(null, getOriginalDexFile(Test981.class));
180 
181     Redefinition.addCommonTransformationResult("art/Test981$Transform", new byte[0], DEX_BYTES_1);
182     Redefinition.addCommonTransformationResult("art/Test981$Transform2", new byte[0], DEX_BYTES_2);
183     Redefinition.enableCommonRetransformation(true);
184     Redefinition.doCommonClassRetransformation(Transform.class, Transform2.class);
185 
186     assertSame(getOriginalDexFile(t1.getClass()), getOriginalDexFile(t2.getClass()));
187     assertSame(null, getOriginalDexFile(Test981.class));
188     // Make sure that the original dex file is a DexCache object.
189     assertSame(getOriginalDexFile(t1.getClass()).getClass(), Class.forName("java.lang.DexCache"));
190 
191     // Check that we end up with a byte[] if we do a direct RedefineClasses
192     Redefinition.enableCommonRetransformation(false);
193     Redefinition.doCommonClassRedefinition(Transform.class, new byte[0], DEX_BYTES_1);
194     assertSame((new byte[0]).getClass(), getOriginalDexFile(t1.getClass()).getClass());
195 
196     // Check we don't have anything if we don't have any originalDexFile if the onload
197     // transformation doesn't do anything.
198     Redefinition.enableCommonRetransformation(true);
199     Class<?> transform3Class = new InMemoryDexClassLoader(
200         ByteBuffer.wrap(DEX_BYTES_3_INITIAL), Test981.class.getClassLoader()).loadClass("Transform3");
201     assertSame(null, getOriginalDexFile(transform3Class));
202 
203     // Check that we end up with a java.lang.Long pointer if we do an 'on-load' redefinition.
204     Redefinition.addCommonTransformationResult("Transform3", new byte[0], DEX_BYTES_3_FINAL);
205     Redefinition.enableCommonRetransformation(true);
206     Class<?> transform3ClassTransformed = new InMemoryDexClassLoader(
207         ByteBuffer.wrap(DEX_BYTES_3_INITIAL), Test981.class.getClassLoader()).loadClass("Transform3");
208     assertSame(Long.class, getOriginalDexFile(transform3ClassTransformed).getClass());
209   }
210 }
211