1 /*
2  * Copyright (C) 2023 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.os;
18 
19 import java.util.Arrays;
20 
21 /**
22  * Keep track of an app boot state. The main purpose is to stream back DDM packet so a DDM client
23  * can synchronize with the app state.
24  *
25  * The state is static so it can be accessed from HELO handler.
26  *
27  * @hide
28  **/
29 public final class DdmSyncState {
30 
31     /**
32      * @hide
33      */
34     public enum Stage {
35         // From zygote to attach
36         Boot("BOOT"),
37 
38         // From attach to handleBindApplication
39         Attach("ATCH"),
40 
41         // When handleBindApplication is finally reached
42         Bind("BIND"),
43 
44         // When the actual package name is known (not the early "<preinitalized>" value).
45         Named("NAMD"),
46 
47         // Can be skipped if the app is not debugged.
48         Debugger("DEBG"),
49 
50         // App is in RunLoop
51         Running("A_GO");
52 
53         final String mLabel;
54 
Stage(String label)55         Stage(String label) {
56             if (label.length() != 4) {
57                 throw new IllegalStateException(
58                     "Bad stage id '" + label + "'. Must be four letters");
59             }
60             this.mLabel = label;
61         }
62 
63         /**
64          * To be included in a DDM packet payload, the stage is encoded in a big-endian int
65          * @hide
66          */
toInt()67         public int toInt() {
68             int result = 0;
69             for (int i = 0; i < 4; ++i) {
70                 result = ((result << 8) | (mLabel.charAt(i) & 0xff));
71             }
72             return result;
73         }
74     }
75 
76     private static int sCurrentStageIndex = 0;
77 
78     /**
79      * @hide
80      */
getStage()81     public static synchronized Stage getStage() {
82         return Stage.values()[sCurrentStageIndex];
83     }
84 
85     /**
86      * @hide
87      */
reset()88     public static void reset() {
89         sCurrentStageIndex = 0;
90     }
91 
92     /**
93      * Search for the next level down the list of Stage. Only succeed if the next stage
94      * if a later stage (no cycling allowed).
95      *
96      * @hide
97      */
next(Stage nextStage)98     public static synchronized void next(Stage nextStage) {
99         Stage[] stages = Stage.values();
100         // Search for the requested next stage
101         int rover = sCurrentStageIndex;
102         while (rover < stages.length && stages[rover] != nextStage) {
103             rover++;
104         }
105 
106         if (rover == stages.length || stages[rover] != nextStage) {
107             throw new IllegalStateException(
108                 "Cannot go to " + nextStage + " from:" + getInternalState());
109         }
110 
111         sCurrentStageIndex = rover;
112     }
113 
114     /**
115      * Use to build error messages
116      * @hide
117      */
getInternalState()118     private static String getInternalState() {
119         StringBuilder sb = new StringBuilder("\n");
120         sb.append("level = ").append(sCurrentStageIndex);
121         sb.append("\n");
122         sb.append("stages = ");
123         sb.append(Arrays.toString(Arrays.stream(Stage.values()).map(Enum::name).toArray()));
124         sb.append("\n");
125         return sb.toString();
126     }
127 }
128