Merge "DDM: Introduce STAG packets"
This commit is contained in:
committed by
Android (Google) Code Review
commit
f82fabf91c
@@ -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: [
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
@@ -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>",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.ddm;
|
||||
|
||||
import android.os.DdmSyncState;
|
||||
import android.os.Debug;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
@@ -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 */
|
||||
@@ -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());
|
||||
@@ -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);
|
||||
|
||||
/*
|
||||
|
||||
63
core/java/android/os/DdmSyncStageUpdater.java
Normal file
63
core/java/android/os/DdmSyncStageUpdater.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
127
core/java/android/os/DdmSyncState.java
Normal file
127
core/java/android/os/DdmSyncState.java
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -79,3 +79,7 @@ per-file PermissionEnforcer.java = tweek@google.com, brufino@google.com
|
||||
|
||||
# ART
|
||||
per-file ArtModuleServiceManager.java = file:platform/art:/OWNERS
|
||||
|
||||
# DDM Protocol
|
||||
per-file DdmSyncState.java = sanglardf@google.com, rpaquay@google.com
|
||||
per-file DdmSyncStageUpdater.java = sanglardf@google.com, rpaquay@google.com
|
||||
|
||||
@@ -41,3 +41,17 @@ java_library {
|
||||
"mockito-target-extended-minus-junit4",
|
||||
],
|
||||
}
|
||||
|
||||
java_test_host {
|
||||
name: "frameworks-base-android-os-unittests",
|
||||
srcs: [
|
||||
":framework-android-os-unit-testable-src",
|
||||
"java/android/os/test/DdmSyncStateTest.java",
|
||||
],
|
||||
static_libs: [
|
||||
"junit",
|
||||
],
|
||||
test_options: {
|
||||
unit_test: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import android.os.DdmSyncState;
|
||||
import android.os.DdmSyncState.Stage;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test DdmSyncState, the Android app stage boot sync system for DDM Client.
|
||||
*/
|
||||
|
||||
public class DdmSyncStateTest {
|
||||
|
||||
@Test
|
||||
public void testNoCycle() {
|
||||
DdmSyncState.reset();
|
||||
try {
|
||||
DdmSyncState.next(Stage.Attach);
|
||||
DdmSyncState.next(Stage.Bind);
|
||||
DdmSyncState.next(Stage.Named);
|
||||
DdmSyncState.next(Stage.Debugger);
|
||||
DdmSyncState.next(Stage.Running);
|
||||
|
||||
// Cycling back here which is not allowed
|
||||
DdmSyncState.next(Stage.Attach);
|
||||
Assert.fail("Going back to attach should have failed");
|
||||
} catch (IllegalStateException ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDebuggerFlow() {
|
||||
DdmSyncState.reset();
|
||||
DdmSyncState.next(Stage.Attach);
|
||||
DdmSyncState.next(Stage.Bind);
|
||||
DdmSyncState.next(Stage.Named);
|
||||
DdmSyncState.next(Stage.Debugger);
|
||||
DdmSyncState.next(Stage.Running);
|
||||
Assert.assertEquals(Stage.Running, DdmSyncState.getStage());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoDebugFlow() {
|
||||
DdmSyncState.reset();
|
||||
DdmSyncState.next(Stage.Attach);
|
||||
DdmSyncState.next(Stage.Bind);
|
||||
DdmSyncState.next(Stage.Named);
|
||||
// Notice how Stage.Debugger stage is skipped
|
||||
DdmSyncState.next(Stage.Running);
|
||||
Assert.assertEquals(Stage.Running, DdmSyncState.getStage());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user