Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a78cc84 authored by Winson Chung's avatar Winson Chung Committed by Android Build Coastguard Worker
Browse files

Fix issue with incorrect drag starter being used and handle removed tasks

- This CL has two parts:
  1) We were always using the split starter to start tasks, but if the
     top-task when the drag started is removed, then it will incorrectly
     attempt to bring forward another task to split with, or if there
     are no other tasks, it will leave the device in a broken split
     state
  2) In order to handle 1), we need to update the drag session and
     layout when tasks are removed while a drag is active.  We do this
     by registering a listener on the organizer (it doesn't need to be
     synchronized with the removal, just notified), and then we
     recalculate the running task to determine whether dropping should
     launch in fullscreen or split

Flag: EXEMPT bugfix
Bug: 346694675
Test: atest WMShellUnitTests
Test: Start a drag, kill the app behind and ensure that the drag can
      still continue without ending up in a broken split state
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f8e279dc91595335a006763281749a15724140de)
Merged-In: I7009389102005e20563973b48143488e86628715
Change-Id: I7009389102005e20563973b48143488e86628715
parent 4253ae01
Loading
Loading
Loading
Loading
+35 −2
Original line number Diff line number Diff line
@@ -123,6 +123,15 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
        default void dump(@NonNull PrintWriter pw, String prefix) {};
    }

    /**
     * Limited scope callback to notify when a task is removed from the system.  This signal is
     * not synchronized with anything (or any transition), and should not be used in cases where
     * that is necessary.
     */
    public interface TaskVanishedListener {
        default void onTaskVanished(RunningTaskInfo taskInfo) {}
    }

    /**
     * Callbacks for events on a task with a locus id.
     */
@@ -167,6 +176,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements

    private final ArraySet<FocusListener> mFocusListeners = new ArraySet<>();

    // Listeners that should be notified when a task is removed
    private final ArraySet<TaskVanishedListener> mTaskVanishedListeners = new ArraySet<>();

    private final Object mLock = new Object();
    private StartingWindowController mStartingWindow;

@@ -409,7 +421,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
    }

    /**
     * Removes listener.
     * Removes a locus id listener.
     */
    public void removeLocusIdListener(LocusIdListener listener) {
        synchronized (mLock) {
@@ -430,7 +442,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
    }

    /**
     * Removes listener.
     * Removes a focus listener.
     */
    public void removeFocusListener(FocusListener listener) {
        synchronized (mLock) {
@@ -438,6 +450,24 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
        }
    }

    /**
     * Adds a listener to be notified when a task vanishes.
     */
    public void addTaskVanishedListener(TaskVanishedListener listener) {
        synchronized (mLock) {
            mTaskVanishedListeners.add(listener);
        }
    }

    /**
     * Removes a task-vanished listener.
     */
    public void removeTaskVanishedListener(TaskVanishedListener listener) {
        synchronized (mLock) {
            mTaskVanishedListeners.remove(listener);
        }
    }

    /**
     * Returns a surface which can be used to attach overlays to the home root task
     */
@@ -614,6 +644,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
                t.apply();
                ProtoLog.v(WM_SHELL_TASK_ORG, "Removing overlay surface");
            }
            for (TaskVanishedListener l : mTaskVanishedListeners) {
                l.onTaskVanished(taskInfo);
            }

            if (!ENABLE_SHELL_TRANSITIONS && (appearedInfo.getLeash() != null)) {
                // Preemptively clean up the leash only if shell transitions are not enabled
+3 −2
Original line number Diff line number Diff line
@@ -642,6 +642,7 @@ public abstract class WMShellModule {
            ShellInit shellInit,
            ShellController shellController,
            ShellCommandHandler shellCommandHandler,
            ShellTaskOrganizer shellTaskOrganizer,
            DisplayController displayController,
            UiEventLogger uiEventLogger,
            IconProvider iconProvider,
@@ -649,8 +650,8 @@ public abstract class WMShellModule {
            Transitions transitions,
            @ShellMainThread ShellExecutor mainExecutor) {
        return new DragAndDropController(context, shellInit, shellController, shellCommandHandler,
                displayController, uiEventLogger, iconProvider, globalDragListener, transitions,
                mainExecutor);
                shellTaskOrganizer, displayController, uiEventLogger, iconProvider,
                globalDragListener, transitions, mainExecutor);
    }

    //
+35 −2
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
@@ -85,6 +86,7 @@ import java.util.function.Function;
public class DragAndDropController implements RemoteCallable<DragAndDropController>,
        GlobalDragListener.GlobalDragListenerCallback,
        DisplayController.OnDisplaysChangedListener,
        ShellTaskOrganizer.TaskVanishedListener,
        View.OnDragListener, ComponentCallbacks2 {

    private static final String TAG = DragAndDropController.class.getSimpleName();
@@ -92,6 +94,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
    private final Context mContext;
    private final ShellController mShellController;
    private final ShellCommandHandler mShellCommandHandler;
    private final ShellTaskOrganizer mShellTaskOrganizer;
    private final DisplayController mDisplayController;
    private final DragAndDropEventLogger mLogger;
    private final IconProvider mIconProvider;
@@ -133,6 +136,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
            ShellInit shellInit,
            ShellController shellController,
            ShellCommandHandler shellCommandHandler,
            ShellTaskOrganizer shellTaskOrganizer,
            DisplayController displayController,
            UiEventLogger uiEventLogger,
            IconProvider iconProvider,
@@ -142,6 +146,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
        mContext = context;
        mShellController = shellController;
        mShellCommandHandler = shellCommandHandler;
        mShellTaskOrganizer = shellTaskOrganizer;
        mDisplayController = displayController;
        mLogger = new DragAndDropEventLogger(uiEventLogger);
        mIconProvider = iconProvider;
@@ -163,6 +168,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
        }, 0);
        mShellController.addExternalInterface(KEY_EXTRA_SHELL_DRAG_AND_DROP,
                this::createExternalInterface, this);
        mShellTaskOrganizer.addTaskVanishedListener(this);
        mShellCommandHandler.addDumpCallback(this::dump, this);
        mGlobalDragListener.setListener(this);
    }
