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<ACTION_ROTATE_SCREEN>".
Change-Id: I180d2fe1a77e98b6427ba831a2db2593739376bd
This commit is contained in:
Riddle Hsu
2021-10-08 19:16:54 +08:00
parent f426ad9776
commit 9cb2c66248
10 changed files with 155 additions and 3 deletions

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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());
}
};
}

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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();
}
/**

View File

@@ -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);
}
}

View File

@@ -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<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) {

View File

@@ -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) {