diff options
10 files changed, 155 insertions, 3 deletions
diff --git a/core/java/android/window/ITransitionMetricsReporter.aidl b/core/java/android/window/ITransitionMetricsReporter.aidl new file mode 100644 index 000000000000..00f71dc7bb90 --- /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 e65fcdd7b13b..3c7cd0254e78 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 c2ffc03b6119..7208930c0b20 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 000000000000..9a93c1a1ffd6 --- /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<TransitionMetrics> 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 e9b8174567a8..4ea5ea5694fa 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 663d6477c3fe..7abda994bb5e 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 27201572d3e8..c36983189a71 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<Transitions> { 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 2f88a9800f90..0ecee6641509 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; @@ -3230,6 +3231,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 b849170d8f3f..bf4cf5f75e36 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); @@ -420,6 +427,28 @@ class TransitionController { proto.end(token); } + static class TransitionMetricsReporter extends ITransitionMetricsReporter.Stub { + private final ArrayMap<IBinder, LongConsumer> 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 6288ab5de888..6e706e9df003 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) { |