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

Commit 79aa93f4 authored by Dohyun Lee's avatar Dohyun Lee Committed by BK Choi
Browse files

Show error dialogs(ANR, crash) for the visible background users



In a car environment, error dialogs are not displayed by default when
an ANR or a crash occurs. However, this can confuse the passenger users
and leave them uninformed when an app is terminated by an ANR or a crash
without any notification. To address this, error dialogs are allowed for
the passenger users who have UI access on assigned displays
(a.k.a. visible background users) on devices that have
config_multiuserVisibleBackgroundUsers enabled.
Accordingly, this patch fixes
CtsAppTestCases:ActivityManagerTest#testGetProcessInErrorState fail for
visible background users.

Bug: 342315883
Flag: EXEMPT bugfix (does not affect phones)
Test: manually verified that error dialogs are shown when an ANR or a
crash occurs for the visible background user.
Test: atest --user-type secondary_user_on_secondary_display CtsAppTestCases:ActivityManagerTest#testGetProcessInErrorState
Test: atest CtsAppTestCases:ActivityManagerTest#testGetProcessInErrorState
Test: atest ActivityTaskManagerServiceTests

Signed-off-by: default avatarDohyun Lee <dohyun.lee@lge.com>
(cherry picked from https://partner-android-review.git.corp.google.com/c/platform/frameworks/base/+/2837587

)

Change-Id: If0733679d2fb8622634568827d7238aab43df800
Signed-off-by: default avatarDohyun Lee <dohyun.lee@lge.com>
parent 05d1d08f
Loading
Loading
Loading
Loading
+51 −11
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ import android.os.Message;
import android.os.Process;
import android.os.Process;
import android.os.SystemClock;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ArraySet;
@@ -63,6 +64,8 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto;
import com.android.server.LocalServices;
import com.android.server.LocalServices;
import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.wm.WindowProcessController;
import com.android.server.wm.WindowProcessController;