@@ -280,6 +286,34 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
        mDisplayDropTargets.remove(displayId);
    }

    @Override
    public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
        if (taskInfo.baseIntent == null) {
            // Invalid info
            return;
        }
        // Find the active drag
        PerDisplay pd = null;
        for (int i = 0; i < mDisplayDropTargets.size(); i++) {
            final PerDisplay iPd = mDisplayDropTargets.valueAt(i);
            if (iPd.isHandlingDrag) {
                pd = iPd;
                break;
            }
        }
        if (pd == null || !pd.isHandlingDrag) {
            // Not currently dragging
            return;
        }

        // Update the drag session
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
                "Handling vanished task: id=%d component=%s", taskInfo.taskId,
                taskInfo.baseIntent.getComponent());
        pd.dragSession.updateRunningTask();
        pd.dragLayout.updateSession(pd.dragSession);
    }

    @Override
    public boolean onDrag(View target, DragEvent event) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
@@ -313,11 +347,10 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
                    Slog.w(TAG, "Unexpected drag start during an active drag");
                    return false;
                }
                // TODO(b/290391688): Also update the session data with task stack changes
                pd.dragSession = new DragSession(ActivityTaskManager.getInstance(),
                        mDisplayController.getDisplayLayout(displayId), event.getClipData(),
                        event.getDragFlags());
                pd.dragSession.update();
                pd.dragSession.initialize();
                pd.activeDragCount++;
                pd.dragLayout.prepare(pd.dragSession, mLogger.logStart(pd.dragSession));
                setDropTargetWindowVisibility(pd, View.VISIBLE);
+20 −12
Original line number Diff line number Diff line
@@ -84,7 +84,10 @@ public class DragAndDropPolicy {
    private static final String TAG = DragAndDropPolicy.class.getSimpleName();

    private final Context mContext;
    private final Starter mStarter;
    // Used only for launching a fullscreen task (or as a fallback if there is no split starter)
    private final Starter mFullscreenStarter;
    // Used for launching tasks into splitscreen
    private final Starter mSplitscreenStarter;
    private final SplitScreenController mSplitScreen;
    private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
    private final RectF mDisallowHitRegion = new RectF();
@@ -97,10 +100,12 @@ public class DragAndDropPolicy {
    }

    @VisibleForTesting
    DragAndDropPolicy(Context context, SplitScreenController splitScreen, Starter starter) {
    DragAndDropPolicy(Context context, SplitScreenController splitScreen,
            Starter fullscreenStarter) {
        mContext = context;
        mSplitScreen = splitScreen;
        mStarter = mSplitScreen != null ? mSplitScreen : starter;
        mFullscreenStarter = fullscreenStarter;
        mSplitscreenStarter = splitScreen;
    }

    /**
@@ -245,17 +250,20 @@ public class DragAndDropPolicy {
            mSplitScreen.onDroppedToSplit(position, mLoggerSessionId);
        }

        final Starter starter = target.type == TYPE_FULLSCREEN
                ? mFullscreenStarter
                : mSplitscreenStarter;
        if (mSession.appData != null) {
            launchApp(mSession, position);
            launchApp(mSession, starter, position);
        } else {
            launchIntent(mSession, position);
            launchIntent(mSession, starter, position);
        }
    }

    /**
     * Launches an app provided by SysUI.
     */
    private void launchApp(DragSession session, @SplitPosition int position) {
    private void launchApp(DragSession session, Starter starter, @SplitPosition int position) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching app data at position=%d",
                position);
        final ClipDescription description = session.getClipDescription();
@@ -275,11 +283,11 @@ public class DragAndDropPolicy {

        if (isTask) {
            final int taskId = session.appData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
            mStarter.startTask(taskId, position, opts);
            starter.startTask(taskId, position, opts);
        } else if (isShortcut) {
            final String packageName = session.appData.getStringExtra(EXTRA_PACKAGE_NAME);
            final String id = session.appData.getStringExtra(EXTRA_SHORTCUT_ID);
            mStarter.startShortcut(packageName, id, position, opts, user);
            starter.startShortcut(packageName, id, position, opts, user);
        } else {
            final PendingIntent launchIntent =
                    session.appData.getParcelableExtra(EXTRA_PENDING_INTENT);
@@ -288,7 +296,7 @@ public class DragAndDropPolicy {
                    Log.e(TAG, "Expected app intent's EXTRA_USER to match pending intent user");
                }
            }
            mStarter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */,
            starter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */,
                    position, opts);
        }
    }
