Loading core/java/android/view/IWindowManager.aidl +15 −0 Original line number Diff line number Diff line Loading @@ -542,6 +542,21 @@ interface IWindowManager */ boolean isWindowTraceEnabled(); /** * Starts a transition trace. */ void startTransitionTrace(); /** * Stops a transition trace. */ void stopTransitionTrace(); /** * Returns true if transition trace is enabled. */ boolean isTransitionTraceEnabled(); /** * Gets the windowing mode of the display. * Loading core/proto/android/server/windowmanagertransitiontrace.proto 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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. */ syntax = "proto3"; package com.android.server.wm.shell; import "frameworks/base/core/proto/android/server/windowmanagerservice.proto"; option java_multiple_files = true; /* Represents a file full of transition entries. Encoded, it should start with 0x9 0x57 0x49 0x4e 0x54 0x52 0x41 0x43 0x45 (.TRNTRACE), such that it can be easily identified. */ message TransitionTraceProto { /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L (this is needed because enums have to be 32 bits and there's no nice way to put 64bit constants into .proto files. */ enum MagicNumber { INVALID = 0; MAGIC_NUMBER_L = 0x544e5254; /* TRNT (little-endian ASCII) */ MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */ } fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ int64 timestamp = 2; /* The timestamp of when the trace was started. */ repeated Transition transition = 3; } message Transition { enum State { COLLECTING = 0; PENDING = -1; STARTED = 1; PLAYING = 2; ABORT = 3; FINISHED = 4; } int32 id = 1; int32 transition_type = 2; int64 timestamp = 3; State state = 5; int32 flags = 6; repeated ChangeInfo change = 7; } message ChangeInfo { com.android.server.wm.IdentifierProto window_identifier = 1; int32 transit_mode = 2; bool has_changed = 3; int32 change_flags = 4; } services/core/java/com/android/server/wm/Transition.java +32 −1 Original line number Diff line number Diff line Loading @@ -131,18 +131,27 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe */ private static final int STATE_ABORT = 3; /** * This transition has finished playing successfully. */ private static final int STATE_FINISHED = 4; @IntDef(prefix = { "STATE_" }, value = { STATE_PENDING, STATE_COLLECTING, STATE_STARTED, STATE_PLAYING, STATE_ABORT STATE_ABORT, STATE_FINISHED }) @Retention(RetentionPolicy.SOURCE) @interface TransitionState {} final @TransitionType int mType; private int mSyncId = -1; // Used for tracking a Transition throughout a lifecycle (i.e. from STATE_COLLECTING to // STATE_FINISHED or STATE_ABORT), and should only be used for testing and debugging. private int mDebugId = -1; private @TransitionFlags int mFlags; private final TransitionController mController; private final BLASTSyncEngine mSyncEngine; Loading Loading @@ -202,6 +211,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mFlags = flags; mController = controller; mSyncEngine = syncEngine; controller.mTransitionTracer.logState(this); } void addFlag(int flag) { Loading Loading @@ -242,11 +253,21 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe info.mFlags = info.mFlags | ChangeInfo.FLAG_SEAMLESS_ROTATION; } @TransitionState int getState() { return mState; } @VisibleForTesting int getSyncId() { return mSyncId; } @VisibleForTesting int getDebugId() { return mDebugId; } @TransitionFlags int getFlags() { return mFlags; Loading @@ -259,6 +280,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } mState = STATE_COLLECTING; mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG); mDebugId = mSyncId; mController.mTransitionTracer.logState(this); } /** Loading @@ -276,6 +300,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d", mSyncId); applyReady(); mController.mTransitionTracer.logState(this); } /** Loading Loading @@ -644,6 +670,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe dc.removeImeSurfaceImmediately(); dc.handleCompleteDeferredRemoval(); } mState = STATE_FINISHED; mController.mTransitionTracer.logState(this); } void abort() { Loading Loading @@ -673,6 +702,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId); return; } if (mTargetDisplays.isEmpty()) { mTargetDisplays.add(mController.mAtm.mRootWindowContainer.getDefaultDisplay()); } Loading Loading @@ -784,6 +814,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } mStartTransaction = transaction; mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get(); buildFinishTransaction(mFinishTransaction, info.getRootLeash()); if (mController.getTransitionPlayer() != null) { mController.dispatchLegacyAppTransitionStarting(info); Loading services/core/java/com/android/server/wm/TransitionController.java +6 −1 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ class TransitionController { private ITransitionPlayer mTransitionPlayer; final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter(); final TransitionTracer mTransitionTracer; private IApplicationThread mTransitionPlayerThread; final ActivityTaskManagerService mAtm; Loading @@ -100,10 +101,12 @@ class TransitionController { final StatusBarManagerInternal mStatusBar; TransitionController(ActivityTaskManagerService atm, TaskSnapshotController taskSnapshotController) { TaskSnapshotController taskSnapshotController, TransitionTracer transitionTracer) { mAtm = atm; mStatusBar = LocalServices.getService(StatusBarManagerInternal.class); mTaskSnapshotController = taskSnapshotController; mTransitionTracer = transitionTracer; mTransitionPlayerDeath = () -> { synchronized (mAtm.mGlobalLock) { // Clean-up/finish any playing transitions. Loading Loading @@ -514,6 +517,7 @@ class TransitionController { setAnimationRunning(true /* running */); } mPlayingTransitions.add(transition); mTransitionTracer.logState(transition); } private void setAnimationRunning(boolean running) { Loading @@ -532,6 +536,7 @@ class TransitionController { } transition.abort(); mCollectingTransition = null; mTransitionTracer.logState(transition); } /** Loading services/core/java/com/android/server/wm/TransitionTracer.java 0 → 100644 +234 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 com.android.server.wm; import static android.os.Build.IS_USER; import static com.android.server.wm.shell.ChangeInfo.CHANGE_FLAGS; import static com.android.server.wm.shell.ChangeInfo.HAS_CHANGED; import static com.android.server.wm.shell.ChangeInfo.TRANSIT_MODE; import static com.android.server.wm.shell.ChangeInfo.WINDOW_IDENTIFIER; import static com.android.server.wm.shell.Transition.CHANGE; import static com.android.server.wm.shell.Transition.FLAGS; import static com.android.server.wm.shell.Transition.ID; import static com.android.server.wm.shell.Transition.STATE; import static com.android.server.wm.shell.Transition.TIMESTAMP; import static com.android.server.wm.shell.Transition.TRANSITION_TYPE; import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER; import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_H; import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_L; import static com.android.server.wm.shell.TransitionTraceProto.TRANSITION; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.SystemClock; import android.os.Trace; import android.util.Log; import android.util.proto.ProtoOutputStream; import com.android.internal.util.TraceBuffer; import com.android.server.wm.Transition.ChangeInfo; import com.android.server.wm.shell.TransitionTraceProto; import java.io.File; import java.io.IOException; import java.io.PrintWriter; /** * Helper class to collect and dump transition traces. */ public class TransitionTracer { private static final String LOG_TAG = "TransitionTracer"; /** * Maximum buffer size, currently defined as 5 MB */ private static final int BUFFER_CAPACITY = 5120 * 1024; // 5 MB static final String WINSCOPE_EXT = ".winscope"; private static final String TRACE_FILE = "/data/misc/wmtrace/transition_trace" + WINSCOPE_EXT; private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; private final TransitionTraceBuffer mTraceBuffer = new TransitionTraceBuffer(); private final Object mEnabledLock = new Object(); private volatile boolean mEnabled = false; private long mTraceStartTimestamp; private class TransitionTraceBuffer { private final TraceBuffer mBuffer = new TraceBuffer(BUFFER_CAPACITY); private void pushTransitionState(Transition transition) { final ProtoOutputStream outputStream = new ProtoOutputStream(); final long transitionEntryToken = outputStream.start(TRANSITION); outputStream.write(ID, transition.getDebugId()); outputStream.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos()); outputStream.write(TRANSITION_TYPE, transition.mType); outputStream.write(STATE, transition.getState()); outputStream.write(FLAGS, transition.getFlags()); for (int i = 0; i < transition.mChanges.size(); ++i) { final WindowContainer window = transition.mChanges.keyAt(i); final ChangeInfo changeInfo = transition.mChanges.valueAt(i); writeChange(outputStream, window, changeInfo); } outputStream.end(transitionEntryToken); mBuffer.add(outputStream); } private void writeChange(ProtoOutputStream outputStream, WindowContainer window, ChangeInfo changeInfo) { Trace.beginSection("TransitionProto#addChange"); final long changeEntryToken = outputStream.start(CHANGE); final int transitMode = changeInfo.getTransitMode(window); final boolean hasChanged = changeInfo.hasChanged(window); final int changeFlags = changeInfo.getChangeFlags(window); outputStream.write(TRANSIT_MODE, transitMode); outputStream.write(HAS_CHANGED, hasChanged); outputStream.write(CHANGE_FLAGS, changeFlags); window.writeIdentifierToProto(outputStream, WINDOW_IDENTIFIER); outputStream.end(changeEntryToken); Trace.endSection(); } public void writeToFile(File file, ProtoOutputStream proto) throws IOException { mBuffer.writeTraceToFile(file, proto); } public void reset() { mBuffer.resetBuffer(); } } /** * Records the current state of a transition in the transition trace (if it is running). * @param transition the transition that we want to record the state of. */ public void logState(com.android.server.wm.Transition transition) { if (!mEnabled) { return; } Log.d(LOG_TAG, "Logging state of transition " + transition); mTraceBuffer.pushTransitionState(transition); } /** * Starts collecting transitions for the trace. * If called while a trace is already running, this will reset the trace. */ public void startTrace(@Nullable PrintWriter pw) { if (IS_USER) { LogAndPrintln.e(pw, "Tracing is not supported on user builds."); return; } Trace.beginSection("TransitionTracer#startTrace"); LogAndPrintln.i(pw, "Starting shell transition trace."); synchronized (mEnabledLock) { mTraceStartTimestamp = SystemClock.elapsedRealtime(); mEnabled = true; mTraceBuffer.reset(); } Trace.endSection(); } /** * Stops collecting the transition trace and dump to trace to file. * * Dumps the trace to @link{TRACE_FILE}. */ public void stopTrace(@Nullable PrintWriter pw) { stopTrace(pw, new File(TRACE_FILE)); } /** * Stops collecting the transition trace and dump to trace to file. * @param outputFile The file to dump the transition trace to. */ public void stopTrace(@Nullable PrintWriter pw, File outputFile) { if (IS_USER) { LogAndPrintln.e(pw, "Tracing is not supported on user builds."); return; } Trace.beginSection("TransitionTracer#stopTrace"); LogAndPrintln.i(pw, "Stopping shell transition trace."); synchronized (mEnabledLock) { if (!mEnabled) { LogAndPrintln.e(pw, "Error: Tracing can't be stopped because it hasn't been started."); return; } mEnabled = false; writeTraceToFileLocked(pw, outputFile); } Trace.endSection(); } boolean isEnabled() { return mEnabled; } private void writeTraceToFileLocked(@Nullable PrintWriter pw, File file) { Trace.beginSection("TransitionTracer#writeTraceToFileLocked"); try { ProtoOutputStream proto = new ProtoOutputStream(); proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); proto.write(TransitionTraceProto.TIMESTAMP, mTraceStartTimestamp); int pid = android.os.Process.myPid(); LogAndPrintln.i(pw, "Writing file to " + file.getAbsolutePath() + " from process " + pid); mTraceBuffer.writeToFile(file, proto); } catch (IOException e) { LogAndPrintln.e(pw, "Unable to write buffer to file", e); } Trace.endSection(); } private static class LogAndPrintln { private static void i(@Nullable PrintWriter pw, String msg) { Log.i(LOG_TAG, msg); if (pw != null) { pw.println(msg); pw.flush(); } } private static void e(@Nullable PrintWriter pw, String msg) { Log.e(LOG_TAG, msg); if (pw != null) { pw.println("ERROR: " + msg); pw.flush(); } } private static void e(@Nullable PrintWriter pw, String msg, @NonNull Exception e) { Log.e(LOG_TAG, msg, e); if (pw != null) { pw.println("ERROR: " + msg + " ::\n " + e); pw.flush(); } } } } Loading
core/java/android/view/IWindowManager.aidl +15 −0 Original line number Diff line number Diff line Loading @@ -542,6 +542,21 @@ interface IWindowManager */ boolean isWindowTraceEnabled(); /** * Starts a transition trace. */ void startTransitionTrace(); /** * Stops a transition trace. */ void stopTransitionTrace(); /** * Returns true if transition trace is enabled. */ boolean isTransitionTraceEnabled(); /** * Gets the windowing mode of the display. * Loading
core/proto/android/server/windowmanagertransitiontrace.proto 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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. */ syntax = "proto3"; package com.android.server.wm.shell; import "frameworks/base/core/proto/android/server/windowmanagerservice.proto"; option java_multiple_files = true; /* Represents a file full of transition entries. Encoded, it should start with 0x9 0x57 0x49 0x4e 0x54 0x52 0x41 0x43 0x45 (.TRNTRACE), such that it can be easily identified. */ message TransitionTraceProto { /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L (this is needed because enums have to be 32 bits and there's no nice way to put 64bit constants into .proto files. */ enum MagicNumber { INVALID = 0; MAGIC_NUMBER_L = 0x544e5254; /* TRNT (little-endian ASCII) */ MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */ } fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ int64 timestamp = 2; /* The timestamp of when the trace was started. */ repeated Transition transition = 3; } message Transition { enum State { COLLECTING = 0; PENDING = -1; STARTED = 1; PLAYING = 2; ABORT = 3; FINISHED = 4; } int32 id = 1; int32 transition_type = 2; int64 timestamp = 3; State state = 5; int32 flags = 6; repeated ChangeInfo change = 7; } message ChangeInfo { com.android.server.wm.IdentifierProto window_identifier = 1; int32 transit_mode = 2; bool has_changed = 3; int32 change_flags = 4; }
services/core/java/com/android/server/wm/Transition.java +32 −1 Original line number Diff line number Diff line Loading @@ -131,18 +131,27 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe */ private static final int STATE_ABORT = 3; /** * This transition has finished playing successfully. */ private static final int STATE_FINISHED = 4; @IntDef(prefix = { "STATE_" }, value = { STATE_PENDING, STATE_COLLECTING, STATE_STARTED, STATE_PLAYING, STATE_ABORT STATE_ABORT, STATE_FINISHED }) @Retention(RetentionPolicy.SOURCE) @interface TransitionState {} final @TransitionType int mType; private int mSyncId = -1; // Used for tracking a Transition throughout a lifecycle (i.e. from STATE_COLLECTING to // STATE_FINISHED or STATE_ABORT), and should only be used for testing and debugging. private int mDebugId = -1; private @TransitionFlags int mFlags; private final TransitionController mController; private final BLASTSyncEngine mSyncEngine; Loading Loading @@ -202,6 +211,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mFlags = flags; mController = controller; mSyncEngine = syncEngine; controller.mTransitionTracer.logState(this); } void addFlag(int flag) { Loading Loading @@ -242,11 +253,21 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe info.mFlags = info.mFlags | ChangeInfo.FLAG_SEAMLESS_ROTATION; } @TransitionState int getState() { return mState; } @VisibleForTesting int getSyncId() { return mSyncId; } @VisibleForTesting int getDebugId() { return mDebugId; } @TransitionFlags int getFlags() { return mFlags; Loading @@ -259,6 +280,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } mState = STATE_COLLECTING; mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG); mDebugId = mSyncId; mController.mTransitionTracer.logState(this); } /** Loading @@ -276,6 +300,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d", mSyncId); applyReady(); mController.mTransitionTracer.logState(this); } /** Loading Loading @@ -644,6 +670,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe dc.removeImeSurfaceImmediately(); dc.handleCompleteDeferredRemoval(); } mState = STATE_FINISHED; mController.mTransitionTracer.logState(this); } void abort() { Loading Loading @@ -673,6 +702,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId); return; } if (mTargetDisplays.isEmpty()) { mTargetDisplays.add(mController.mAtm.mRootWindowContainer.getDefaultDisplay()); } Loading Loading @@ -784,6 +814,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } mStartTransaction = transaction; mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get(); buildFinishTransaction(mFinishTransaction, info.getRootLeash()); if (mController.getTransitionPlayer() != null) { mController.dispatchLegacyAppTransitionStarting(info); Loading
services/core/java/com/android/server/wm/TransitionController.java +6 −1 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ class TransitionController { private ITransitionPlayer mTransitionPlayer; final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter(); final TransitionTracer mTransitionTracer; private IApplicationThread mTransitionPlayerThread; final ActivityTaskManagerService mAtm; Loading @@ -100,10 +101,12 @@ class TransitionController { final StatusBarManagerInternal mStatusBar; TransitionController(ActivityTaskManagerService atm, TaskSnapshotController taskSnapshotController) { TaskSnapshotController taskSnapshotController, TransitionTracer transitionTracer) { mAtm = atm; mStatusBar = LocalServices.getService(StatusBarManagerInternal.class); mTaskSnapshotController = taskSnapshotController; mTransitionTracer = transitionTracer; mTransitionPlayerDeath = () -> { synchronized (mAtm.mGlobalLock) { // Clean-up/finish any playing transitions. Loading Loading @@ -514,6 +517,7 @@ class TransitionController { setAnimationRunning(true /* running */); } mPlayingTransitions.add(transition); mTransitionTracer.logState(transition); } private void setAnimationRunning(boolean running) { Loading @@ -532,6 +536,7 @@ class TransitionController { } transition.abort(); mCollectingTransition = null; mTransitionTracer.logState(transition); } /** Loading
services/core/java/com/android/server/wm/TransitionTracer.java 0 → 100644 +234 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 com.android.server.wm; import static android.os.Build.IS_USER; import static com.android.server.wm.shell.ChangeInfo.CHANGE_FLAGS; import static com.android.server.wm.shell.ChangeInfo.HAS_CHANGED; import static com.android.server.wm.shell.ChangeInfo.TRANSIT_MODE; import static com.android.server.wm.shell.ChangeInfo.WINDOW_IDENTIFIER; import static com.android.server.wm.shell.Transition.CHANGE; import static com.android.server.wm.shell.Transition.FLAGS; import static com.android.server.wm.shell.Transition.ID; import static com.android.server.wm.shell.Transition.STATE; import static com.android.server.wm.shell.Transition.TIMESTAMP; import static com.android.server.wm.shell.Transition.TRANSITION_TYPE; import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER; import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_H; import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_L; import static com.android.server.wm.shell.TransitionTraceProto.TRANSITION; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.SystemClock; import android.os.Trace; import android.util.Log; import android.util.proto.ProtoOutputStream; import com.android.internal.util.TraceBuffer; import com.android.server.wm.Transition.ChangeInfo; import com.android.server.wm.shell.TransitionTraceProto; import java.io.File; import java.io.IOException; import java.io.PrintWriter; /** * Helper class to collect and dump transition traces. */ public class TransitionTracer { private static final String LOG_TAG = "TransitionTracer"; /** * Maximum buffer size, currently defined as 5 MB */ private static final int BUFFER_CAPACITY = 5120 * 1024; // 5 MB static final String WINSCOPE_EXT = ".winscope"; private static final String TRACE_FILE = "/data/misc/wmtrace/transition_trace" + WINSCOPE_EXT; private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; private final TransitionTraceBuffer mTraceBuffer = new TransitionTraceBuffer(); private final Object mEnabledLock = new Object(); private volatile boolean mEnabled = false; private long mTraceStartTimestamp; private class TransitionTraceBuffer { private final TraceBuffer mBuffer = new TraceBuffer(BUFFER_CAPACITY); private void pushTransitionState(Transition transition) { final ProtoOutputStream outputStream = new ProtoOutputStream(); final long transitionEntryToken = outputStream.start(TRANSITION); outputStream.write(ID, transition.getDebugId()); outputStream.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos()); outputStream.write(TRANSITION_TYPE, transition.mType); outputStream.write(STATE, transition.getState()); outputStream.write(FLAGS, transition.getFlags()); for (int i = 0; i < transition.mChanges.size(); ++i) { final WindowContainer window = transition.mChanges.keyAt(i); final ChangeInfo changeInfo = transition.mChanges.valueAt(i); writeChange(outputStream, window, changeInfo); } outputStream.end(transitionEntryToken); mBuffer.add(outputStream); } private void writeChange(ProtoOutputStream outputStream, WindowContainer window, ChangeInfo changeInfo) { Trace.beginSection("TransitionProto#addChange"); final long changeEntryToken = outputStream.start(CHANGE); final int transitMode = changeInfo.getTransitMode(window); final boolean hasChanged = changeInfo.hasChanged(window); final int changeFlags = changeInfo.getChangeFlags(window); outputStream.write(TRANSIT_MODE, transitMode); outputStream.write(HAS_CHANGED, hasChanged); outputStream.write(CHANGE_FLAGS, changeFlags); window.writeIdentifierToProto(outputStream, WINDOW_IDENTIFIER); outputStream.end(changeEntryToken); Trace.endSection(); } public void writeToFile(File file, ProtoOutputStream proto) throws IOException { mBuffer.writeTraceToFile(file, proto); } public void reset() { mBuffer.resetBuffer(); } } /** * Records the current state of a transition in the transition trace (if it is running). * @param transition the transition that we want to record the state of. */ public void logState(com.android.server.wm.Transition transition) { if (!mEnabled) { return; } Log.d(LOG_TAG, "Logging state of transition " + transition); mTraceBuffer.pushTransitionState(transition); } /** * Starts collecting transitions for the trace. * If called while a trace is already running, this will reset the trace. */ public void startTrace(@Nullable PrintWriter pw) { if (IS_USER) { LogAndPrintln.e(pw, "Tracing is not supported on user builds."); return; } Trace.beginSection("TransitionTracer#startTrace"); LogAndPrintln.i(pw, "Starting shell transition trace."); synchronized (mEnabledLock) { mTraceStartTimestamp = SystemClock.elapsedRealtime(); mEnabled = true; mTraceBuffer.reset(); } Trace.endSection(); } /** * Stops collecting the transition trace and dump to trace to file. * * Dumps the trace to @link{TRACE_FILE}. */ public void stopTrace(@Nullable PrintWriter pw) { stopTrace(pw, new File(TRACE_FILE)); } /** * Stops collecting the transition trace and dump to trace to file. * @param outputFile The file to dump the transition trace to. */ public void stopTrace(@Nullable PrintWriter pw, File outputFile) { if (IS_USER) { LogAndPrintln.e(pw, "Tracing is not supported on user builds."); return; } Trace.beginSection("TransitionTracer#stopTrace"); LogAndPrintln.i(pw, "Stopping shell transition trace."); synchronized (mEnabledLock) { if (!mEnabled) { LogAndPrintln.e(pw, "Error: Tracing can't be stopped because it hasn't been started."); return; } mEnabled = false; writeTraceToFileLocked(pw, outputFile); } Trace.endSection(); } boolean isEnabled() { return mEnabled; } private void writeTraceToFileLocked(@Nullable PrintWriter pw, File file) { Trace.beginSection("TransitionTracer#writeTraceToFileLocked"); try { ProtoOutputStream proto = new ProtoOutputStream(); proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); proto.write(TransitionTraceProto.TIMESTAMP, mTraceStartTimestamp); int pid = android.os.Process.myPid(); LogAndPrintln.i(pw, "Writing file to " + file.getAbsolutePath() + " from process " + pid); mTraceBuffer.writeToFile(file, proto); } catch (IOException e) { LogAndPrintln.e(pw, "Unable to write buffer to file", e); } Trace.endSection(); } private static class LogAndPrintln { private static void i(@Nullable PrintWriter pw, String msg) { Log.i(LOG_TAG, msg); if (pw != null) { pw.println(msg); pw.flush(); } } private static void e(@Nullable PrintWriter pw, String msg) { Log.e(LOG_TAG, msg); if (pw != null) { pw.println("ERROR: " + msg); pw.flush(); } } private static void e(@Nullable PrintWriter pw, String msg, @NonNull Exception e) { Log.e(LOG_TAG, msg, e); if (pw != null) { pw.println("ERROR: " + msg + " ::\n " + e); pw.flush(); } } } }