1 /*
2  * Copyright (C) 2019 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.Executable;
20 import java.lang.reflect.Field;
21 import java.util.Base64;
22 
23 public class Test1984 {
notifyFieldModify( Executable method, long location, Class<?> f_klass, Object target, Field f, Object value)24   public static void notifyFieldModify(
25       Executable method, long location, Class<?> f_klass, Object target, Field f, Object value) {
26     System.out.println("method: " + method + "\tMODIFY: " + f + "\tSet to: " + value);
27   }
28 
notifyFieldAccess( Executable method, long location, Class<?> f_klass, Object target, Field f)29   public static void notifyFieldAccess(
30       Executable method, long location, Class<?> f_klass, Object target, Field f) {
31     System.out.println("method: " + method + "\tACCESS: " + f);
32   }
33 
34   public static class Transform {
35     public static int count_down = 2;
36     public static boolean boom = false;
37     public static boolean tock = false;
38 
tick()39     public static void tick() {
40       boolean tocked = tock;
41       tock = !tock;
42       if (tocked) {
43         count_down--;
44       }
45       if (count_down == 0) {
46         boom = true;
47       }
48     }
49   }
50 
51   /* Base64 encoded dex file for.
52    * // NB The addition of aaa_INITIAL means the fields all have different offsets
53    * public static class Transform {
54    *   public static int aaa_INITIAL = 0;
55    *   public static int count_down = 2;
56    *   public static boolean boom = false;
57    *   public static boolean tock = false;
58    *   public static void tick() {
59    *     boolean tocked = tock;
60    *     tock = !tock;
61    *     if (tocked) {
62    *       count_down--;
63    *     }
64    *     if (count_down == 0) {
65    *       boom = true;
66    *     }
67    *   }
68    * }
69    */
70   public static final byte[] REDEFINED_DEX_BYTES =
71       Base64.getDecoder()
72           .decode(
73               "ZGV4CjAzNQDejZufbnVbJEn1/OfB3XmJPtVbudlWkvnsAwAAcAAAAHhWNBIAAAAAAAAAADQDAAAU"
74                   + "AAAAcAAAAAgAAADAAAAAAQAAAOAAAAAEAAAA7AAAAAQAAAAMAQAAAQAAACwBAACgAgAATAEAAPAB"
75                   + "AAD6AQAAAgIAAAUCAAAfAgAALwIAAFMCAABzAgAAhwIAAJYCAAChAgAApAIAAKcCAAC0AgAAwQIA"
76                   + "AMcCAADTAgAA2QIAAN8CAADlAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACgAAAAsAAAAKAAAA"
77                   + "BgAAAAAAAAABAAAADAAAAAEABwAOAAAAAQAAAA8AAAABAAcAEgAAAAEAAAAAAAAAAQAAAAEAAAAB"
78                   + "AAAAEQAAAAUAAAABAAAAAQAAAAEAAAAFAAAAAAAAAAgAAADgAQAAFgMAAAAAAAACAAAABwMAAA0D"
79                   + "AAACAAAAAAAAAOwCAAALAAAAEgFnAQAAEiBnAAIAagEBAGoBAwAOAAAAAQABAAEAAAD0AgAABAAA"
80                   + "AHAQAwAAAA4AAwAAAAAAAAD5AgAAGwAAABIRYwIDAGMAAwA5ABQAARBqAAMAOAIIAGAAAgDYAAD/"
81                   + "ZwACAGAAAgA5AAQAagEBAA4AEgAo7gAATAEAAAAAAAAAAAAAAAAAAAg8Y2xpbml0PgAGPGluaXQ+"
82                   + "AAFJABhMYXJ0L1Rlc3QxOTg0JFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5ODQ7ACJMZGFsdmlrL2Fu"
83                   + "bm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJDbGFzczsA"
84                   + "EkxqYXZhL2xhbmcvT2JqZWN0OwANVGVzdDE5ODQuamF2YQAJVHJhbnNmb3JtAAFWAAFaAAthYWFf"
85                   + "SU5JVElBTAALYWNjZXNzRmxhZ3MABGJvb20ACmNvdW50X2Rvd24ABG5hbWUABHRpY2sABHRvY2sA"
86                   + "BXZhbHVlAAgABx0tPC0ABwAHDgANAAcdLXgtaksuAnkdAAIDARMYAgIEAg0ECRAXCQQAAwAACQEJ"
87                   + "AQkBCQCIgATYAgGBgASAAwEJmAMAAA8AAAAAAAAAAQAAAAAAAAABAAAAFAAAAHAAAAACAAAACAAA"
88                   + "AMAAAAADAAAAAQAAAOAAAAAEAAAABAAAAOwAAAAFAAAABAAAAAwBAAAGAAAAAQAAACwBAAADEAAA"
89                   + "AQAAAEwBAAABIAAAAwAAAFgBAAAGIAAAAQAAAOABAAACIAAAFAAAAPABAAADIAAAAwAAAOwCAAAE"
90                   + "IAAAAgAAAAcDAAAAIAAAAQAAABYDAAAAEAAAAQAAADQDAAA=");
91 
run()92   public static void run() throws Exception {
93     System.out.println("Dumping fields at start");
94     for (Field f : Transform.class.getDeclaredFields()) {
95       System.out.println(f.toString() + "=" + f.get(null));
96     }
97     Trace.disableTracing(Thread.currentThread());
98     Trace.enableFieldTracing(
99         Test1984.class,
100         Test1984.class.getDeclaredMethod(
101             "notifyFieldAccess",
102             Executable.class,
103             Long.TYPE,
104             Class.class,
105             Object.class,
106             Field.class),
107         Test1984.class.getDeclaredMethod(
108             "notifyFieldModify",
109             Executable.class,
110             Long.TYPE,
111             Class.class,
112             Object.class,
113             Field.class,
114             Object.class),
115         Thread.currentThread());
116     for (Field f : Transform.class.getDeclaredFields()) {
117       Trace.watchFieldAccess(f);
118       Trace.watchFieldModification(f);
119     }
120     // count_down = 2
121     Transform.tick(); // count_down = 2
122     Transform.tick(); // count_down = 1
123     System.out.println("REDEFINING TRANSFORM CLASS");
124     Redefinition.doCommonStructuralClassRedefinition(Transform.class, REDEFINED_DEX_BYTES);
125     Transform.tick(); // count_down = 1
126     Transform.tick(); // count_down = 0
127     System.out.println("Dumping fields at end");
128     for (Field f : Transform.class.getDeclaredFields()) {
129       System.out.println(f.toString() + "=" + f.get(null));
130     }
131     // Turn off tracing so we don't have to deal with print internals.
132     Trace.disableTracing(Thread.currentThread());
133   }
134 }
135