@@ -296,7 +304,7 @@ public class DragAndDropPolicy {
    /**
     * Launches an intent sender provided by an application.
     */
    private void launchIntent(DragSession session, @SplitPosition int position) {
    private void launchIntent(DragSession session, Starter starter, @SplitPosition int position) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching intent at position=%d",
                position);
        final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic();
@@ -309,7 +317,7 @@ public class DragAndDropPolicy {
                | FLAG_ACTIVITY_MULTIPLE_TASK);

        final Bundle opts = baseActivityOpts.toBundle();
        mStarter.startIntent(session.launchableIntent,
        starter.startIntent(session.launchableIntent,
                session.launchableIntent.getCreatorUserHandle().getIdentifier(),
                null /* fillIntent */, position, opts);
    }
@@ -420,7 +428,7 @@ public class DragAndDropPolicy {

        @Override
        public String toString() {
            return "Target {hit=" + hitRegion + " draw=" + drawRegion + "}";
            return "Target {type=" + type + " hit=" + hitRegion + " draw=" + drawRegion + "}";
        }
    }
}
+30 −2
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
@@ -102,6 +103,8 @@ public class DragLayout extends LinearLayout
    private boolean mIsShowing;
    private boolean mHasDropped;
    private DragSession mSession;
    // The last position that was handled by the drag layout
    private final Point mLastPosition = new Point();

    @SuppressLint("WrongConstant")
    public DragLayout(Context context, SplitScreenController splitScreenController,
@@ -265,6 +268,15 @@ public class DragLayout extends LinearLayout
     */
    public void prepare(DragSession session, InstanceId loggerSessionId) {
        mPolicy.start(session, loggerSessionId);
        updateSession(session);
    }

    /**
     * Updates the drag layout based on the diven drag session.
     */
    public void updateSession(DragSession session) {
        // Note: The policy currently just keeps a reference to the session
        boolean updatingExistingSession = mSession != null;
        mSession = session;
        mHasDropped = false;
        mCurrentTarget = null;
@@ -312,6 +324,11 @@ public class DragLayout extends LinearLayout
            updateDropZoneSizes(topOrLeftBounds, bottomOrRightBounds);
        }
        requestLayout();
        if (updatingExistingSession) {
            // Update targets if we are already currently dragging
            recomputeDropTargets();
            update(mLastPosition.x, mLastPosition.y);
        }
    }

    private void updateDropZoneSizesForSingleTask() {
@@ -359,6 +376,9 @@ public class DragLayout extends LinearLayout
        mDropZoneView2.setLayoutParams(dropZoneView2);
    }

    /**
     * Shows the drag layout.
     */
    public void show() {
        mIsShowing = true;
        recomputeDropTargets();
@@ -384,13 +404,19 @@ public class DragLayout extends LinearLayout
     * Updates the visible drop target as the user drags.
     */
    public void update(DragEvent event) {
        update((int) event.getX(), (int) event.getY());
    }

    /**
     * Updates the visible drop target as the user drags to the given coordinates.
     */
    private void update(int x, int y) {
        if (mHasDropped) {
            return;
        }
        // Find containing region, if the same as mCurrentRegion, then skip, otherwise, animate the
        // visibility of the current region
        DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation(
                (int) event.getX(), (int) event.getY());
        DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation(x, y);
        if (mCurrentTarget != target) {
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Current target: %s", target);
            if (target == null) {
@@ -429,6 +455,7 @@ public class DragLayout extends LinearLayout
            }
            mCurrentTarget = target;
        }
        mLastPosition.set(x, y);
    }

    /**
@@ -436,6 +463,7 @@ public class DragLayout extends LinearLayout
     */
    public void hide(DragEvent event, Runnable hideCompleteCallback) {
        mIsShowing = false;
        mLastPosition.set(-1, -1);
        animateSplitContainers(false, () -> {
            if (hideCompleteCallback != null) {
                hideCompleteCallback.run();
Loading