Merge "Reduce lock contention of setting running shell remote transition" into tm-qpr-dev

This commit is contained in:
Riddle Hsu
2022-08-01 07:18:50 +00:00
committed by Android (Google) Code Review
11 changed files with 181 additions and 86 deletions

View File

@@ -68,7 +68,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.Context;
@@ -522,14 +521,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
finishedCallback.onAnimationFinished();
}
};
Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication());
try {
try {
ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
adapter.getCallingApplication());
} catch (SecurityException e) {
Slog.e(TAG, "Unable to boost animation thread. This should only happen"
+ " during unit tests");
}
adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
augmentedNonApps, wrapCallback);
} catch (RemoteException e) {

View File

@@ -18,11 +18,9 @@ package com.android.wm.shell.transition;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
@@ -87,17 +85,11 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
});
}
};
Transitions.setRunningRemoteTransitionDelegate(mRemote.getAppThread());
try {
if (mRemote.asBinder() != null) {
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
try {
ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
mRemote.getAppThread());
} catch (SecurityException e) {
Slog.e(Transitions.TAG, "Unable to boost animation thread. This should only happen"
+ " during unit tests");
}
mRemote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb);
// assume that remote will apply the start transaction.
startTransaction.clear();

View File

@@ -18,7 +18,6 @@ package com.android.wm.shell.transition;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -129,15 +128,9 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
});
}
};
Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread());
try {
handleDeath(remote.asBinder(), finishCallback);
try {
ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
remote.getAppThread());
} catch (SecurityException e) {
Log.e(Transitions.TAG, "Unable to boost animation thread. This should only happen"
+ " during unit tests");
}
remote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb);
// assume that remote will apply the start transaction.
startTransaction.clear();

View File

