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