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

Commit a8b00124 authored by Lingyu Feng's avatar Lingyu Feng
Browse files

Move error dialogs to default display when connected display is switched

to mirroring

When mirroring starts on the connected displays, tasks are reparented to
the default display. However, if any app error dialogs are showing on
the connected display, those dialogs will persist there, and mirroring
cannot start correctly.

This CL removes AppErrorDialog, AppNotRespondingDialog,
StrictModeViolationDialog and AppWaitingForDebuggerDialog from the
display when mirroring starts, and also shows a corresponding new one on
the default display if one doesn't already exist there.

Bug: 389833364
Test: manually
Flag: com.android.server.display.feature.flags.enable_display_content_mode_management
Change-Id: I3cb95e0f8727a540b393ea0bb3acf9fa67bec605
parent 0efc9694
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -696,6 +696,17 @@ public abstract class ActivityManagerInternal {
     */
    public abstract void rescheduleAnrDialog(Object data);

    /**
     * Move all the error dialogs (including {@code com.android.server.am.AppErrorDialog},
     * {@code com.android.server.am.AppNotRespondingDialog},
     * {@code com.android.server.am.StrictModeViolationDialog},
     * and {@code com.android.server.am.AppWaitingForDebuggerDialog}) to the default display.
     *
     * @param displayId The display id of the display where the error dialogs are showing and need
     *                  to be moved.
     */
    public abstract void moveErrorDialogsToDefaultDisplay(int displayId);

    /**
     * Sends {@link android.content.Intent#ACTION_CONFIGURATION_CHANGED} with all the appropriate
     * flags.
+16 −0
Original line number Diff line number Diff line
@@ -17428,6 +17428,22 @@ public class ActivityManagerService extends IActivityManager.Stub
            mUiHandler.sendMessageDelayed(msg, InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
        }
        @Override
        public void moveErrorDialogsToDefaultDisplay(int displayId) {
            mUiHandler.post(() -> {
                synchronized (mProcLock) {
                    mProcessList.forEachLruProcessesLOSP(false, app -> {
                        if (app.getThread() == null) {
                            return;
                        }
                        ErrorDialogController controller = app.mErrorState.getDialogController();
                        controller.moveAllErrorDialogsToDefaultDisplay(displayId);
                    });
                }
            });
        }
        @Override
        public void broadcastGlobalConfigurationChanged(int changes, boolean initLocale) {
            synchronized (ActivityManagerService.this) {
+11 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen

    private final ActivityManagerService mService;
    private final ActivityManagerGlobalLock mProcLock;
    private final Data mData;
    private final AppErrorResult mResult;
    private final ProcessRecord mProc;
    private final boolean mIsRestartable;
@@ -62,6 +63,7 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen

        mService = service;
        mProcLock = service.mProcLock;
        mData = data;
        mProc = data.proc;
        mResult = data.result;
        mIsRestartable = (data.taskId != INVALID_TASK_ID || data.isRestartableForService)
@@ -151,6 +153,15 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen
        super.dismiss();
    }

    void remove() {
        // Only dismiss the dialog to remove the window from the display, do not set result.
        super.dismiss();
    }

    Data getData() {
        return mData;
    }

    private void setResult(int result) {
        synchronized (mProcLock) {
            if (mProc != null) {
+4 −0
Original line number Diff line number Diff line
@@ -137,6 +137,10 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli
        }
    }

    Data getData() {
        return mData;
    }

    private final Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            Intent appErrorIntent = null;
+164 −0
Original line number Diff line number Diff line
@@ -16,10 +16,12 @@

package com.android.server.am;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AnrController;
import android.app.Dialog;
import android.content.Context;
import android.view.Display;

import com.android.internal.annotations.GuardedBy;

@@ -104,6 +106,22 @@ final class ErrorDialogController {
        clearWaitingDialog();
    }

    /**
     * If there are any error dialogs on the display, close them and show a corresponding new one on
     * the default display if one doesn't already exist there.
     */
    @GuardedBy("mProcLock")
    void moveAllErrorDialogsToDefaultDisplay(int displayId) {
        if (displayId == Display.DEFAULT_DISPLAY) {
            return;
        }

        moveCrashDialogToDefaultDisplay(displayId);
        moveAnrDialogToDefaultDisplay(displayId);
        moveViolationDialogToDefaultDisplay(displayId);
        moveWaitingDialogToDefaultDisplay(displayId);
    }

    @GuardedBy("mProcLock")
    void clearCrashDialogs() {
        clearCrashDialogs(true /* needDismiss */);
@@ -236,6 +254,152 @@ final class ErrorDialogController {
        mAnrController = controller;
    }

    @GuardedBy("mProcLock")
    @Nullable
    private <T extends BaseErrorDialog> T findDialogOnDisplay(@NonNull List<T> dialogs,
            int displayId) {
        for (int i = dialogs.size() - 1; i >= 0; i--) {
            T dialog = dialogs.get(i);
            if (dialog == null) {
                continue;
            }

            if (dialog.getContext().getDisplayId() == displayId && dialog.isShowing()) {
                return dialog;
            }
        }
        return null;
    }

    /**
     * If a crash dialog is showing on the display, close it and show a new one on the default
     * display if one doesn't already exist there.
     *
     * @param displayId The display id of the display where the crash dialog is showing and needs to
     *                  be moved.
     */
    @GuardedBy("mProcLock")
    private void  moveCrashDialogToDefaultDisplay(int displayId) {
        if (!hasCrashDialogs()) {
            return;
        }

        AppErrorDialog dialogToRemoved = findDialogOnDisplay(mCrashDialogs, displayId);
        if (dialogToRemoved == null) {
            return;
        }
        AppErrorDialog.Data data = dialogToRemoved.getData();
        mCrashDialogs.remove(dialogToRemoved);
        mService.mUiHandler.post(dialogToRemoved::remove);

        boolean showingInDefaultDisplay =
                (findDialogOnDisplay(mCrashDialogs, Display.DEFAULT_DISPLAY) != null);
        if (showingInDefaultDisplay || data == null) {
            return;
        }

        // If there is no crash dialog showing in the default display, show a new one.
        // TODO(b/412589019): Use WindowContext instead.
        Context c = mService.mWmInternal.getDisplayUiContext(Display.DEFAULT_DISPLAY);
        AppErrorDialog newDialog = new AppErrorDialog(c, mService, data);
        mCrashDialogs.add(newDialog);
        mService.mUiHandler.post(newDialog::show);
    }

    /**
     * If a ANR dialog is showing on the display, close it and show a new one on the default display
     * if one doesn't already exist there.
     *
     * @param displayId The display id of the display where the ANR dialog is showing and needs to
     *                  be moved.
     */
    @GuardedBy("mProcLock")
    private void moveAnrDialogToDefaultDisplay(int displayId) {
        if (!hasAnrDialogs()) {
            return;
        }

        AppNotRespondingDialog dialogToRemoved = findDialogOnDisplay(mAnrDialogs, displayId);
        if (dialogToRemoved == null) {
            return;
        }
        AppNotRespondingDialog.Data data = dialogToRemoved.getData();
        mAnrDialogs.remove(dialogToRemoved);
        mService.mUiHandler.post(dialogToRemoved::dismiss);

        boolean showingInDefaultDisplay =
                (findDialogOnDisplay(mAnrDialogs, Display.DEFAULT_DISPLAY) != null);
        if (showingInDefaultDisplay || data == null) {
            return;
        }

        // If there is no ANR dialog showing in the default display, show a new one.
        // TODO(b/412589019): Use WindowContext instead.
        Context c = mService.mWmInternal.getDisplayUiContext(Display.DEFAULT_DISPLAY);
        AppNotRespondingDialog newDialog = new AppNotRespondingDialog(mService, c, data);
        mAnrDialogs.add(newDialog);
        mService.mUiHandler.post(newDialog::show);
    }

    /**
     * If a strict mode violation dialog is showing on the display, close it and show a new one on
     * the default display if one doesn't already exist there.
     *
     * @param displayId The display id of the display where the violation dialog is showing and
     *                  needs to be moved.
     */
    @GuardedBy("mProcLock")
    private void moveViolationDialogToDefaultDisplay(int displayId) {
        if (!hasViolationDialogs()) {
            return;
        }

        StrictModeViolationDialog dialogToRemoved =
                findDialogOnDisplay(mViolationDialogs, displayId);
        if (dialogToRemoved == null) {
            return;
        }
        AppErrorResult result = dialogToRemoved.getResult();
        mViolationDialogs.remove(dialogToRemoved);
        mService.mUiHandler.post(dialogToRemoved::dismiss);

        boolean showingInDefaultDisplay =
                (findDialogOnDisplay(mViolationDialogs, Display.DEFAULT_DISPLAY) != null);
        if (showingInDefaultDisplay || result == null) {
            return;
        }

        // If there is no violation dialog showing in the default display, show a new one.
        // TODO(b/412589019): Use WindowContext instead.
        Context c = mService.mWmInternal.getDisplayUiContext(Display.DEFAULT_DISPLAY);
        StrictModeViolationDialog newDialog =
                new StrictModeViolationDialog(c, mService, result, mApp);
        mViolationDialogs.add(newDialog);
        mService.mUiHandler.post(newDialog::show);
    }

    /**
     * If a debug waiting dialog is showing on the display, close it and show a new one on the
     * default display if one doesn't already exist there.
     *
     * @param displayId The display id of the display where the waiting dialog is showing and needs
     *                  to be moved.
     */
    @GuardedBy("mProcLock")
    private void moveWaitingDialogToDefaultDisplay(int displayId) {
        if (mWaitDialog == null || mWaitDialog.getContext().getDisplayId() != displayId) {
            return;
        }

        // The debug waiting dialog is showing on the display, remove this and show a new one on the
        // default display.
        mWaitDialog.dismiss();
        Context c = mService.mWmInternal.getDisplayUiContext(Display.DEFAULT_DISPLAY);
        mWaitDialog = new AppWaitingForDebuggerDialog(mService, c, mApp);
        mService.mUiHandler.post(mWaitDialog::show);
    }


    /**
     * Helper function to collect contexts from crashed app located displays.
     *
Loading