@@ -868,9 +871,6 @@ class AppErrors {
    private boolean handleAppCrashLSPB(ProcessRecord app, String reason,
    private boolean handleAppCrashLSPB(ProcessRecord app, String reason,
            String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
            String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
        final long now = SystemClock.uptimeMillis();
        final long now = SystemClock.uptimeMillis();
        final boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.ANR_SHOW_BACKGROUND, 0,
                mService.mUserController.getCurrentUserId()) != 0;


        Long crashTime;
        Long crashTime;
        Long crashTimePersistent;
        Long crashTimePersistent;
@@ -881,6 +881,8 @@ class AppErrors {
        final boolean persistent = app.isPersistent();
        final boolean persistent = app.isPersistent();
        final WindowProcessController proc = app.getWindowProcessController();
        final WindowProcessController proc = app.getWindowProcessController();
        final ProcessErrorStateRecord errState = app.mErrorState;
        final ProcessErrorStateRecord errState = app.mErrorState;
        final boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.ANR_SHOW_BACKGROUND, 0, getVisibleUserId(userId)) != 0;


        if (!app.isolated) {
        if (!app.isolated) {
            crashTime = mProcessCrashTimes.get(processName, uid);
            crashTime = mProcessCrashTimes.get(processName, uid);
@@ -1000,9 +1002,6 @@ class AppErrors {


    void handleShowAppErrorUi(Message msg) {
    void handleShowAppErrorUi(Message msg) {
        AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj;
        AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj;
        boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.ANR_SHOW_BACKGROUND, 0,
                mService.mUserController.getCurrentUserId()) != 0;


        final int userId;
        final int userId;
        synchronized (mProcLock) {
        synchronized (mProcLock) {
@@ -1027,7 +1026,11 @@ class AppErrors {
            for (int profileId : mService.mUserController.getCurrentProfileIds()) {
            for (int profileId : mService.mUserController.getCurrentProfileIds()) {
                isBackground &= (userId != profileId);
                isBackground &= (userId != profileId);
            }
            }
            if (isBackground && !showBackground) {
            int visibleUserId = getVisibleUserId(userId);
            boolean isVisibleUser = isVisibleBackgroundUser(visibleUserId);
            boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                    Settings.Secure.ANR_SHOW_BACKGROUND, 0, visibleUserId) != 0;
            if (isBackground && !showBackground && !isVisibleUser) {
                Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
                Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
                if (res != null) {
                if (res != null) {
                    res.set(AppErrorDialog.BACKGROUND_USER);
                    res.set(AppErrorDialog.BACKGROUND_USER);
@@ -1054,7 +1057,7 @@ class AppErrors {
                final long now = SystemClock.uptimeMillis();
                final long now = SystemClock.uptimeMillis();
                final boolean shouldThottle = crashShowErrorTime != null
                final boolean shouldThottle = crashShowErrorTime != null
                        && now < crashShowErrorTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
                        && now < crashShowErrorTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
                if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
                if ((mService.mAtmInternal.canShowErrorDialogs(visibleUserId) || showBackground)
                        && !crashSilenced && !shouldThottle
                        && !crashSilenced && !shouldThottle
                        && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
                        && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
                    Slog.i(TAG, "Showing crash dialog for package " + packageName + " u" + userId);
                    Slog.i(TAG, "Showing crash dialog for package " + packageName + " u" + userId);
@@ -1103,10 +1106,10 @@ class AppErrors {
                return;
                return;
            }
            }


            int visibleUserId = getVisibleUserId(proc.userId);
            boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
            boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                    Settings.Secure.ANR_SHOW_BACKGROUND, 0,
                    Settings.Secure.ANR_SHOW_BACKGROUND, 0, visibleUserId) != 0;
                    mService.mUserController.getCurrentUserId()) != 0;
            if (mService.mAtmInternal.canShowErrorDialogs(visibleUserId) || showBackground) {
            if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) {
                AnrController anrController = errState.getDialogController().getAnrController();
                AnrController anrController = errState.getDialogController().getAnrController();
                if (anrController == null) {
                if (anrController == null) {
                    errState.getDialogController().showAnrDialogs(data);
                    errState.getDialogController().showAnrDialogs(data);
@@ -1162,6 +1165,43 @@ class AppErrors {
        }
        }
    }
    }


    /**
     * Returns the user ID of the visible user associated with the error occurrence.
     *
     * <p>For most cases it will return the current foreground user ID, but on devices that
     * {@link UserManager#isVisibleBackgroundUsersEnabled() support visible background users},
     * it will return the given app user ID passed as parameter.
     *
     * @param appUserId The user ID of the app where the error occurred.
     * @return The ID of the visible user associated with the error.
     */
    private int getVisibleUserId(int appUserId) {
        if (!UserManager.isVisibleBackgroundUsersEnabled()) {
            return mService.mUserController.getCurrentUserId();
        }
        return appUserId;
    }

    /**
     * Checks if the given user is a visible background user, which is a full, background user
     * assigned to secondary displays on the devices that have
     * {@link UserManager#isVisibleBackgroundUsersEnabled()
     * config_multiuserVisibleBackgroundUsers enabled} (for example, passenger users on
     * automotive builds, using the display associated with their seats).
     *
     * @see UserManager#isUserVisible()
     */
    private boolean isVisibleBackgroundUser(int userId) {
        if (!UserManager.isVisibleBackgroundUsersEnabled()) {
            return false;
        }
        boolean isForeground = mService.mUserController.getCurrentUserId() == userId;
        boolean isProfile = UserManagerService.getInstance().isProfile(userId);
        boolean isVisible = LocalServices.getService(UserManagerInternal.class)
                .isUserVisible(userId);
        return isVisible && !isForeground && !isProfile;
    }

    /**
    /**
     * Information about a process that is currently marked as bad.
     * Information about a process that is currently marked as bad.
     */
     */
+1 −1
Original line number Original line Diff line number Diff line
@@ -584,7 +584,7 @@ public abstract class ActivityTaskManagerInternal {


    public abstract void clearLockedTasks(String reason);
    public abstract void clearLockedTasks(String reason);
    public abstract void updateUserConfiguration();
    public abstract void updateUserConfiguration();
    public abstract boolean canShowErrorDialogs();
    public abstract boolean canShowErrorDialogs(int userId);


    public abstract void setProfileApp(String profileApp);
    public abstract void setProfileApp(String profileApp);
    public abstract void setProfileProc(WindowProcessController wpc);
    public abstract void setProfileProc(WindowProcessController wpc);
+68 −8
Original line number Original line Diff line number Diff line
@@ -190,6 +190,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources;
@@ -4899,14 +4900,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
     * dialog / global actions also might want different behaviors.
     * dialog / global actions also might want different behaviors.
     */
     */
    private void updateShouldShowDialogsLocked(Configuration config) {
    private void updateShouldShowDialogsLocked(Configuration config) {
        mShowDialogs = shouldShowDialogs(config, /* checkUiMode= */ true);
    }

    private boolean shouldShowDialogs(Configuration config, boolean checkUiMode) {
        final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
        final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
                && config.navigation == Configuration.NAVIGATION_NONAV);
                && config.navigation == Configuration.NAVIGATION_NONAV);
        final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(),
        final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(),
                HIDE_ERROR_DIALOGS, 0) != 0;
                HIDE_ERROR_DIALOGS, 0) != 0;
        mShowDialogs = inputMethodExists
        boolean showDialogs = inputMethodExists && !hideDialogsSet;
                && ActivityTaskManager.currentUiModeSupportsErrorDialogs(config)
        if (checkUiMode) {
                && !hideDialogsSet;
            showDialogs = showDialogs
                    && ActivityTaskManager.currentUiModeSupportsErrorDialogs(config);
        }
        return showDialogs;
    }
    }


    private void updateFontScaleIfNeeded(@UserIdInt int userId) {
    private void updateFontScaleIfNeeded(@UserIdInt int userId) {
@@ -7148,15 +7156,67 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        }
        }


        @Override
        @Override
        public boolean canShowErrorDialogs() {
        public boolean canShowErrorDialogs(int userId) {
            synchronized (mGlobalLock) {
            synchronized (mGlobalLock) {
                return mShowDialogs && !mSleeping && !mShuttingDown
                final boolean showDialogs = mShowDialogs
                        || shouldShowDialogsForVisibleBackgroundUserLocked(userId);
                final UserInfo userInfo = getUserManager().getUserInfo(userId);
                if (userInfo == null) {
                    // Unable to retrieve user information. Returning false, assuming there is
                    // no valid user with the given id.
                    return false;
                }
                return showDialogs && !mSleeping && !mShuttingDown
                        && !mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
                        && !mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
                        && !hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
                        && !hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, userId)
                        mAmInternal.getCurrentUserId())
                        && !(UserManager.isDeviceInDemoMode(mContext)
                        && !(UserManager.isDeviceInDemoMode(mContext)
                        && mAmInternal.getCurrentUser().isDemo());
                        && userInfo.isDemo());
            }
        }

        /**
         * Checks if the given user is a visible background user, which is a full, background user
         * assigned to secondary displays on the devices that have
         * {@link UserManager#isVisibleBackgroundUsersEnabled()
         * config_multiuserVisibleBackgroundUsers enabled} (for example, passenger users on
         * automotive builds, using the display associated with their seats).
         *
         * @see UserManager#isUserVisible()
         */
        private boolean isVisibleBackgroundUser(int userId) {
            if (!UserManager.isVisibleBackgroundUsersEnabled()) {
                return false;
            }
            boolean isForeground = getCurrentUserId() == userId;
            boolean isProfile = getUserManager().isProfile(userId);
            boolean isVisible = mWindowManager.mUmInternal.isUserVisible(userId);
            return isVisible && !isForeground && !isProfile;
        }

        /**
         * In a car environment, {@link ActivityTaskManagerService#mShowDialogs} is always set to
         * {@code false} from {@link ActivityTaskManagerService#updateShouldShowDialogsLocked}
         * because its UI mode is {@link Configuration#UI_MODE_TYPE_CAR}. Thus, error dialogs are
         * not displayed when an ANR or a crash occurs. However, in the automotive multi-user
         * multi-display environment, this can confuse the passenger users and leave them
         * uninformed when an app is terminated by the ANR or crash without any notification.
         * To address this, error dialogs are allowed for the passenger users who have UI access
         * on assigned displays (a.k.a. visible background users) on devices that have
         * config_multiuserVisibleBackgroundUsers enabled even though the UI mode is
         * {@link Configuration#UI_MODE_TYPE_CAR}.
         *
         * @see ActivityTaskManagerService#updateShouldShowDialogsLocked
         */
        private boolean shouldShowDialogsForVisibleBackgroundUserLocked(int userId) {
            if (!isVisibleBackgroundUser(userId)) {
                return false;
            }
            final int displayId = mWindowManager.mUmInternal.getMainDisplayAssignedToUser(userId);
            final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
            if (dc == null) {
                return false;
            }
            }
            return shouldShowDialogs(dc.getConfiguration(), /* checkUiMode= */ false);
        }
        }


        @Override
        @Override