Merge "Show error dialogs(ANR, crash) for the visible background users" into main
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 061bcd7..ee7033e 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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 3b0b727..26a6b00 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 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 ff46b33..a84598d 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.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 @@
* 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,17 +7156,69 @@
}
@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
public void setProfileApp(String profileApp) {
synchronized (mGlobalLock) {