Add error handling for start task and intent

If user start task and intent with same app but not support
multi-instance, in this case it will only start one side split and
the other one show as black screen.
We should avoid such status and do some error handling, this patch
prevent it by exiting split immediately and expand the that app as
full screen.

Fix: 224888879
Test: manual
Test: pass existing tests
Change-Id: I827debb01f3a2fc8f04f4b1931084f2fa475f8a6
This commit is contained in:
Tony Huang
2022-03-28 18:45:09 +08:00
parent 43f42ced37
commit 4ae839860f
5 changed files with 43 additions and 19 deletions

View File

@@ -190,7 +190,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider);
mIconProvider, mMainExecutor, mRecentTasksOptional, mUnfoldControllerProvider);
}
}

View File

@@ -94,6 +94,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
@@ -157,6 +158,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private final TransactionPool mTransactionPool;
private final SplitScreenTransitions mSplitTransitions;
private final SplitscreenEventLogger mLogger;
private final ShellExecutor mMainExecutor;
private final Optional<RecentTasksController> mRecentTasks;
/**
@@ -196,13 +198,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, SplitscreenEventLogger logger,
IconProvider iconProvider, Optional<RecentTasksController> recentTasks,
IconProvider iconProvider, ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
mTaskOrganizer = taskOrganizer;
mLogger = logger;
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
@@ -247,7 +251,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayController displayController, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger,
SplitscreenEventLogger logger, ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mContext = context;
@@ -266,6 +270,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mLogger = logger;
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
mDisplayController.addDisplayWindowListener(this);
mDisplayLayout = new DisplayLayout();
@@ -394,6 +399,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Nullable PendingIntent pendingIntent, @Nullable Intent fillInIntent,
@Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
final boolean withIntent = pendingIntent != null && fillInIntent != null;
// Init divider first to make divider leash for remote animation target.
mSplitLayout.init();
// Set false to avoid record new bounds with old task still on top;
@@ -423,10 +429,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
mSyncQueue.queue(evictWct);
mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
onRemoteAnimationFinishedOrCancelled(evictWct);
finishedCallback.onAnimationFinished();
}
};
@@ -447,10 +450,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onAnimationCancelled() {
mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
mSyncQueue.queue(evictWct);
mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
onRemoteAnimationFinishedOrCancelled(evictWct);
try {
adapter.getRunner().onAnimationCancelled();
} catch (RemoteException e) {
@@ -486,20 +486,37 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
addActivityOptions(sideOptions, mSideStage);
// Add task launch requests
if (pendingIntent != null && fillInIntent != null) {
wct.startTask(mainTaskId, mainOptions);
wct.startTask(mainTaskId, mainOptions);
if (withIntent) {
wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
} else {
wct.startTask(mainTaskId, mainOptions);
wct.startTask(sideTaskId, sideOptions);
}
// Using legacy transitions, so we can't use blast sync since it conflicts.
mTaskOrganizer.applyTransaction(wct);
mSyncQueue.runInSync(t ->
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
}
private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) {
mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
// If any stage has no child after animation finished, it means that split will display
// nothing, such status will happen if task and intent is same app but not support
// multi-instagce, we should exit split and expand that app as full screen.
if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
mMainExecutor.execute(() ->
exitSplitScreen(mMainStage.getChildCount() == 0
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
} else {
mSyncQueue.queue(evictWct);
mSyncQueue.runInSync(t -> {
setDividerVisibility(true, t);
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
});
}
}
/**
* Collects all the current child tasks of a specific split and prepares transaction to evict
* them to display.

View File

@@ -31,6 +31,7 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
@@ -72,11 +73,13 @@ public class SplitTestUtils {
DisplayController displayController, DisplayImeController imeController,
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger, Optional<RecentTasksController> recentTasks,
SplitscreenEventLogger logger, ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> unfoldController) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
transitions, transactionPool, logger, recentTasks, unfoldController);
transitions, transactionPool, logger, mainExecutor, recentTasks,
unfoldController);
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();

View File

@@ -97,6 +97,7 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private SurfaceSession mSurfaceSession;
@Mock private SplitscreenEventLogger mLogger;
@Mock private IconProvider mIconProvider;
@Mock private ShellExecutor mMainExecutor;
private SplitLayout mSplitLayout;
private MainStage mMainStage;
private SideStage mSideStage;
@@ -126,7 +127,7 @@ public class SplitTransitionTests extends ShellTestCase {
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
mTransactionPool, mLogger, Optional.empty(), Optional::empty);
mTransactionPool, mLogger, mMainExecutor, Optional.empty(), Optional::empty);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());

View File

@@ -54,6 +54,7 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
@@ -101,6 +102,8 @@ public class StageCoordinatorTests extends ShellTestCase {
private TransactionPool mTransactionPool;
@Mock
private SplitscreenEventLogger mLogger;
@Mock
private ShellExecutor mMainExecutor;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -116,7 +119,7 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mLogger,
Optional.empty(), new UnfoldControllerProvider()));
mMainExecutor, Optional.empty(), new UnfoldControllerProvider()));
doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);