@@ -31,6 +31,8 @@ import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTas
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -234,6 +236,19 @@ public class Transitions implements RemoteCallable<Transitions> {
mRemoteTransitionHandler.removeFiltered(remoteTransition);
}
/** Boosts the process priority of remote animation player. */
public static void setRunningRemoteTransitionDelegate(IApplicationThread appThread) {
if (appThread == null) return;
try {
ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(appThread);
} catch (SecurityException e) {
Log.e(TAG, "Unable to boost animation process. This should only happen"
+ " during unit tests");
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Runs the given {@code runnable} when the last active transition has finished, or immediately
* if there are currently no active transitions.

View File

@@ -1661,9 +1661,6 @@ class ActivityStarter {
&& transitionController.getTransitionPlayer() != null)
? transitionController.createTransition(TRANSIT_OPEN) : null;
RemoteTransition remoteTransition = r.takeRemoteTransition();
if (newTransition != null && remoteTransition != null) {
newTransition.setRemoteTransition(remoteTransition);
}
transitionController.collect(r);
try {
mService.deferWindowLayout();

View File

@@ -5324,7 +5324,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
public void setRunningRemoteTransitionDelegate(IApplicationThread caller) {
public void setRunningRemoteTransitionDelegate(IApplicationThread delegate) {
final TransitionController controller = getTransitionController();
// A quick path without entering WM lock.
if (delegate != null && controller.mRemotePlayer.reportRunning(delegate)) {
// The delegate was known as running remote transition.
return;
}
mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
"setRunningRemoteTransition");
final int callingPid = Binder.getCallingPid();
@@ -5341,13 +5347,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
Slog.e(TAG, msg);
throw new SecurityException(msg);
}
final WindowProcessController wpc = getProcessController(caller);
final WindowProcessController wpc = getProcessController(delegate);
if (wpc == null) {
Slog.w(TAG, "Unable to find process for application " + caller);
Slog.w(TAG, "setRunningRemoteTransition: no process for " + delegate);
return;
}
wpc.setRunningRemoteAnimation(true /* running */);
callingProc.addRemoteAnimationDelegate(wpc);
controller.mRemotePlayer.update(wpc, true /* running */, false /* predict */);
}
}

View File

@@ -45,6 +45,7 @@ import android.window.RemoteTransition;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.LocalServices;
@@ -77,9 +78,10 @@ class TransitionController {
final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter();
final TransitionTracer mTransitionTracer;
private IApplicationThread mTransitionPlayerThread;
private WindowProcessController mTransitionPlayerProc;
final ActivityTaskManagerService mAtm;
final TaskSnapshotController mTaskSnapshotController;
final RemotePlayer mRemotePlayer;
private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners =
new ArrayList<>();
@@ -112,6 +114,7 @@ class TransitionController {
TaskSnapshotController taskSnapshotController,
TransitionTracer transitionTracer) {
mAtm = atm;
mRemotePlayer = new RemotePlayer(atm);
mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
mTaskSnapshotController = taskSnapshotController;
mTransitionTracer = transitionTracer;
@@ -123,6 +126,8 @@ class TransitionController {
}
mPlayingTransitions.clear();
mTransitionPlayer = null;
mTransitionPlayerProc = null;
mRemotePlayer.clear();
mRunningLock.doNotifyLocked();
}
};
@@ -169,7 +174,7 @@ class TransitionController {
}
void registerTransitionPlayer(@Nullable ITransitionPlayer player,
@Nullable IApplicationThread appThread) {
@Nullable WindowProcessController playerProc) {
try {
// Note: asBinder() can be null if player is same process (likely in a test).
if (mTransitionPlayer != null) {
@@ -182,7 +187,7 @@ class TransitionController {
player.asBinder().linkToDeath(mTransitionPlayerDeath, 0);
}
mTransitionPlayer = player;
mTransitionPlayerThread = appThread;
mTransitionPlayerProc = playerProc;
} catch (RemoteException e) {
throw new RuntimeException("Unable to set transition player");
}
@@ -421,6 +426,7 @@ class TransitionController {
}
mTransitionPlayer.requestStartTransition(transition, new TransitionRequestInfo(
transition.mType, info, remoteTransition, displayChange));
transition.setRemoteTransition(remoteTransition);
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting transition", e);
transition.start();
@@ -537,9 +543,7 @@ class TransitionController {
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Finish Transition: %s", record);
mPlayingTransitions.remove(record);
if (mPlayingTransitions.isEmpty()) {
setAnimationRunning(false /* running */);
}
updateRunningRemoteAnimation(record, false /* isPlaying */);
record.finishTransition();
mRunningLock.doNotifyLocked();
}
@@ -549,21 +553,27 @@ class TransitionController {
throw new IllegalStateException("Trying to move non-collecting transition to playing");
}
mCollectingTransition = null;
if (mPlayingTransitions.isEmpty()) {
setAnimationRunning(true /* running */);
}
mPlayingTransitions.add(transition);
updateRunningRemoteAnimation(transition, true /* isPlaying */);
mTransitionTracer.logState(transition);
}
private void setAnimationRunning(boolean running) {
if (mTransitionPlayerThread == null) return;
final WindowProcessController wpc = mAtm.getProcessController(mTransitionPlayerThread);
if (wpc == null) {
Slog.w(TAG, "Unable to find process for player thread=" + mTransitionPlayerThread);
/** Updates the process state of animation player. */
private void updateRunningRemoteAnimation(Transition transition, boolean isPlaying) {
if (mTransitionPlayerProc == null) return;
if (isPlaying) {
mTransitionPlayerProc.setRunningRemoteAnimation(true);
} else if (mPlayingTransitions.isEmpty()) {
mTransitionPlayerProc.setRunningRemoteAnimation(false);
mRemotePlayer.clear();
return;
}
wpc.setRunningRemoteAnimation(running);
final RemoteTransition remote = transition.getRemoteTransition();
if (remote == null) return;
final IApplicationThread appThread = remote.getAppThread();
final WindowProcessController delegate = mAtm.getProcessController(appThread);
if (delegate == null) return;
mRemotePlayer.update(delegate, isPlaying, true /* predict */);
}
void abort(Transition transition) {
@@ -666,6 +676,95 @@ class TransitionController {
proto.end(token);
}
/**
* This manages the animating state of processes that are running remote animations for
* {@link #mTransitionPlayerProc}.
*/
static class RemotePlayer {
private static final long REPORT_RUNNING_GRACE_PERIOD_MS = 100;
@GuardedBy("itself")
private final ArrayMap<IBinder, DelegateProcess> mDelegateProcesses = new ArrayMap<>();
private final ActivityTaskManagerService mAtm;
private class DelegateProcess implements Runnable {
final WindowProcessController mProc;
/** Requires {@link RemotePlayer#reportRunning} to confirm it is really running. */
boolean mNeedReport;
DelegateProcess(WindowProcessController proc) {
mProc = proc;
}
/** This runs when the remote player doesn't report running in time. */
@Override
public void run() {
synchronized (mAtm.mGlobalLockWithoutBoost) {
update(mProc, false /* running */, false /* predict */);
}
}
}
RemotePlayer(ActivityTaskManagerService atm) {
mAtm = atm;
}
void update(@NonNull WindowProcessController delegate, boolean running, boolean predict) {
if (!running) {
synchronized (mDelegateProcesses) {
boolean removed = false;
for (int i = mDelegateProcesses.size() - 1; i >= 0; i--) {
if (mDelegateProcesses.valueAt(i).mProc == delegate) {
mDelegateProcesses.removeAt(i);
removed = true;
break;
}
}
if (!removed) return;
}
delegate.setRunningRemoteAnimation(false);
return;
}
if (delegate.isRunningRemoteTransition() || !delegate.hasThread()) return;
delegate.setRunningRemoteAnimation(true);
final DelegateProcess delegateProc = new DelegateProcess(delegate);
// If "predict" is true, that means the remote animation is set from
// ActivityOptions#makeRemoteAnimation(). But it is still up to shell side to decide
// whether to use the remote animation, so there is a timeout to cancel the prediction
// if the remote animation doesn't happen.
if (predict) {
delegateProc.mNeedReport = true;
mAtm.mH.postDelayed(delegateProc, REPORT_RUNNING_GRACE_PERIOD_MS);
}
synchronized (mDelegateProcesses) {
mDelegateProcesses.put(delegate.getThread().asBinder(), delegateProc);
}
}
void clear() {
synchronized (mDelegateProcesses) {
for (int i = mDelegateProcesses.size() - 1; i >= 0; i--) {
mDelegateProcesses.valueAt(i).mProc.setRunningRemoteAnimation(false);
}
mDelegateProcesses.clear();
}
}
/** Returns {@code true} if the app is known to be running remote transition. */
boolean reportRunning(@NonNull IApplicationThread appThread) {
final DelegateProcess delegate;
synchronized (mDelegateProcesses) {
delegate = mDelegateProcesses.get(appThread.asBinder());
if (delegate != null && delegate.mNeedReport) {
// It was predicted to run remote transition. Now it is really requesting so
// remove the timeout of restoration.
delegate.mNeedReport = false;
mAtm.mH.removeCallbacks(delegate);
}
}
return delegate != null;
}
}
static class TransitionMetricsReporter extends ITransitionMetricsReporter.Stub {
private final ArrayMap<IBinder, LongConsumer> mMetricConsumers = new ArrayMap<>();

View File

@@ -55,7 +55,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
import android.app.WindowConfiguration;
import android.content.ActivityNotFoundException;
import android.content.Intent;
@@ -1449,11 +1448,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
synchronized (mGlobalLock) {
final WindowProcessController wpc =
mService.getProcessController(callerPid, callerUid);
IApplicationThread appThread = null;
if (wpc != null) {
appThread = wpc.getThread();
}
mTransitionController.registerTransitionPlayer(player, appThread);
mTransitionController.registerTransitionPlayer(player, wpc);
}
} finally {
Binder.restoreCallingIdentity(ident);

View File

@@ -77,7 +77,6 @@ import com.android.server.wm.ActivityTaskManagerService.HotPath;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -227,10 +226,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
/** Whether our process is currently running a {@link IRemoteAnimationRunner} */
private boolean mRunningRemoteAnimation;
/** List of "chained" processes that are running remote animations for this process */
private final ArrayList<WeakReference<WindowProcessController>> mRemoteAnimationDelegates =
new ArrayList<>();
// The bits used for mActivityStateFlags.
private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
@@ -1663,30 +1658,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
updateRunningRemoteOrRecentsAnimation();
}
/**
* Marks another process as a "delegate" animator. This means that process is doing some part
* of a remote animation on behalf of this process.
*/
void addRemoteAnimationDelegate(WindowProcessController delegate) {
if (!isRunningRemoteTransition()) {
throw new IllegalStateException("Can't add a delegate to a process which isn't itself"
+ " running a remote animation");
}
mRemoteAnimationDelegates.add(new WeakReference<>(delegate));
}
void updateRunningRemoteOrRecentsAnimation() {
if (!isRunningRemoteTransition()) {
// Clean-up any delegates
for (int i = 0; i < mRemoteAnimationDelegates.size(); ++i) {
final WindowProcessController delegate = mRemoteAnimationDelegates.get(i).get();
if (delegate == null) continue;
delegate.setRunningRemoteAnimation(false);
delegate.setRunningRecentsAnimation(false);
}
mRemoteAnimationDelegates.clear();
}
// Posting on handler so WM lock isn't held when we call into AM.
mAtm.mH.sendMessage(PooledLambda.obtainMessage(
WindowProcessListener::setRunningRemoteAnimation, mListener,

View File

@@ -62,9 +62,11 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.SurfaceControl;
import android.window.IDisplayAreaOrganizer;
import android.window.IRemoteTransition;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskOrganizer;
import android.window.ITransitionPlayer;
import android.window.RemoteTransition;
import android.window.TaskFragmentOrganizer;
import android.window.TransitionInfo;
@@ -412,6 +414,38 @@ public class TransitionTests extends WindowTestsBase {
info.getChanges().get(0).getParent());
}
@Test
public void testRunningRemoteTransition() {
final TestTransitionPlayer testPlayer = new TestTransitionPlayer(
mAtm.getTransitionController(), mAtm.mWindowOrganizerController);
final WindowProcessController playerProc = mSystemServicesTestRule.addProcess(
"pkg.player", "proc.player", 5000 /* pid */, 5000 /* uid */);
testPlayer.mController.registerTransitionPlayer(testPlayer, playerProc);
doReturn(mock(IBinder.class)).when(playerProc.getThread()).asBinder();
final WindowProcessController delegateProc = mSystemServicesTestRule.addProcess(
"pkg.delegate", "proc.delegate", 6000 /* pid */, 6000 /* uid */);
doReturn(mock(IBinder.class)).when(delegateProc.getThread()).asBinder();
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
final TransitionController controller = app.mTransitionController;
final Transition transition = controller.createTransition(TRANSIT_OPEN);
final RemoteTransition remoteTransition = new RemoteTransition(
mock(IRemoteTransition.class));
remoteTransition.setAppThread(delegateProc.getThread());
transition.collectExistenceChange(app.getTask());
controller.requestStartTransition(transition, app.getTask(), remoteTransition,
null /* displayChange */);
testPlayer.startTransition();
testPlayer.onTransactionReady(app.getSyncTransaction());
assertTrue(playerProc.isRunningRemoteTransition());
assertTrue(delegateProc.isRunningRemoteTransition());
assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
testPlayer.finish();
assertFalse(playerProc.isRunningRemoteTransition());
assertFalse(delegateProc.isRunningRemoteTransition());
assertFalse(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
}
@Test
public void testOpenActivityInTheSameTaskWithDisplayChange() {
final ActivityRecord closing = createActivityRecord(mDisplayContent);
@@ -861,7 +895,7 @@ public class TransitionTests extends WindowTestsBase {
final TransitionController controller = new TransitionController(mAtm, snapshotController,
mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
controller.registerTransitionPlayer(player, null /* playerProc */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
// Start out with task2 visible and set up a transition that closes task2 and opens task1
@@ -926,7 +960,7 @@ public class TransitionTests extends WindowTestsBase {
final TransitionController controller = new TransitionController(mAtm, snapshotController,
mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
controller.registerTransitionPlayer(player, null /* playerProc */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
// Start out with task2 visible and set up a transition that closes task2 and opens task1
@@ -990,7 +1024,7 @@ public class TransitionTests extends WindowTestsBase {
final TransitionController controller = new TransitionController(mAtm, snapshotController,
mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
controller.registerTransitionPlayer(player, null /* playerProc */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
// Start out with task2 visible and set up a transition that closes task2 and opens task1

View File

@@ -830,7 +830,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
TestTransitionPlayer registerTestTransitionPlayer() {
final TestTransitionPlayer testPlayer = new TestTransitionPlayer(
mAtm.getTransitionController(), mAtm.mWindowOrganizerController);
testPlayer.mController.registerTransitionPlayer(testPlayer, null /* appThread */);
testPlayer.mController.registerTransitionPlayer(testPlayer, null /* playerProc */);
return testPlayer;
}