diff options
3 files changed, 120 insertions, 20 deletions
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 061bcd740f6b..ee7033e4a437 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -47,6 +47,7 @@ import android.os.Message; import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; @@ -63,6 +64,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.server.LocalServices; 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.wm.WindowProcessController; @@ -868,9 +871,6 @@ class AppErrors { private boolean handleAppCrashLSPB(ProcessRecord app, String reason, String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) { 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 crashTimePersistent; @@ -881,6 +881,8 @@ class AppErrors { final boolean persistent = app.isPersistent(); final WindowProcessController proc = app.getWindowProcessController(); 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) { crashTime = mProcessCrashTimes.get(processName, uid); @@ -1000,9 +1002,6 @@ class AppErrors { void handleShowAppErrorUi(Message msg) { 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; synchronized (mProcLock) { @@ -1027,7 +1026,11 @@ class AppErrors { for (int profileId : mService.mUserController.getCurrentProfileIds()) { 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"); if (res != null) { res.set(AppErrorDialog.BACKGROUND_USER); @@ -1054,7 +1057,7 @@ class AppErrors { final long now = SystemClock.uptimeMillis(); final boolean shouldThottle = crashShowErrorTime != null && now < crashShowErrorTime + ActivityManagerConstants.MIN_CRASH_INTERVAL; - if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground) + if ((mService.mAtmInternal.canShowErrorDialogs(visibleUserId) || showBackground) && !crashSilenced && !shouldThottle && (showFirstCrash || showFirstCrashDevOption || data.repeating)) { Slog.i(TAG, "Showing crash dialog for package " + packageName + " u" + userId); @@ -1103,10 +1106,10 @@ class AppErrors { return; } + int visibleUserId = getVisibleUserId(proc.userId); boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.ANR_SHOW_BACKGROUND, 0, - mService.mUserController.getCurrentUserId()) != 0; - if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) { + Settings.Secure.ANR_SHOW_BACKGROUND, 0, visibleUserId) != 0; + if (mService.mAtmInternal.canShowErrorDialogs(visibleUserId) || showBackground) { AnrController anrController = errState.getDialogController().getAnrController(); if (anrController == null) { errState.getDialogController().showAnrDialogs(data); @@ -1163,6 +1166,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. */ static final class BadProcessInfo { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 3b0b727597a5..26a6b00254d3 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -584,7 +584,7 @@ public abstract class ActivityTaskManagerInternal { public abstract void clearLockedTasks(String reason); public abstract void updateUserConfiguration(); - public abstract boolean canShowErrorDialogs(); + public abstract boolean canShowErrorDialogs(int userId); public abstract void setProfileApp(String profileApp); public abstract void setProfileProc(WindowProcessController wpc); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index ff46b33571f3..a84598dd73dc 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -190,6 +190,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; +import android.content.pm.UserInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; @@ -4899,14 +4900,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * dialog / global actions also might want different behaviors. */ 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 && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH && config.navigation == Configuration.NAVIGATION_NONAV); final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(), HIDE_ERROR_DIALOGS, 0) != 0; - mShowDialogs = inputMethodExists - && ActivityTaskManager.currentUiModeSupportsErrorDialogs(config) - && !hideDialogsSet; + boolean showDialogs = inputMethodExists && !hideDialogsSet; + if (checkUiMode) { + showDialogs = showDialogs + && ActivityTaskManager.currentUiModeSupportsErrorDialogs(config); + } + return showDialogs; } private void updateFontScaleIfNeeded(@UserIdInt int userId) { @@ -7148,15 +7156,67 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public boolean canShowErrorDialogs() { + public boolean canShowErrorDialogs(int userId) { 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) - && !hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, - mAmInternal.getCurrentUserId()) + && !hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, userId) && !(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 |