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

Commit eb66a7e4 authored by Winson Chung's avatar Winson Chung
Browse files

Ensure that TaskView callbacks are made on the ui thread for the view

- In cases where the TaskView are not created on the shell main thread,
  the callbacks on the view may be made on the wrong thread leading to
  an error or the a race between usage/cleanup of the surfaces used by
  the surface view.

Fixes: 292241747
Fixes: 300870297

Test: atest TaskViewTest
Test: atest PanelTaskViewControllerTest
Test: atest ControlsUiControllerImplTest
Change-Id: I43bbd9d8089d0ab6e6bd10dd5cb4072f0029d8dc
parent dd593baa
Loading
Loading
Loading
Loading
+33 −3
Original line number Diff line number Diff line
@@ -29,12 +29,16 @@ import android.content.pm.ShortcutInfo;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler;
import android.os.Looper;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewTreeObserver;

import com.android.internal.annotations.VisibleForTesting;

import java.util.concurrent.Executor;

/**
@@ -74,6 +78,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
    private final TaskViewTaskController mTaskViewTaskController;
    private Region mObscuredTouchRegion;
    private Insets mCaptionInsets;
    private Handler mHandler;

    public TaskView(Context context, TaskViewTaskController taskViewTaskController) {
        super(context, null, 0, 0, true /* disableBackgroundLayer */);
@@ -81,6 +86,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
        // TODO(b/266736992): Think about a better way to set the TaskViewBase on the
        //  TaskViewTaskController and vice-versa
        mTaskViewTaskController.setTaskViewBase(this);
        mHandler = Handler.getMain();
        getHolder().addCallback(this);
    }

@@ -117,14 +123,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
    public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
        onLocationChanged();
        if (taskInfo.taskDescription != null) {
            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
            final int bgColor = taskInfo.taskDescription.getBackgroundColor();
            runOnViewThread(() -> setResizeBackgroundColor(bgColor));
        }
    }

    @Override
    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
        if (taskInfo.taskDescription != null) {
            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
            final int bgColor = taskInfo.taskDescription.getBackgroundColor();
            runOnViewThread(() -> setResizeBackgroundColor(bgColor));
        }
    }

@@ -143,7 +151,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,

    @Override
    public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) {
        setResizeBackgroundColor(t, bgColor);
        runOnViewThread(() -> setResizeBackgroundColor(t, bgColor));
    }

    /**
@@ -272,12 +280,14 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        getViewTreeObserver().addOnComputeInternalInsetsListener(this);
        mHandler = getHandler();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
        mHandler = Handler.getMain();
    }

    /** Returns the task info for the task in the TaskView. */
@@ -285,4 +295,24 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
    public ActivityManager.RunningTaskInfo getTaskInfo() {
        return mTaskViewTaskController.getTaskInfo();
    }

    /**
     * Sets the handler, only for testing.
     */
    @VisibleForTesting
    void setHandler(Handler viewHandler) {
        mHandler = viewHandler;
    }

    /**
     * Ensures that the given runnable runs on the view's thread.
     */
    private void runOnViewThread(Runnable r) {
        if (mHandler.getLooper().isCurrentThread()) {
            r.run();
        } else {
            // If this call is not from the same thread as the view, then post it
            mHandler.post(r);
        }
    }
}
+22 −0
Original line number Diff line number Diff line
@@ -43,6 +43,8 @@ import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
@@ -88,6 +90,10 @@ public class TaskViewTest extends ShellTestCase {
    SyncTransactionQueue mSyncQueue;
    @Mock
    Transitions mTransitions;
    @Mock
    Looper mViewLooper;
    @Mock
    Handler mViewHandler;

    SurfaceSession mSession;
    SurfaceControl mLeash;
@@ -105,6 +111,8 @@ public class TaskViewTest extends ShellTestCase {
                .build();

        mContext = getContext();
        doReturn(true).when(mViewLooper).isCurrentThread();
        doReturn(mViewLooper).when(mViewHandler).getLooper();

        mTaskInfo = new ActivityManager.RunningTaskInfo();
        mTaskInfo.token = mToken;
@@ -132,6 +140,7 @@ public class TaskViewTest extends ShellTestCase {
        mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer,
                mTaskViewTransitions, mSyncQueue));
        mTaskView = new TaskView(mContext, mTaskViewTaskController);
        mTaskView.setHandler(mViewHandler);
        mTaskView.setListener(mExecutor, mViewListener);
    }

@@ -646,4 +655,17 @@ public class TaskViewTest extends ShellTestCase {

        assertThat(mTaskViewTaskController.getTaskInfo()).isNull();
    }

    @Test
    public void testOnTaskInfoChangedOnSameUiThread() {
        mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
        verify(mViewHandler, never()).post(any());
    }

    @Test
    public void testOnTaskInfoChangedOnDifferentUiThread() {
        doReturn(false).when(mViewLooper).isCurrentThread();
        mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
        verify(mViewHandler).post(any());
    }
}