From 4a38acb02e88d81e95940a3cd458769df6117101 Mon Sep 17 00:00:00 2001 From: Achim Thesmann Date: Thu, 9 Mar 2023 07:39:25 -0800 Subject: [PATCH] Log the BAL hardened result if changed To help identifying cases that are impacted by BAL hardening this change is logging the BAL verdict for the case that the feature is enabled and also if not, if the verdict is changed by the feature. Test: atest BackgroundActivityLaunchTests Change-Id: I7c4e510af7ab8fd8cc7039b9a9db43b8e773009e Bug: 236231785 --- .../wm/BackgroundActivityStartController.java | 350 +++++++++++------- 1 file changed, 224 insertions(+), 126 deletions(-) diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 2344739596513..6773bcd6fac82 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER; +import static com.android.internal.util.Preconditions.checkState; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -45,6 +46,7 @@ import android.util.ArraySet; import android.util.DebugUtils; import android.util.Slog; + import com.android.internal.util.FrameworkStatsLog; import com.android.server.am.PendingIntentRecord; @@ -59,6 +61,9 @@ public class BackgroundActivityStartController { private static final String TAG = TAG_WITH_CLASS_NAME ? "BackgroundActivityStartController" : TAG_ATM; + public static final String VERDICT_ALLOWED = "Activity start allowed"; + public static final String VERDICT_WOULD_BE_ALLOWED_IF_SENDER_GRANTS_BAL = + "Activity start would be allowed if the sender granted BAL privileges"; private final ActivityTaskManagerService mService; private final ActivityTaskSupervisor mSupervisor; @@ -234,10 +239,6 @@ public class BackgroundActivityStartController { // don't abort if the callingUid has a visible window or is a persistent system process final int callingUidProcState = mService.mActiveUids.getUidState(callingUid); final boolean callingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid); - final boolean isCallingUidForeground = - callingUidHasAnyVisibleWindow - || callingUidProcState == ActivityManager.PROCESS_STATE_TOP - || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP; final boolean isCallingUidPersistentSystemProcess = callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; @@ -266,11 +267,6 @@ public class BackgroundActivityStartController { (callingUid == realCallingUid) ? callingUidHasAnyVisibleWindow : mService.hasActiveVisibleWindow(realCallingUid); - final boolean isRealCallingUidForeground = - (callingUid == realCallingUid) - ? isCallingUidForeground - : realCallingUidHasAnyVisibleWindow - || realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP; final int realCallingAppId = UserHandle.getAppId(realCallingUid); final boolean isRealCallingUidPersistentSystemProcess = (callingUid == realCallingUid) @@ -297,75 +293,63 @@ public class BackgroundActivityStartController { final BackgroundStartPrivileges balAllowedByPiSender = PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller( checkedOptions, realCallingUid); - if (balAllowedByPiSender.allowsBackgroundActivityStarts() - && realCallingUid != callingUid) { - final boolean useCallerPermission = - PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions); - if (useCallerPermission - && ActivityManager.checkComponentPermission( - android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, - realCallingUid, - -1, - true) - == PackageManager.PERMISSION_GRANTED) { - return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT, - /*background*/ false, callingUid, realCallingUid, intent, - "realCallingUid has BAL permission. realCallingUid: " + realCallingUid); - } - // don't abort if the realCallingUid has a visible window - // TODO(b/171459802): We should check appSwitchAllowed also - if (realCallingUidHasAnyVisibleWindow) { - return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT, - /*background*/ false, callingUid, realCallingUid, intent, - "realCallingUid has visible (non-toast) window. realCallingUid: " - + realCallingUid); - } - // if the realCallingUid is a persistent system process, abort if the IntentSender - // wasn't allowed to start an activity - if (isRealCallingUidPersistentSystemProcess - && backgroundStartPrivileges.allowsBackgroundActivityStarts()) { - return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT, - /*background*/ false, callingUid, realCallingUid, intent, - "realCallingUid is persistent system process AND intent " - + "sender allowed (allowBackgroundActivityStart = true). " - + "realCallingUid: " + realCallingUid); - } - // don't abort if the realCallingUid is an associated companion app - if (mService.isAssociatedCompanionApp( - UserHandle.getUserId(realCallingUid), realCallingUid)) { - return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT, - /*background*/ false, callingUid, realCallingUid, intent, - "realCallingUid is a companion app. " - + "realCallingUid: " + realCallingUid); - } + final boolean logVerdictChangeByPiDefaultChange = checkedOptions == null + || checkedOptions.getPendingIntentBackgroundActivityStartMode() + == ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; + final boolean considerPiRules = logVerdictChangeByPiDefaultChange + || balAllowedByPiSender.allowsBackgroundActivityStarts(); + final String verdictLogForPiSender = + balAllowedByPiSender.allowsBackgroundActivityStarts() ? VERDICT_ALLOWED + : VERDICT_WOULD_BE_ALLOWED_IF_SENDER_GRANTS_BAL; + + @BalCode int resultIfPiSenderAllowsBal = BAL_BLOCK; + if (realCallingUid != callingUid && considerPiRules) { + resultIfPiSenderAllowsBal = checkPiBackgroundActivityStart(callingUid, realCallingUid, + backgroundStartPrivileges, intent, checkedOptions, + realCallingUidHasAnyVisibleWindow, isRealCallingUidPersistentSystemProcess, + verdictLogForPiSender); + } + if (resultIfPiSenderAllowsBal != BAL_BLOCK + && balAllowedByPiSender.allowsBackgroundActivityStarts() + && !logVerdictChangeByPiDefaultChange) { + // The result is to allow (because the sender allows BAL) and we are not interested in + // logging differences, so just return. + return resultIfPiSenderAllowsBal; } if (useCallingUidState) { // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission if (ActivityTaskManagerService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) { return logStartAllowedAndReturnCode(BAL_ALLOW_PERMISSION, - /*background*/ true, callingUid, realCallingUid, intent, - "START_ACTIVITIES_FROM_BACKGROUND permission granted"); + resultIfPiSenderAllowsBal, balAllowedByPiSender, + /*background*/ true, callingUid, realCallingUid, intent, + "START_ACTIVITIES_FROM_BACKGROUND permission granted"); } // don't abort if the caller has the same uid as the recents component if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) { - return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT, - /*background*/ true, callingUid, realCallingUid, - intent, "Recents Component"); + return logStartAllowedAndReturnCode( + BAL_ALLOW_ALLOWLISTED_COMPONENT, + resultIfPiSenderAllowsBal, balAllowedByPiSender, + /*background*/ true, callingUid, realCallingUid, + intent, "Recents Component"); } // don't abort if the callingUid is the device owner if (mService.isDeviceOwner(callingUid)) { - return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT, - /*background*/ true, callingUid, realCallingUid, - intent, "Device Owner"); + return logStartAllowedAndReturnCode( + BAL_ALLOW_ALLOWLISTED_COMPONENT, + resultIfPiSenderAllowsBal, balAllowedByPiSender, + /*background*/ true, callingUid, realCallingUid, + intent, "Device Owner"); } // don't abort if the callingUid has companion device final int callingUserId = UserHandle.getUserId(callingUid); if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) { - return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT, - /*background*/ true, callingUid, realCallingUid, - intent, "Companion App"); + return logStartAllowedAndReturnCode( + BAL_ALLOW_ALLOWLISTED_COMPONENT, + resultIfPiSenderAllowsBal, balAllowedByPiSender, + /*background*/ true, callingUid, realCallingUid, + intent, "Companion App"); } // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) { @@ -374,18 +358,19 @@ public class BackgroundActivityStartController { "Background activity start for " + callingPackage + " allowed because SYSTEM_ALERT_WINDOW permission is granted."); - return logStartAllowedAndReturnCode(BAL_ALLOW_SAW_PERMISSION, - /*background*/ true, callingUid, realCallingUid, - intent, "SYSTEM_ALERT_WINDOW permission is granted"); + return logStartAllowedAndReturnCode( + BAL_ALLOW_SAW_PERMISSION, + resultIfPiSenderAllowsBal, balAllowedByPiSender, + /*background*/ true, callingUid, realCallingUid, + intent, "SYSTEM_ALERT_WINDOW permission is granted"); } // don't abort if the callingUid and callingPackage have the // OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop if (isSystemExemptFlagEnabled() && mService.getAppOpsManager().checkOpNoThrow( - AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION, - callingUid, - callingPackage) - == AppOpsManager.MODE_ALLOWED) { + AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION, + callingUid, callingPackage) == AppOpsManager.MODE_ALLOWED) { return logStartAllowedAndReturnCode(BAL_ALLOW_PERMISSION, + resultIfPiSenderAllowsBal, balAllowedByPiSender, /*background*/ true, callingUid, realCallingUid, intent, "OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop is granted"); } @@ -395,78 +380,119 @@ public class BackgroundActivityStartController { // up and alive. If that's the case, we retrieve the WindowProcessController for the send() // caller if caller allows, so that we can make the decision based on its state. int callerAppUid = callingUid; - if (callerApp == null && balAllowedByPiSender.allowsBackgroundActivityStarts()) { + boolean callerAppBasedOnPiSender = callerApp == null && considerPiRules + && resultIfPiSenderAllowsBal == BAL_BLOCK; + if (callerAppBasedOnPiSender) { callerApp = mService.getProcessController(realCallingPid, realCallingUid); callerAppUid = realCallingUid; } // don't abort if the callerApp or other processes of that uid are allowed in any way if (callerApp != null && useCallingUidState) { // first check the original calling process - @BalCode int balAllowedForCaller = callerApp + final @BalCode int balAllowedForCaller = callerApp .areBackgroundActivityStartsAllowed(appSwitchState); if (balAllowedForCaller != BAL_BLOCK) { - return logStartAllowedAndReturnCode(balAllowedForCaller, + if (callerAppBasedOnPiSender) { + resultIfPiSenderAllowsBal = logStartAllowedAndReturnCode(balAllowedForCaller, /*background*/ true, callingUid, realCallingUid, intent, "callerApp process (pid = " + callerApp.getPid() - + ", uid = " + callerAppUid + ") is allowed"); - } - // only if that one wasn't allowed, check the other ones - final ArraySet uidProcesses = + + ", uid = " + callerAppUid + ") is allowed", verdictLogForPiSender); + } else { + return logStartAllowedAndReturnCode(balAllowedForCaller, + resultIfPiSenderAllowsBal, balAllowedByPiSender, + /*background*/ true, callingUid, realCallingUid, intent, + "callerApp process (pid = " + callerApp.getPid() + + ", uid = " + callerAppUid + ") is allowed"); + } + } else { + // only if that one wasn't allowed, check the other ones + final ArraySet uidProcesses = mService.mProcessMap.getProcesses(callerAppUid); - if (uidProcesses != null) { - for (int i = uidProcesses.size() - 1; i >= 0; i--) { - final WindowProcessController proc = uidProcesses.valueAt(i); - int balAllowedForUid = proc.areBackgroundActivityStartsAllowed(appSwitchState); - if (proc != callerApp - && balAllowedForUid != BAL_BLOCK) { - return logStartAllowedAndReturnCode(balAllowedForUid, - /*background*/ true, callingUid, realCallingUid, intent, - "process" + proc.getPid() - + " from uid " + callerAppUid + " is allowed"); + if (uidProcesses != null) { + for (int i = uidProcesses.size() - 1; i >= 0; i--) { + final WindowProcessController proc = uidProcesses.valueAt(i); + int balAllowedForUid = proc.areBackgroundActivityStartsAllowed( + appSwitchState); + if (proc != callerApp && balAllowedForUid != BAL_BLOCK) { + if (callerAppBasedOnPiSender) { + resultIfPiSenderAllowsBal = logStartAllowedAndReturnCode( + balAllowedForUid, + /*background*/ true, callingUid, realCallingUid, intent, + "process" + proc.getPid() + " from uid " + callerAppUid + + " is allowed", verdictLogForPiSender); + break; + } else { + return logStartAllowedAndReturnCode(balAllowedForUid, + resultIfPiSenderAllowsBal, balAllowedByPiSender, + /*background*/ true, callingUid, realCallingUid, intent, + "process" + proc.getPid() + " from uid " + callerAppUid + + " is allowed"); + } + } } } } + if (callerAppBasedOnPiSender) { + // If caller app was based on PI sender, this result is part of + // resultIfPiSenderAllowsBal + if (resultIfPiSenderAllowsBal != BAL_BLOCK + && balAllowedByPiSender.allowsBackgroundActivityStarts() + && !logVerdictChangeByPiDefaultChange) { + // The result is to allow (because the sender allows BAL) and we are not + // interested in logging differences, so just return. + return resultIfPiSenderAllowsBal; + } + } else { + // If caller app was NOT based on PI sender and we found a allow reason we should + // have returned already + checkState(balAllowedForCaller == BAL_BLOCK, + "balAllowedForCaller = " + balAllowedForCaller + " (should have returned)"); + } + } + // If we are here, it means all exemptions not based on PI sender failed, so we'll block + // unless resultIfPiSenderAllowsBal is an allow and the PI sender allows BAL + + String stateDumpLog = " [callingPackage: " + callingPackage + + "; callingUid: " + callingUid + + "; appSwitchState: " + appSwitchState + + "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow + + "; callingUidProcState: " + DebugUtils.valueToString( + ActivityManager.class, "PROCESS_STATE_", callingUidProcState) + + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess + + "; balAllowedByPiSender: " + balAllowedByPiSender + + "; realCallingUid: " + realCallingUid + + "; realCallingUidHasAnyVisibleWindow: " + realCallingUidHasAnyVisibleWindow + + "; realCallingUidProcState: " + DebugUtils.valueToString( + ActivityManager.class, "PROCESS_STATE_", realCallingUidProcState) + + "; isRealCallingUidPersistentSystemProcess: " + + isRealCallingUidPersistentSystemProcess + + "; originatingPendingIntent: " + originatingPendingIntent + + "; backgroundStartPrivileges: " + backgroundStartPrivileges + + "; intent: " + intent + + "; callerApp: " + callerApp + + "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask()) + + "]"; + if (resultIfPiSenderAllowsBal != BAL_BLOCK) { + // We should have returned before if !logVerdictChangeByPiDefaultChange + checkState(logVerdictChangeByPiDefaultChange, + "resultIfPiSenderAllowsBal = " + balCodeToString(resultIfPiSenderAllowsBal) + + " at the end but logVerdictChangeByPiDefaultChange = false"); + if (balAllowedByPiSender.allowsBackgroundActivityStarts()) { + // The verdict changed from block to allow, PI sender default change is off and + // we'd block if it were on + Slog.wtf(TAG, "With BAL hardening this activity start would be blocked!" + + stateDumpLog); + return resultIfPiSenderAllowsBal; + } else { + // The verdict changed from allow (resultIfPiSenderAllowsBal) to block, PI sender + // default change is on (otherwise we would have fallen into if above) and we'd + // allow if it were off + Slog.wtf(TAG, "Without BAL hardening this activity start would NOT be allowed!" + + stateDumpLog); + } } // anything that has fallen through would currently be aborted - Slog.w( - TAG, - "Background activity launch blocked [callingPackage: " - + callingPackage - + "; callingUid: " - + callingUid - + "; appSwitchState: " - + appSwitchState - + "; isCallingUidForeground: " - + isCallingUidForeground - + "; callingUidHasAnyVisibleWindow: " - + callingUidHasAnyVisibleWindow - + "; callingUidProcState: " - + DebugUtils.valueToString( - ActivityManager.class, "PROCESS_STATE_", callingUidProcState) - + "; isCallingUidPersistentSystemProcess: " - + isCallingUidPersistentSystemProcess - + "; realCallingUid: " - + realCallingUid - + "; isRealCallingUidForeground: " - + isRealCallingUidForeground - + "; realCallingUidHasAnyVisibleWindow: " - + realCallingUidHasAnyVisibleWindow - + "; realCallingUidProcState: " - + DebugUtils.valueToString( - ActivityManager.class, "PROCESS_STATE_", realCallingUidProcState) - + "; isRealCallingUidPersistentSystemProcess: " - + isRealCallingUidPersistentSystemProcess - + "; originatingPendingIntent: " - + originatingPendingIntent - + "; backgroundStartPrivileges: " - + backgroundStartPrivileges - + "; intent: " - + intent - + "; callerApp: " - + callerApp - + "; inVisibleTask: " - + (callerApp != null && callerApp.hasActivityInVisibleTask()) - + "]"); + Slog.w(TAG, "Background activity launch blocked" + stateDumpLog); // log aborted activity start to TRON if (mService.isActivityStartsLoggingEnabled()) { mSupervisor @@ -486,6 +512,51 @@ public class BackgroundActivityStartController { return BAL_BLOCK; } + private @BalCode int checkPiBackgroundActivityStart(int callingUid, int realCallingUid, + BackgroundStartPrivileges backgroundStartPrivileges, Intent intent, + ActivityOptions checkedOptions, boolean realCallingUidHasAnyVisibleWindow, + boolean isRealCallingUidPersistentSystemProcess, String verdictLog) { + final boolean useCallerPermission = + PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions); + if (useCallerPermission + && ActivityManager.checkComponentPermission( + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, + realCallingUid, -1, true) == PackageManager.PERMISSION_GRANTED) { + return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT, + /*background*/ false, callingUid, realCallingUid, intent, + "realCallingUid has BAL permission. realCallingUid: " + realCallingUid, + verdictLog); + } + + // don't abort if the realCallingUid has a visible window + // TODO(b/171459802): We should check appSwitchAllowed also + if (realCallingUidHasAnyVisibleWindow) { + return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT, + /*background*/ false, callingUid, realCallingUid, intent, + "realCallingUid has visible (non-toast) window. realCallingUid: " + + realCallingUid, verdictLog); + } + // if the realCallingUid is a persistent system process, abort if the IntentSender + // wasn't allowed to start an activity + if (isRealCallingUidPersistentSystemProcess + && backgroundStartPrivileges.allowsBackgroundActivityStarts()) { + return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT, + /*background*/ false, callingUid, realCallingUid, intent, + "realCallingUid is persistent system process AND intent " + + "sender allowed (allowBackgroundActivityStart = true). " + + "realCallingUid: " + realCallingUid, verdictLog); + } + // don't abort if the realCallingUid is an associated companion app + if (mService.isAssociatedCompanionApp( + UserHandle.getUserId(realCallingUid), realCallingUid)) { + return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT, + /*background*/ false, callingUid, realCallingUid, intent, + "realCallingUid is a companion app. " + + "realCallingUid: " + realCallingUid, verdictLog); + } + return BAL_BLOCK; + } + static @BalCode int logStartAllowedAndReturnCode(@BalCode int code, boolean background, int callingUid, int realCallingUid, Intent intent, int pid, String msg) { return logStartAllowedAndReturnCode(code, background, callingUid, realCallingUid, intent, @@ -494,16 +565,43 @@ public class BackgroundActivityStartController { static @BalCode int logStartAllowedAndReturnCode(@BalCode int code, boolean background, int callingUid, int realCallingUid, Intent intent, String msg) { + return logStartAllowedAndReturnCode(code, background, callingUid, realCallingUid, intent, + msg, VERDICT_ALLOWED); + } + + /** + * Logs the start and returns one of the provided codes depending on if the PI sender allows + * using its BAL privileges. + */ + static @BalCode int logStartAllowedAndReturnCode(@BalCode int result, + @BalCode int resultIfPiSenderAllowsBal, BackgroundStartPrivileges balAllowedByPiSender, + boolean background, int callingUid, int realCallingUid, Intent intent, String msg) { + if (resultIfPiSenderAllowsBal != BAL_BLOCK + && balAllowedByPiSender.allowsBackgroundActivityStarts()) { + // resultIfPiSenderAllowsBal was already logged, so just return + return resultIfPiSenderAllowsBal; + } + return logStartAllowedAndReturnCode(result, background, callingUid, realCallingUid, + intent, msg, VERDICT_ALLOWED); + } + + + static @BalCode int logStartAllowedAndReturnCode(@BalCode int code, boolean background, + int callingUid, int realCallingUid, Intent intent, String msg, String verdict) { statsLogBalAllowed(code, callingUid, realCallingUid, intent); if (DEBUG_ACTIVITY_STARTS) { StringBuilder builder = new StringBuilder(); if (background) { builder.append("Background "); } - builder.append("Activity start allowed: " + msg + ". callingUid: " + callingUid + ". "); + builder.append(verdict + ": " + msg + ". callingUid: " + callingUid + ". "); builder.append("BAL Code: "); builder.append(balCodeToString(code)); - Slog.d(TAG, builder.toString()); + if (verdict.equals(VERDICT_ALLOWED)) { + Slog.i(TAG, builder.toString()); + } else { + Slog.d(TAG, builder.toString()); + } } return code; }