From 9cb2c66248a2b702632e1e2e7fd314f7add3b539 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Fri, 8 Oct 2021 19:16:54 +0800 Subject: [PATCH] Migrate screen rotation latency tracker with shell transition It was done by WMS#startFreezingDisplay~stopFreezingDisplayLocked. The duration is measured from rotation change to start animation. Add a remote callback to know when the rotation animation is started. So WM core can have the paired begin/end of trace that matches the latency exactly. The added ITransitionMetricsReporter is available for any processes. So in the future it can also report the metrics from remote animator. This change focuses on the rotation animation handled by shell. Bug: 199836343 Test: adb shell setprop persist.debug.shell_transit 1; reboot Rotate display and check trace "L". Change-Id: I180d2fe1a77e98b6427ba831a2db2593739376bd --- .../window/ITransitionMetricsReporter.aidl | 33 +++++++++++ .../window/IWindowOrganizerController.aidl | 4 ++ core/java/android/window/TransitionInfo.java | 4 +- .../android/window/TransitionMetrics.java | 57 +++++++++++++++++++ core/java/android/window/WindowOrganizer.java | 14 ++++- .../transition/DefaultTransitionHandler.java | 2 + .../wm/shell/transition/Transitions.java | 3 + .../com/android/server/wm/DisplayContent.java | 6 ++ .../server/wm/TransitionController.java | 29 ++++++++++ .../server/wm/WindowOrganizerController.java | 6 ++ 10 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 core/java/android/window/ITransitionMetricsReporter.aidl create mode 100644 core/java/android/window/TransitionMetrics.java diff --git a/core/java/android/window/ITransitionMetricsReporter.aidl b/core/java/android/window/ITransitionMetricsReporter.aidl new file mode 100644 index 0000000000000..00f71dc7bb905 --- /dev/null +++ b/core/java/android/window/ITransitionMetricsReporter.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 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.window; + +import android.os.IBinder; + +/** + * Implemented by WM Core to know the metrics of transition that runs on a different process. + * @hide + */ +oneway interface ITransitionMetricsReporter { + + /** + * Called when the transition animation starts. + * + * @param startTime The time when the animation started. + */ + void reportAnimationStart(IBinder transitionToken, long startTime); +} diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl index e65fcdd7b13bb..3c7cd0254e789 100644 --- a/core/java/android/window/IWindowOrganizerController.aidl +++ b/core/java/android/window/IWindowOrganizerController.aidl @@ -23,6 +23,7 @@ import android.view.RemoteAnimationAdapter; import android.window.IDisplayAreaOrganizerController; import android.window.ITaskFragmentOrganizerController; import android.window.ITaskOrganizerController; +import android.window.ITransitionMetricsReporter; import android.window.ITransitionPlayer; import android.window.IWindowContainerTransactionCallback; import android.window.WindowContainerToken; @@ -98,4 +99,7 @@ interface IWindowOrganizerController { * this will replace the existing one if set. */ void registerTransitionPlayer(in ITransitionPlayer player); + + /** @return An interface enabling the transition players to report its metrics. */ + ITransitionMetricsReporter getTransitionMetricsReporter(); } diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index c2ffc03b61192..7208930c0b204 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -140,7 +140,7 @@ public final class TransitionInfo implements Parcelable { private TransitionInfo(Parcel in) { mType = in.readInt(); mFlags = in.readInt(); - in.readList(mChanges, null /* classLoader */); + in.readTypedList(mChanges, Change.CREATOR); mRootLeash = new SurfaceControl(); mRootLeash.readFromParcel(in); mRootOffset.readFromParcel(in); @@ -152,7 +152,7 @@ public final class TransitionInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mType); dest.writeInt(mFlags); - dest.writeList(mChanges); + dest.writeTypedList(mChanges); mRootLeash.writeToParcel(dest, flags); mRootOffset.writeToParcel(dest, flags); dest.writeTypedObject(mOptions, flags); diff --git a/core/java/android/window/TransitionMetrics.java b/core/java/android/window/TransitionMetrics.java new file mode 100644 index 0000000000000..9a93c1a1ffd68 --- /dev/null +++ b/core/java/android/window/TransitionMetrics.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 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.window; + +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Singleton; + +/** + * A helper class for who plays transition animation can report its metrics easily. + * @hide + */ +public class TransitionMetrics { + + private final ITransitionMetricsReporter mTransitionMetricsReporter; + + private TransitionMetrics(ITransitionMetricsReporter reporter) { + mTransitionMetricsReporter = reporter; + } + + /** Reports the current timestamp as when the transition animation starts. */ + public void reportAnimationStart(IBinder transitionToken) { + try { + mTransitionMetricsReporter.reportAnimationStart(transitionToken, + SystemClock.elapsedRealtime()); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** Gets the singleton instance of TransitionMetrics. */ + public static TransitionMetrics getInstance() { + return sTransitionMetrics.get(); + } + + private static final Singleton sTransitionMetrics = new Singleton<>() { + @Override + protected TransitionMetrics create() { + return new TransitionMetrics(WindowOrganizer.getTransitionMetricsReporter()); + } + }; +} diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java index e9b8174567a8a..4ea5ea5694fa6 100644 --- a/core/java/android/window/WindowOrganizer.java +++ b/core/java/android/window/WindowOrganizer.java @@ -159,7 +159,19 @@ public class WindowOrganizer { } } - IWindowOrganizerController getWindowOrganizerController() { + /** + * @see TransitionMetrics + * @hide + */ + public static ITransitionMetricsReporter getTransitionMetricsReporter() { + try { + return getWindowOrganizerController().getTransitionMetricsReporter(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + static IWindowOrganizerController getWindowOrganizerController() { return IWindowOrganizerControllerSingleton.get(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 663d6477c3fef..7abda994bb5e0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -70,6 +70,7 @@ import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Transformation; import android.window.TransitionInfo; +import android.window.TransitionMetrics; import android.window.TransitionRequestInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -362,6 +363,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } startTransaction.apply(); + TransitionMetrics.getInstance().reportAnimationStart(transition); // run finish now in-case there are no animations onAnimFinish.run(); return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 27201572d3e8b..c36983189a713 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -44,6 +44,7 @@ import android.window.ITransitionPlayer; import android.window.RemoteTransition; import android.window.TransitionFilter; import android.window.TransitionInfo; +import android.window.TransitionMetrics; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; @@ -192,6 +193,8 @@ public class Transitions implements RemoteCallable { public void register(ShellTaskOrganizer taskOrganizer) { if (mPlayerImpl == null) return; taskOrganizer.registerTransitionPlayer(mPlayerImpl); + // Pre-load the instance. + TransitionMetrics.getInstance(); } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index aaca58ef4ac00..cb2e8beab35e3 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -93,6 +93,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER; import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS; +import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; @@ -3191,6 +3192,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } final Transition t = controller.requestTransitionIfNeeded(TRANSIT_CHANGE, this); if (t != null) { + if (getRotation() != getWindowConfiguration().getRotation()) { + mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN); + controller.mTransitionMetricsReporter.associate(t, + startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN)); + } t.setKnownConfigChanges(this, changes); } } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index ff4cc3d1b7848..94ae1176010ab 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -30,10 +30,12 @@ import android.os.IBinder; import android.os.IRemoteCallback; import android.os.RemoteException; import android.os.SystemClock; +import android.util.ArrayMap; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.WindowManager; import android.window.IRemoteTransition; +import android.window.ITransitionMetricsReporter; import android.window.ITransitionPlayer; import android.window.RemoteTransition; import android.window.TransitionInfo; @@ -45,6 +47,7 @@ import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; import java.util.ArrayList; +import java.util.function.LongConsumer; /** * Handles all the aspects of recording and synchronizing transitions. @@ -58,6 +61,8 @@ class TransitionController { private static final int LEGACY_STATE_RUNNING = 2; private ITransitionPlayer mTransitionPlayer; + final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter(); + final ActivityTaskManagerService mAtm; final TaskSnapshotController mTaskSnapshotController; @@ -324,6 +329,8 @@ class TransitionController { /** @see Transition#finishTransition */ void finishTransition(@NonNull IBinder token) { + // It is usually a no-op but make sure that the metric consumer is removed. + mTransitionMetricsReporter.reportAnimationStart(token, 0 /* startTime */); final Transition record = Transition.fromBinder(token); if (record == null || !mPlayingTransitions.contains(record)) { Slog.e(TAG, "Trying to finish a non-playing transition " + token); @@ -419,6 +426,28 @@ class TransitionController { proto.end(token); } + static class TransitionMetricsReporter extends ITransitionMetricsReporter.Stub { + private final ArrayMap mMetricConsumers = new ArrayMap<>(); + + void associate(IBinder transitionToken, LongConsumer consumer) { + synchronized (mMetricConsumers) { + mMetricConsumers.put(transitionToken, consumer); + } + } + + @Override + public void reportAnimationStart(IBinder transitionToken, long startTime) { + final LongConsumer c; + synchronized (mMetricConsumers) { + if (mMetricConsumers.isEmpty()) return; + c = mMetricConsumers.remove(transitionToken); + } + if (c != null) { + c.accept(startTime); + } + } + } + class Lock { private int mTransitionWaiters = 0; void runWhenIdle(long timeout, Runnable r) { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 6288ab5de8880..6e706e9df0030 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -66,6 +66,7 @@ import android.window.IDisplayAreaOrganizerController; import android.window.ITaskFragmentOrganizer; import android.window.ITaskFragmentOrganizerController; import android.window.ITaskOrganizerController; +import android.window.ITransitionMetricsReporter; import android.window.ITransitionPlayer; import android.window.IWindowContainerTransactionCallback; import android.window.IWindowOrganizerController; @@ -1040,6 +1041,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } } + @Override + public ITransitionMetricsReporter getTransitionMetricsReporter() { + return mTransitionController.mTransitionMetricsReporter; + } + /** Whether the configuration changes are important to report back to an organizer. */ static boolean configurationsAreEqualForOrganizer( Configuration newConfig, @Nullable Configuration oldConfig) {