Loading Android.bp +7 −0 Original line number Diff line number Diff line Loading @@ -503,6 +503,13 @@ filegroup { ], } filegroup { name: "framework-android-os-unit-testable-src", srcs: [ "core/java/android/os/DdmSyncState.java", ], } filegroup { name: "framework-networkstack-shared-srcs", srcs: [ Loading core/java/android/app/ActivityThread.java +11 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,8 @@ import android.os.BluetoothServiceManager; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.DdmSyncStageUpdater; import android.os.DdmSyncState.Stage; import android.os.Debug; import android.os.Environment; import android.os.FileUtils; Loading Loading @@ -264,6 +266,9 @@ import java.util.concurrent.atomic.AtomicInteger; */ public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal { private final DdmSyncStageUpdater mDdmSyncStageUpdater = new DdmSyncStageUpdater(); /** @hide */ public static final String TAG = "ActivityThread"; private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565; Loading Loading @@ -6689,6 +6694,8 @@ public final class ActivityThread extends ClientTransactionHandler @UnsupportedAppUsage private void handleBindApplication(AppBindData data) { mDdmSyncStageUpdater.next(Stage.Bind); // Register the UI Thread as a sensitive thread to the runtime. VMRuntime.registerSensitiveThread(); // In the case the stack depth property exists, pass it down to the runtime. Loading Loading @@ -6738,6 +6745,7 @@ public final class ActivityThread extends ClientTransactionHandler data.appInfo.packageName, UserHandle.myUserId()); VMRuntime.setProcessPackageName(data.appInfo.packageName); mDdmSyncStageUpdater.next(Stage.Named); // Pass data directory path to ART. This is used for caching information and // should be set before any application code is loaded. Loading Loading @@ -6942,6 +6950,7 @@ public final class ActivityThread extends ClientTransactionHandler final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy(); if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) { mDdmSyncStageUpdater.next(Stage.Debugger); if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) { waitForDebugger(data); } else if (data.debugMode == ApplicationThreadConstants.DEBUG_SUSPEND) { Loading @@ -6949,6 +6958,7 @@ public final class ActivityThread extends ClientTransactionHandler } // Nothing special to do in case of DEBUG_ON. } mDdmSyncStageUpdater.next(Stage.Running); try { // If the app is being launched for full backup or restore, bring it up in Loading Loading @@ -7852,6 +7862,7 @@ public final class ActivityThread extends ClientTransactionHandler mConfigurationController = new ConfigurationController(this); mSystemThread = system; mStartSeq = startSeq; mDdmSyncStageUpdater.next(Stage.Attach); if (!system) { android.ddm.DdmHandleAppName.setAppName("<pre-initialized>", Loading core/java/android/ddm/DdmHandleHello.java +9 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.ddm; import android.os.DdmSyncState; import android.os.Debug; import android.os.UserHandle; import android.util.Log; Loading Loading @@ -44,6 +45,7 @@ public class DdmHandleHello extends DdmHandle { private static final String[] FRAMEWORK_FEATURES = new String[] { "opengl-tracing", "view-hierarchy", "support_boot_stages" }; /* singleton, do not instantiate */ Loading Loading @@ -145,7 +147,9 @@ public class DdmHandleHello extends DdmHandle { + instructionSetDescription.length() * 2 + vmFlags.length() * 2 + 1 + pkgName.length() * 2); + pkgName.length() * 2 // STAG id (int) + Integer.BYTES); out.order(ChunkHandler.CHUNK_ORDER); out.putInt(CLIENT_PROTOCOL_VERSION); out.putInt(android.os.Process.myPid()); Loading @@ -162,6 +166,10 @@ public class DdmHandleHello extends DdmHandle { out.putInt(pkgName.length()); putString(out, pkgName); // Added API 34 (and advertised via FEAT ddm packet) // Send the current boot stage in ActivityThread out.putInt(DdmSyncState.getStage().toInt()); Chunk reply = new Chunk(CHUNK_HELO, out); /* Loading core/java/android/os/DdmSyncStageUpdater.java 0 → 100644 +63 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; import android.os.DdmSyncState.Stage; import android.util.Slog; import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; import java.nio.ByteBuffer; /** * @hide */ // Making it public so ActivityThread can access it. public class DdmSyncStageUpdater { private static final String TAG = "DdmSyncStageUpdater"; private static final int CHUNK_STAGE = ChunkHandler.type("STAG"); /** * @hide */ public DdmSyncStageUpdater() { } /** * @hide */ // Making it public so ActivityThread can access it. public synchronized void next(Stage stage) { try { DdmSyncState.next(stage); // Request DDMServer to send a STAG chunk ByteBuffer data = ByteBuffer.allocate(Integer.BYTES); data.putInt(stage.toInt()); Chunk stagChunk = new Chunk(CHUNK_STAGE, data); DdmServer.sendChunk(stagChunk); } catch (Exception e) { // Catch everything to make sure we don't impact ActivityThread Slog.w(TAG, "Unable to go to next stage" + stage, e); } } } core/java/android/os/DdmSyncState.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; import java.util.Arrays; /** * Keep track of an app boot state. The main purpose is to stream back DDM packet so a DDM client * can synchronize with the app state. * * The state is static so it can be accessed from HELO handler. * * @hide **/ public final class DdmSyncState { /** * @hide */ public enum Stage { // From zygote to attach Boot("BOOT"), // From attach to handleBindApplication Attach("ATCH"), // When handleBindApplication is finally reached Bind("BIND"), // When the actual package name is known (not the early "<preinitalized>" value). Named("NAMD"), // Can be skipped if the app is not debugged. Debugger("DEBG"), // App is in RunLoop Running("A_GO"); final String mLabel; Stage(String label) { if (label.length() != 4) { throw new IllegalStateException( "Bad stage id '" + label + "'. Must be four letters"); } this.mLabel = label; } /** * To be included in a DDM packet payload, the stage is encoded in a big-endian int * @hide */ public int toInt() { int result = 0; for (int i = 0; i < 4; ++i) { result = ((result << 8) | (mLabel.charAt(i) & 0xff)); } return result; } } private static int sCurrentStageIndex = 0; /** * @hide */ public static synchronized Stage getStage() { return Stage.values()[sCurrentStageIndex]; } /** * @hide */ public static void reset() { sCurrentStageIndex = 0; } /** * Search for the next level down the list of Stage. Only succeed if the next stage * if a later stage (no cycling allowed). * * @hide */ public static synchronized void next(Stage nextStage) { Stage[] stages = Stage.values(); // Search for the requested next stage int rover = sCurrentStageIndex; while (rover < stages.length && stages[rover] != nextStage) { rover++; } if (rover == stages.length || stages[rover] != nextStage) { throw new IllegalStateException( "Cannot go to " + nextStage + " from:" + getInternalState()); } sCurrentStageIndex = rover; } /** * Use to build error messages * @hide */ private static String getInternalState() { StringBuilder sb = new StringBuilder("\n"); sb.append("level = ").append(sCurrentStageIndex); sb.append("\n"); sb.append("stages = "); sb.append(Arrays.toString(Arrays.stream(Stage.values()).map(Enum::name).toArray())); sb.append("\n"); return sb.toString(); } } Loading
Android.bp +7 −0 Original line number Diff line number Diff line Loading @@ -503,6 +503,13 @@ filegroup { ], } filegroup { name: "framework-android-os-unit-testable-src", srcs: [ "core/java/android/os/DdmSyncState.java", ], } filegroup { name: "framework-networkstack-shared-srcs", srcs: [ Loading
core/java/android/app/ActivityThread.java +11 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,8 @@ import android.os.BluetoothServiceManager; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.DdmSyncStageUpdater; import android.os.DdmSyncState.Stage; import android.os.Debug; import android.os.Environment; import android.os.FileUtils; Loading Loading @@ -264,6 +266,9 @@ import java.util.concurrent.atomic.AtomicInteger; */ public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal { private final DdmSyncStageUpdater mDdmSyncStageUpdater = new DdmSyncStageUpdater(); /** @hide */ public static final String TAG = "ActivityThread"; private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565; Loading Loading @@ -6689,6 +6694,8 @@ public final class ActivityThread extends ClientTransactionHandler @UnsupportedAppUsage private void handleBindApplication(AppBindData data) { mDdmSyncStageUpdater.next(Stage.Bind); // Register the UI Thread as a sensitive thread to the runtime. VMRuntime.registerSensitiveThread(); // In the case the stack depth property exists, pass it down to the runtime. Loading Loading @@ -6738,6 +6745,7 @@ public final class ActivityThread extends ClientTransactionHandler data.appInfo.packageName, UserHandle.myUserId()); VMRuntime.setProcessPackageName(data.appInfo.packageName); mDdmSyncStageUpdater.next(Stage.Named); // Pass data directory path to ART. This is used for caching information and // should be set before any application code is loaded. Loading Loading @@ -6942,6 +6950,7 @@ public final class ActivityThread extends ClientTransactionHandler final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy(); if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) { mDdmSyncStageUpdater.next(Stage.Debugger); if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) { waitForDebugger(data); } else if (data.debugMode == ApplicationThreadConstants.DEBUG_SUSPEND) { Loading @@ -6949,6 +6958,7 @@ public final class ActivityThread extends ClientTransactionHandler } // Nothing special to do in case of DEBUG_ON. } mDdmSyncStageUpdater.next(Stage.Running); try { // If the app is being launched for full backup or restore, bring it up in Loading Loading @@ -7852,6 +7862,7 @@ public final class ActivityThread extends ClientTransactionHandler mConfigurationController = new ConfigurationController(this); mSystemThread = system; mStartSeq = startSeq; mDdmSyncStageUpdater.next(Stage.Attach); if (!system) { android.ddm.DdmHandleAppName.setAppName("<pre-initialized>", Loading
core/java/android/ddm/DdmHandleHello.java +9 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.ddm; import android.os.DdmSyncState; import android.os.Debug; import android.os.UserHandle; import android.util.Log; Loading Loading @@ -44,6 +45,7 @@ public class DdmHandleHello extends DdmHandle { private static final String[] FRAMEWORK_FEATURES = new String[] { "opengl-tracing", "view-hierarchy", "support_boot_stages" }; /* singleton, do not instantiate */ Loading Loading @@ -145,7 +147,9 @@ public class DdmHandleHello extends DdmHandle { + instructionSetDescription.length() * 2 + vmFlags.length() * 2 + 1 + pkgName.length() * 2); + pkgName.length() * 2 // STAG id (int) + Integer.BYTES); out.order(ChunkHandler.CHUNK_ORDER); out.putInt(CLIENT_PROTOCOL_VERSION); out.putInt(android.os.Process.myPid()); Loading @@ -162,6 +166,10 @@ public class DdmHandleHello extends DdmHandle { out.putInt(pkgName.length()); putString(out, pkgName); // Added API 34 (and advertised via FEAT ddm packet) // Send the current boot stage in ActivityThread out.putInt(DdmSyncState.getStage().toInt()); Chunk reply = new Chunk(CHUNK_HELO, out); /* Loading
core/java/android/os/DdmSyncStageUpdater.java 0 → 100644 +63 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; import android.os.DdmSyncState.Stage; import android.util.Slog; import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; import java.nio.ByteBuffer; /** * @hide */ // Making it public so ActivityThread can access it. public class DdmSyncStageUpdater { private static final String TAG = "DdmSyncStageUpdater"; private static final int CHUNK_STAGE = ChunkHandler.type("STAG"); /** * @hide */ public DdmSyncStageUpdater() { } /** * @hide */ // Making it public so ActivityThread can access it. public synchronized void next(Stage stage) { try { DdmSyncState.next(stage); // Request DDMServer to send a STAG chunk ByteBuffer data = ByteBuffer.allocate(Integer.BYTES); data.putInt(stage.toInt()); Chunk stagChunk = new Chunk(CHUNK_STAGE, data); DdmServer.sendChunk(stagChunk); } catch (Exception e) { // Catch everything to make sure we don't impact ActivityThread Slog.w(TAG, "Unable to go to next stage" + stage, e); } } }
core/java/android/os/DdmSyncState.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; import java.util.Arrays; /** * Keep track of an app boot state. The main purpose is to stream back DDM packet so a DDM client * can synchronize with the app state. * * The state is static so it can be accessed from HELO handler. * * @hide **/ public final class DdmSyncState { /** * @hide */ public enum Stage { // From zygote to attach Boot("BOOT"), // From attach to handleBindApplication Attach("ATCH"), // When handleBindApplication is finally reached Bind("BIND"), // When the actual package name is known (not the early "<preinitalized>" value). Named("NAMD"), // Can be skipped if the app is not debugged. Debugger("DEBG"), // App is in RunLoop Running("A_GO"); final String mLabel; Stage(String label) { if (label.length() != 4) { throw new IllegalStateException( "Bad stage id '" + label + "'. Must be four letters"); } this.mLabel = label; } /** * To be included in a DDM packet payload, the stage is encoded in a big-endian int * @hide */ public int toInt() { int result = 0; for (int i = 0; i < 4; ++i) { result = ((result << 8) | (mLabel.charAt(i) & 0xff)); } return result; } } private static int sCurrentStageIndex = 0; /** * @hide */ public static synchronized Stage getStage() { return Stage.values()[sCurrentStageIndex]; } /** * @hide */ public static void reset() { sCurrentStageIndex = 0; } /** * Search for the next level down the list of Stage. Only succeed if the next stage * if a later stage (no cycling allowed). * * @hide */ public static synchronized void next(Stage nextStage) { Stage[] stages = Stage.values(); // Search for the requested next stage int rover = sCurrentStageIndex; while (rover < stages.length && stages[rover] != nextStage) { rover++; } if (rover == stages.length || stages[rover] != nextStage) { throw new IllegalStateException( "Cannot go to " + nextStage + " from:" + getInternalState()); } sCurrentStageIndex = rover; } /** * Use to build error messages * @hide */ private static String getInternalState() { StringBuilder sb = new StringBuilder("\n"); sb.append("level = ").append(sCurrentStageIndex); sb.append("\n"); sb.append("stages = "); sb.append(Arrays.toString(Arrays.stream(Stage.values()).map(Enum::name).toArray())); sb.append("\n"); return sb.toString(); } }