From 6f77f2e8d2ffd83d687f3aaf0b733c25dec65131 Mon Sep 17 00:00:00 2001 From: Achim Thesmann Date: Wed, 31 Aug 2022 16:50:09 -0700 Subject: [PATCH 1/3] Merge "Add ownership for BackgroundActivityStartController" into tm-qpr-dev Bug: 236234252 Test: atest ActivityStarterTests Ignore-AOSP-First: Internal merge conflict, AOSP change in aosp/2169485 Change-Id: I6ee0e839a4000c9e27630c2e654d945bf45b21e6 Merged-In: I6ee0e839a4000c9e27630c2e654d945bf45b21e6 --- .../server/wm/ActivityStartController.java | 7 + .../android/server/wm/ActivityStarter.java | 305 +------------ .../server/wm/ActivityTaskManagerService.java | 17 +- .../com/android/server/wm/AppTaskImpl.java | 18 +- .../wm/BackgroundActivityStartController.java | 421 ++++++++++++++++++ .../core/java/com/android/server/wm/OWNERS | 3 + .../server/wm/ActivityStarterTests.java | 12 +- 7 files changed, 481 insertions(+), 302 deletions(-) create mode 100644 services/core/java/com/android/server/wm/BackgroundActivityStartController.java diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 4663662c5585f..39106f61d5155 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -98,6 +98,8 @@ public class ActivityStartController { /** Whether an {@link ActivityStarter} is currently executing (starting an Activity). */ private boolean mInExecution = false; + private final BackgroundActivityStartController mBalController; + /** * TODO(b/64750076): Capture information necessary for dump and * {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object @@ -120,6 +122,7 @@ public class ActivityStartController { mFactory.setController(this); mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service.mGlobalLock, service.mH); + mBalController = new BackgroundActivityStartController(mService, mSupervisor); } /** @@ -670,4 +673,8 @@ public class ActivityStartController { pw.println("(nothing)"); } } + + BackgroundActivityStartController getBackgroundActivityLaunchController() { + return mBalController; + } } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index aad89b441732f..d6c1d7ffe47f5 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.app.Activity.RESULT_CANCELED; import static android.app.ActivityManager.START_ABORTED; import static android.app.ActivityManager.START_CANCELED; @@ -52,7 +51,6 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; import static android.content.pm.ActivityInfo.launchModeToString; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.INVALID_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_NONE; @@ -63,7 +61,6 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; import static com.android.server.wm.ActivityRecord.State.RESUMED; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING; @@ -74,8 +71,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; -import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW; -import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY; import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; @@ -99,7 +94,6 @@ import android.app.WaitResult; import android.app.WindowConfiguration; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; -import android.content.ComponentName; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; @@ -115,15 +109,12 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.IBinder; -import android.os.Process; import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.service.voice.IVoiceInteractionSession; import android.text.TextUtils; -import android.util.ArraySet; -import android.util.DebugUtils; import android.util.Pools.SynchronizedPool; import android.util.Slog; import android.window.RemoteTransition; @@ -264,8 +255,6 @@ class ActivityStarter { /** * Generates an {@link ActivityStarter} that is ready to handle a new start request. - * @param controller The {@link ActivityStartController} which the starter who will own - * this instance. * @return an {@link ActivityStarter} */ ActivityStarter obtain(); @@ -1036,10 +1025,20 @@ class ActivityStarter { try { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "shouldAbortBackgroundActivityStart"); - restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid, - callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, - request.originatingPendingIntent, request.allowBackgroundActivityStart, - intent, checkedOptions); + BackgroundActivityStartController balController = + mController.getBackgroundActivityLaunchController(); + restrictedBgActivity = + balController.shouldAbortBackgroundActivityStart( + callingUid, + callingPid, + callingPackage, + realCallingUid, + realCallingPid, + callerApp, + request.originatingPendingIntent, + request.allowBackgroundActivityStart, + intent, + checkedOptions); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } @@ -1273,282 +1272,6 @@ class ActivityStarter { mController.onExecutionStarted(); } - private boolean isHomeApp(int uid, @Nullable String packageName) { - if (mService.mHomeProcess != null) { - // Fast check - return uid == mService.mHomeProcess.mUid; - } - if (packageName == null) { - return false; - } - ComponentName activity = - mService.getPackageManagerInternalLocked().getDefaultHomeActivity( - UserHandle.getUserId(uid)); - return activity != null && packageName.equals(activity.getPackageName()); - } - - boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, - final String callingPackage, int realCallingUid, int realCallingPid, - WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, - boolean allowBackgroundActivityStart, Intent intent, ActivityOptions checkedOptions) { - // don't abort for the most important UIDs - final int callingAppId = UserHandle.getAppId(callingUid); - final boolean useCallingUidState = - originatingPendingIntent == null || checkedOptions == null - || !checkedOptions.getIgnorePendingIntentCreatorForegroundState(); - if (useCallingUidState) { - if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID - || callingAppId == Process.NFC_UID) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, - "Activity start allowed for important callingUid (" + callingUid + ")"); - } - return false; - } - - // Always allow home application to start activities. - if (isHomeApp(callingUid, callingPackage)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, - "Activity start allowed for home app callingUid (" + callingUid + ")"); - } - return false; - } - - // IME should always be allowed to start activity, like IME settings. - final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow(); - if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")"); - } - return false; - } - } - - // This is used to block background activity launch even if the app is still - // visible to user after user clicking home button. - final int appSwitchState = mService.getBalAppSwitchesState(); - - // 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; - - // Normal apps with visible app window will be allowed to start activity if app switching - // is allowed, or apps like live wallpaper with non app visible window will be allowed. - final boolean appSwitchAllowedOrFg = - appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY; - final boolean allowCallingUidStartActivity = - ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) - && callingUidHasAnyVisibleWindow) - || isCallingUidPersistentSystemProcess; - if (useCallingUidState && allowCallingUidStartActivity) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid - + ", isCallingUidPersistentSystemProcess = " - + isCallingUidPersistentSystemProcess); - } - return false; - } - // take realCallingUid into consideration - final int realCallingUidProcState = (callingUid == realCallingUid) - ? callingUidProcState - : mService.mActiveUids.getUidState(realCallingUid); - final boolean realCallingUidHasAnyVisibleWindow = (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) - ? isCallingUidPersistentSystemProcess - : (realCallingAppId == Process.SYSTEM_UID) - || realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; - - // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a - // visible window. - if (Process.isSdkSandboxUid(realCallingUid)) { - int realCallingSdkSandboxUidToAppUid = Process.getAppUidForSdkSandboxUid( - UserHandle.getAppId(realCallingUid)); - - if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Activity start allowed: uid in SDK sandbox (" - + realCallingUid + ") has visible (non-toast) window."); - } - return false; - } - } - - // Legacy behavior allows to use caller foreground state to bypass BAL restriction. - final boolean balAllowedByPiSender = - PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions); - - if (balAllowedByPiSender && 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) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid - + ") has BAL permission."); - } - return false; - } - - // don't abort if the realCallingUid has a visible window - // TODO(b/171459802): We should check appSwitchAllowed also - if (realCallingUidHasAnyVisibleWindow) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid - + ") has visible (non-toast) window"); - } - return false; - } - // if the realCallingUid is a persistent system process, abort if the IntentSender - // wasn't allowed to start an activity - if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid - + ") is persistent system process AND intent sender allowed " - + "(allowBackgroundActivityStart = true)"); - } - return false; - } - // don't abort if the realCallingUid is an associated companion app - if (mService.isAssociatedCompanionApp(UserHandle.getUserId(realCallingUid), - realCallingUid)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid - + ") is companion app"); - } - return false; - } - } - if (useCallingUidState) { - // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission - if (mService.checkPermission( - START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) - == PERMISSION_GRANTED) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, - "Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND " - + "permission granted for uid " - + callingUid); - } - return false; - } - // don't abort if the caller has the same uid as the recents component - if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid - + ") is recents"); - } - return false; - } - // don't abort if the callingUid is the device owner - if (mService.isDeviceOwner(callingUid)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid - + ") is device owner"); - } - return false; - } - // don't abort if the callingUid has companion device - final int callingUserId = UserHandle.getUserId(callingUid); - if (mService.isAssociatedCompanionApp(callingUserId, - callingUid)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid - + ") is companion app"); - } - return false; - } - // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission - if (mService.hasSystemAlertWindowPermission(callingUid, - callingPid, callingPackage)) { - Slog.w(TAG, "Background activity start for " + callingPackage - + " allowed because SYSTEM_ALERT_WINDOW permission is granted."); - return false; - } - } - // If we don't have callerApp at this point, no caller was provided to startActivity(). - // That's the case for PendingIntent-based starts, since the creator's process might not be - // 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) { - 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 - if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Background activity start allowed: callerApp process (pid = " - + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed"); - } - return false; - } - // 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); - if (proc != callerApp - && proc.areBackgroundActivityStartsAllowed(appSwitchState)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, - "Background activity start allowed: process " + proc.getPid() - + " from uid " + callerAppUid + " is allowed"); - } - return false; - } - } - } - } - // anything that has fallen through would currently be aborted - Slog.w(TAG, "Background activity start [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 - + "; allowBackgroundActivityStart: " + allowBackgroundActivityStart - + "; intent: " + intent - + "; callerApp: " + callerApp - + "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask()) - + "]"); - // log aborted activity start to TRON - if (mService.isActivityStartsLoggingEnabled()) { - mSupervisor.getActivityMetricsLogger().logAbortedBgActivityStart(intent, callerApp, - callingUid, callingPackage, callingUidProcState, callingUidHasAnyVisibleWindow, - realCallingUid, realCallingUidProcState, realCallingUidHasAnyVisibleWindow, - (originatingPendingIntent != null)); - } - return true; - } - /** * Creates a launch intent for the given auxiliary resolution data. */ diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 491e58b63f76e..9236ab63e7f97 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2182,10 +2182,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (appThread != null) { callerApp = getProcessController(appThread); } - final ActivityStarter starter = getActivityStartController().obtainStarter( - null /* intent */, "moveTaskToFront"); - if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1, - -1, callerApp, null, false, null, null)) { + final BackgroundActivityStartController balController = + getActivityStartController().getBackgroundActivityLaunchController(); + if (balController.shouldAbortBackgroundActivityStart( + callingUid, + callingPid, + callingPackage, + -1, + -1, + callerApp, + null, + false, + null, + null)) { if (!isBackgroundActivityStartsEnabled()) { return; } diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 0bfc48b4b54c5..b160af6a3e11f 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -119,10 +119,20 @@ class AppTaskImpl extends IAppTask.Stub { if (appThread != null) { callerApp = mService.getProcessController(appThread); } - final ActivityStarter starter = mService.getActivityStartController().obtainStarter( - null /* intent */, "moveToFront"); - if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, - callingPackage, -1, -1, callerApp, null, false, null, null)) { + final BackgroundActivityStartController balController = + mService.getActivityStartController() + .getBackgroundActivityLaunchController(); + if (balController.shouldAbortBackgroundActivityStart( + callingUid, + callingPid, + callingPackage, + -1, + -1, + callerApp, + null, + false, + null, + null)) { if (!mService.isBackgroundActivityStartsEnabled()) { return; } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java new file mode 100644 index 0000000000000..d515a277e6926 --- /dev/null +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +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; +import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW; +import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY; + +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Process; +import android.os.UserHandle; +import android.util.ArraySet; +import android.util.DebugUtils; +import android.util.Slog; + +import com.android.server.am.PendingIntentRecord; + +/** + * Helper class to check permissions for starting Activities. + * + *

This class collects all the logic to prevent malicious attempts to start activities. + */ +public class BackgroundActivityStartController { + + private static final String TAG = + TAG_WITH_CLASS_NAME ? "BackgroundActivityStartController" : TAG_ATM; + + private final ActivityTaskManagerService mService; + private final ActivityTaskSupervisor mSupervisor; + + BackgroundActivityStartController( + final ActivityTaskManagerService service, final ActivityTaskSupervisor supervisor) { + mService = service; + mSupervisor = supervisor; + } + + private boolean isHomeApp(int uid, @Nullable String packageName) { + if (mService.mHomeProcess != null) { + // Fast check + return uid == mService.mHomeProcess.mUid; + } + if (packageName == null) { + return false; + } + ComponentName activity = + mService.getPackageManagerInternalLocked() + .getDefaultHomeActivity(UserHandle.getUserId(uid)); + return activity != null && packageName.equals(activity.getPackageName()); + } + + boolean shouldAbortBackgroundActivityStart( + int callingUid, + int callingPid, + final String callingPackage, + int realCallingUid, + int realCallingPid, + WindowProcessController callerApp, + PendingIntentRecord originatingPendingIntent, + boolean allowBackgroundActivityStart, + Intent intent, + ActivityOptions checkedOptions) { + // don't abort for the most important UIDs + final int callingAppId = UserHandle.getAppId(callingUid); + final boolean useCallingUidState = + originatingPendingIntent == null + || checkedOptions == null + || !checkedOptions.getIgnorePendingIntentCreatorForegroundState(); + if (useCallingUidState) { + if (callingUid == Process.ROOT_UID + || callingAppId == Process.SYSTEM_UID + || callingAppId == Process.NFC_UID) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Activity start allowed for important callingUid (" + callingUid + ")"); + } + return false; + } + + // Always allow home application to start activities. + if (isHomeApp(callingUid, callingPackage)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Activity start allowed for home app callingUid (" + callingUid + ")"); + } + return false; + } + + // IME should always be allowed to start activity, like IME settings. + final WindowState imeWindow = + mService.mRootWindowContainer.getCurrentInputMethodWindow(); + if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")"); + } + return false; + } + } + + // This is used to block background activity launch even if the app is still + // visible to user after user clicking home button. + final int appSwitchState = mService.getBalAppSwitchesState(); + + // 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; + + // Normal apps with visible app window will be allowed to start activity if app switching + // is allowed, or apps like live wallpaper with non app visible window will be allowed. + final boolean appSwitchAllowedOrFg = + appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY; + final boolean allowCallingUidStartActivity = + ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) + && callingUidHasAnyVisibleWindow) + || isCallingUidPersistentSystemProcess; + if (useCallingUidState && allowCallingUidStartActivity) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Activity start allowed: callingUidHasAnyVisibleWindow = " + + callingUid + + ", isCallingUidPersistentSystemProcess = " + + isCallingUidPersistentSystemProcess); + } + return false; + } + // take realCallingUid into consideration + final int realCallingUidProcState = + (callingUid == realCallingUid) + ? callingUidProcState + : mService.mActiveUids.getUidState(realCallingUid); + final boolean realCallingUidHasAnyVisibleWindow = + (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) + ? isCallingUidPersistentSystemProcess + : (realCallingAppId == Process.SYSTEM_UID) + || realCallingUidProcState + <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; + + // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a + // visible window. + if (Process.isSdkSandboxUid(realCallingUid)) { + int realCallingSdkSandboxUidToAppUid = + Process.getAppUidForSdkSandboxUid(UserHandle.getAppId(realCallingUid)); + + if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Activity start allowed: uid in SDK sandbox (" + + realCallingUid + + ") has visible (non-toast) window."); + } + return false; + } + } + + // Legacy behavior allows to use caller foreground state to bypass BAL restriction. + final boolean balAllowedByPiSender = + PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions); + + if (balAllowedByPiSender && 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) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Activity start allowed: realCallingUid (" + + realCallingUid + + ") has BAL permission."); + } + return false; + } + + // don't abort if the realCallingUid has a visible window + // TODO(b/171459802): We should check appSwitchAllowed also + if (realCallingUidHasAnyVisibleWindow) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Activity start allowed: realCallingUid (" + + realCallingUid + + ") has visible (non-toast) window"); + } + return false; + } + // if the realCallingUid is a persistent system process, abort if the IntentSender + // wasn't allowed to start an activity + if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Activity start allowed: realCallingUid (" + + realCallingUid + + ") is persistent system process AND intent sender allowed " + + "(allowBackgroundActivityStart = true)"); + } + return false; + } + // don't abort if the realCallingUid is an associated companion app + if (mService.isAssociatedCompanionApp( + UserHandle.getUserId(realCallingUid), realCallingUid)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Activity start allowed: realCallingUid (" + + realCallingUid + + ") is companion app"); + } + return false; + } + } + if (useCallingUidState) { + // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission + if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) + == PERMISSION_GRANTED) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND " + + "permission granted for uid " + + callingUid); + } + return false; + } + // don't abort if the caller has the same uid as the recents component + if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Background activity start allowed: callingUid (" + + callingUid + + ") is recents"); + } + return false; + } + // don't abort if the callingUid is the device owner + if (mService.isDeviceOwner(callingUid)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Background activity start allowed: callingUid (" + + callingUid + + ") is device owner"); + } + return false; + } + // don't abort if the callingUid has companion device + final int callingUserId = UserHandle.getUserId(callingUid); + if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Background activity start allowed: callingUid (" + + callingUid + + ") is companion app"); + } + return false; + } + // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission + if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) { + Slog.w( + TAG, + "Background activity start for " + + callingPackage + + " allowed because SYSTEM_ALERT_WINDOW permission is granted."); + return false; + } + } + // If we don't have callerApp at this point, no caller was provided to startActivity(). + // That's the case for PendingIntent-based starts, since the creator's process might not be + // 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) { + 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 + if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Background activity start allowed: callerApp process (pid = " + + callerApp.getPid() + + ", uid = " + + callerAppUid + + ") is allowed"); + } + return false; + } + // 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); + if (proc != callerApp + && proc.areBackgroundActivityStartsAllowed(appSwitchState)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d( + TAG, + "Background activity start allowed: process " + + proc.getPid() + + " from uid " + + callerAppUid + + " is allowed"); + } + return false; + } + } + } + } + // anything that has fallen through would currently be aborted + Slog.w( + TAG, + "Background activity start [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 + + "; allowBackgroundActivityStart: " + + allowBackgroundActivityStart + + "; intent: " + + intent + + "; callerApp: " + + callerApp + + "; inVisibleTask: " + + (callerApp != null && callerApp.hasActivityInVisibleTask()) + + "]"); + // log aborted activity start to TRON + if (mService.isActivityStartsLoggingEnabled()) { + mSupervisor + .getActivityMetricsLogger() + .logAbortedBgActivityStart( + intent, + callerApp, + callingUid, + callingPackage, + callingUidProcState, + callingUidHasAnyVisibleWindow, + realCallingUid, + realCallingUidProcState, + realCallingUidHasAnyVisibleWindow, + (originatingPendingIntent != null)); + } + return true; + } +} diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS index 6602d29883e4a..4f506a5dd945f 100644 --- a/services/core/java/com/android/server/wm/OWNERS +++ b/services/core/java/com/android/server/wm/OWNERS @@ -14,3 +14,6 @@ winsonc@google.com tigerhuang@google.com lihongyu@google.com mariiasand@google.com + +per-file BackgroundActivityStartController.java = set noparent +per-file BackgroundActivityStartController.java = brufino@google.com, ogunwale@google.com, louischang@google.com, lus@google.com, rickywai@google.com diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index b4ffc2adbeea2..e8d5c00147114 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -150,6 +150,9 @@ public class ActivityStarterTests extends WindowTestsBase { @Before public void setUp() throws Exception { mController = mock(ActivityStartController.class); + BackgroundActivityStartController balController = + new BackgroundActivityStartController(mAtm, mSupervisor); + doReturn(balController).when(mController).getBackgroundActivityLaunchController(); mActivityMetricsLogger = mock(ActivityMetricsLogger.class); clearInvocations(mActivityMetricsLogger); } @@ -211,10 +214,13 @@ public class ActivityStarterTests extends WindowTestsBase { int expectedResult) { final ActivityTaskManagerService service = mAtm; final IPackageManager packageManager = mock(IPackageManager.class); - final ActivityStartController controller = mock(ActivityStartController.class); - final ActivityStarter starter = new ActivityStarter(controller, service, - service.mTaskSupervisor, mock(ActivityStartInterceptor.class)); + final ActivityStarter starter = + new ActivityStarter( + mController, + service, + service.mTaskSupervisor, + mock(ActivityStartInterceptor.class)); prepareStarter(launchFlags); final IApplicationThread caller = mock(IApplicationThread.class); final WindowProcessListener listener = mock(WindowProcessListener.class); From d5de388da7d6de814c718d2cad6bdbb91eb5b8ed Mon Sep 17 00:00:00 2001 From: Hani Kazmi Date: Thu, 10 Nov 2022 17:50:44 +0000 Subject: [PATCH 2/3] Merge "Update BAL Check to return Allow Reason" into tm-qpr-dev Bug: 246738504 Test: atest ActivityStarterTests BackgroundActivityLaunchTest Change-Id: Ie2bb56c53e49f6511a3e830ed1fa228b26e93a05 Merged-In: Ie2bb56c53e49f6511a3e830ed1fa228b26e93a05 --- .../android/server/wm/ActivityStarter.java | 39 +-- .../wm/BackgroundActivityStartController.java | 284 ++++++++++-------- .../wm/BackgroundLaunchProcessController.java | 19 +- .../server/wm/WindowProcessController.java | 9 +- .../server/wm/ActivityStarterTests.java | 4 +- .../DisplayWindowPolicyControllerTests.java | 2 +- 6 files changed, 203 insertions(+), 154 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index d6c1d7ffe47f5..d1a5ead78af26 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -74,6 +74,8 @@ import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_DEFAULT; +import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT; @@ -130,6 +132,7 @@ import com.android.server.power.ShutdownCheckPoints; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.NeededUriGrants; import com.android.server.wm.ActivityMetricsLogger.LaunchingState; +import com.android.server.wm.BackgroundActivityStartController.BalCode; import com.android.server.wm.LaunchParamsController.LaunchParams; import com.android.server.wm.TaskFragment.EmbeddingCheckResult; @@ -171,9 +174,10 @@ class ActivityStarter { private int mCallingUid; private ActivityOptions mOptions; - // If it is true, background activity can only be started in an existing task that contains + // If it is BAL_BLOCK, background activity can only be started in an existing task that contains // an activity with same uid, or if activity starts are enabled in developer options. - private boolean mRestrictedBgActivity; + @BalCode + private int mBalCode; private int mLaunchMode; private boolean mLaunchTaskBehind; @@ -589,7 +593,7 @@ class ActivityStarter { mIntent = starter.mIntent; mCallingUid = starter.mCallingUid; mOptions = starter.mOptions; - mRestrictedBgActivity = starter.mRestrictedBgActivity; + mBalCode = starter.mBalCode; mLaunchTaskBehind = starter.mLaunchTaskBehind; mLaunchFlags = starter.mLaunchFlags; @@ -1020,15 +1024,15 @@ class ActivityStarter { ActivityOptions checkedOptions = options != null ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null; - boolean restrictedBgActivity = false; + @BalCode int balCode = BAL_ALLOW_DEFAULT; if (!abort) { try { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "shouldAbortBackgroundActivityStart"); BackgroundActivityStartController balController = mController.getBackgroundActivityLaunchController(); - restrictedBgActivity = - balController.shouldAbortBackgroundActivityStart( + balCode = + balController.checkBackgroundActivityStart( callingUid, callingPid, callingPackage, @@ -1216,13 +1220,13 @@ class ActivityStarter { WindowProcessController homeProcess = mService.mHomeProcess; boolean isHomeProcess = homeProcess != null && aInfo.applicationInfo.uid == homeProcess.mUid; - if (!restrictedBgActivity && !isHomeProcess) { + if (balCode != BAL_BLOCK && !isHomeProcess) { mService.resumeAppSwitches(); } mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession, request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, - inTask, inTaskFragment, restrictedBgActivity, intentGrants); + inTask, inTaskFragment, balCode, intentGrants); if (request.outActivity != null) { request.outActivity[0] = mLastStartActivityRecord; @@ -1372,7 +1376,7 @@ class ActivityStarter { private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, Task inTask, - TaskFragment inTaskFragment, boolean restrictedBgActivity, + TaskFragment inTaskFragment, @BalCode int balCode, NeededUriGrants intentGrants) { int result = START_CANCELED; final Task startedActivityRootTask; @@ -1392,7 +1396,7 @@ class ActivityStarter { try { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner"); result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor, - startFlags, doResume, options, inTask, inTaskFragment, restrictedBgActivity, + startFlags, doResume, options, inTask, inTaskFragment, balCode, intentGrants); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); @@ -1539,10 +1543,10 @@ class ActivityStarter { int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, Task inTask, - TaskFragment inTaskFragment, boolean restrictedBgActivity, + TaskFragment inTaskFragment, @BalCode int balCode, NeededUriGrants intentGrants) { setInitialState(r, options, inTask, inTaskFragment, doResume, startFlags, sourceRecord, - voiceSession, voiceInteractor, restrictedBgActivity); + voiceSession, voiceInteractor, balCode); computeLaunchingTaskFlags(); mIntent.setFlags(mLaunchFlags); @@ -1778,7 +1782,8 @@ class ActivityStarter { || !targetTask.isUidPresent(mCallingUid) || (LAUNCH_SINGLE_INSTANCE == mLaunchMode && targetTask.inPinnedWindowingMode())); - if (mRestrictedBgActivity && blockBalInTask && handleBackgroundActivityAbort(r)) { + if (mBalCode == BAL_BLOCK && blockBalInTask + && handleBackgroundActivityAbort(r)) { Slog.e(TAG, "Abort background activity starts from " + mCallingUid); return START_ABORTED; } @@ -2175,7 +2180,7 @@ class ActivityStarter { mIntent = null; mCallingUid = -1; mOptions = null; - mRestrictedBgActivity = false; + mBalCode = BAL_ALLOW_DEFAULT; mLaunchTaskBehind = false; mLaunchFlags = 0; @@ -2220,7 +2225,7 @@ class ActivityStarter { private void setInitialState(ActivityRecord r, ActivityOptions options, Task inTask, TaskFragment inTaskFragment, boolean doResume, int startFlags, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, - IVoiceInteractor voiceInteractor, boolean restrictedBgActivity) { + IVoiceInteractor voiceInteractor, @BalCode int balCode) { reset(false /* clearRequest */); mStartActivity = r; @@ -2231,7 +2236,7 @@ class ActivityStarter { mSourceRootTask = mSourceRecord != null ? mSourceRecord.getRootTask() : null; mVoiceSession = voiceSession; mVoiceInteractor = voiceInteractor; - mRestrictedBgActivity = restrictedBgActivity; + mBalCode = balCode; mLaunchParams.reset(); @@ -2368,7 +2373,7 @@ class ActivityStarter { mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0; - if (mRestrictedBgActivity && !mService.isBackgroundActivityStartsEnabled()) { + if (mBalCode == BAL_BLOCK && !mService.isBackgroundActivityStartsEnabled()) { mAvoidMoveToFront = true; mDoResume = false; } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index d515a277e6926..2315795a003b2 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -25,6 +25,9 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW; import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -39,6 +42,8 @@ import android.util.Slog; import com.android.server.am.PendingIntentRecord; +import java.lang.annotation.Retention; + /** * Helper class to check permissions for starting Activities. * @@ -52,6 +57,56 @@ public class BackgroundActivityStartController { private final ActivityTaskManagerService mService; private final ActivityTaskSupervisor mSupervisor; + // TODO(b/263368846) Rename when ASM logic is moved in + @Retention(SOURCE) + @IntDef({BAL_BLOCK, + BAL_ALLOW_DEFAULT, + BAL_ALLOW_ALLOWLISTED_UID, + BAL_ALLOW_ALLOWLISTED_COMPONENT, + BAL_ALLOW_VISIBLE_WINDOW, + BAL_ALLOW_PENDING_INTENT, + BAL_ALLOW_BAL_PERMISSION, + BAL_ALLOW_SAW_PERMISSION, + BAL_ALLOW_GRACE_PERIOD, + BAL_ALLOW_FOREGROUND, + BAL_ALLOW_SDK_SANDBOX + }) + public @interface BalCode {} + + static final int BAL_BLOCK = 0; + + static final int BAL_ALLOW_DEFAULT = 1; + + // Following codes are in order of precedence + + /** Important UIDs which should be always allowed to launch activities */ + static final int BAL_ALLOW_ALLOWLISTED_UID = 2; + + /** Apps that fulfill a certain role that can can always launch new tasks */ + static final int BAL_ALLOW_ALLOWLISTED_COMPONENT = 3; + + /** Apps which currently have a visible window */ + static final int BAL_ALLOW_VISIBLE_WINDOW = 4; + + /** Allowed due to the PendingIntent sender */ + static final int BAL_ALLOW_PENDING_INTENT = 5; + + /** App has START_ACTIVITIES_FROM_BACKGROUND permission or BAL instrumentation privileges + * granted to it */ + static final int BAL_ALLOW_BAL_PERMISSION = 6; + + /** Process has SYSTEM_ALERT_WINDOW permission granted to it */ + static final int BAL_ALLOW_SAW_PERMISSION = 7; + + /** App is in grace period after an activity was started or finished */ + static final int BAL_ALLOW_GRACE_PERIOD = 8; + + /** App is in a foreground task or bound to a foreground service (but not itself visible) */ + static final int BAL_ALLOW_FOREGROUND = 9; + + /** Process belongs to a SDK sandbox */ + static final int BAL_ALLOW_SDK_SANDBOX = 10; + BackgroundActivityStartController( final ActivityTaskManagerService service, final ActivityTaskSupervisor supervisor) { mService = service; @@ -83,6 +138,27 @@ public class BackgroundActivityStartController { boolean allowBackgroundActivityStart, Intent intent, ActivityOptions checkedOptions) { + return checkBackgroundActivityStart(callingUid, callingPid, callingPackage, + realCallingUid, realCallingPid, callerApp, originatingPendingIntent, + allowBackgroundActivityStart, intent, checkedOptions) == BAL_BLOCK; + } + + /** + * @return A code denoting which BAL rule allows an activity to be started, + * or {@link BAL_BLOCK} if the launch should be blocked + */ + @BalCode + int checkBackgroundActivityStart( + int callingUid, + int callingPid, + final String callingPackage, + int realCallingUid, + int realCallingPid, + WindowProcessController callerApp, + PendingIntentRecord originatingPendingIntent, + boolean allowBackgroundActivityStart, + Intent intent, + ActivityOptions checkedOptions) { // don't abort for the most important UIDs final int callingAppId = UserHandle.getAppId(callingUid); final boolean useCallingUidState = @@ -93,32 +169,22 @@ public class BackgroundActivityStartController { if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID || callingAppId == Process.NFC_UID) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Activity start allowed for important callingUid (" + callingUid + ")"); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ false, callingUid, + BAL_ALLOW_ALLOWLISTED_UID, "Important callingUid"); } // Always allow home application to start activities. if (isHomeApp(callingUid, callingPackage)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Activity start allowed for home app callingUid (" + callingUid + ")"); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ false, callingUid, + BAL_ALLOW_ALLOWLISTED_COMPONENT, "Home app"); } // IME should always be allowed to start activity, like IME settings. final WindowState imeWindow = mService.mRootWindowContainer.getCurrentInputMethodWindow(); if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")"); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ false, callingUid, + BAL_ALLOW_ALLOWLISTED_COMPONENT, "Active ime"); } } @@ -145,15 +211,12 @@ public class BackgroundActivityStartController { && callingUidHasAnyVisibleWindow) || isCallingUidPersistentSystemProcess; if (useCallingUidState && allowCallingUidStartActivity) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Activity start allowed: callingUidHasAnyVisibleWindow = " - + callingUid - + ", isCallingUidPersistentSystemProcess = " - + isCallingUidPersistentSystemProcess); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ false, + BAL_ALLOW_VISIBLE_WINDOW, + "callingUidHasAnyVisibleWindow = " + + callingUid + + ", isCallingUidPersistentSystemProcess = " + + isCallingUidPersistentSystemProcess); } // take realCallingUid into consideration final int realCallingUidProcState = @@ -184,14 +247,9 @@ public class BackgroundActivityStartController { Process.getAppUidForSdkSandboxUid(UserHandle.getAppId(realCallingUid)); if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Activity start allowed: uid in SDK sandbox (" - + realCallingUid - + ") has visible (non-toast) window."); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ false, realCallingUid, + BAL_ALLOW_SDK_SANDBOX, + "uid in SDK sandbox has visible (non-toast) window"); } } @@ -209,100 +267,60 @@ public class BackgroundActivityStartController { -1, true) == PackageManager.PERMISSION_GRANTED) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Activity start allowed: realCallingUid (" - + realCallingUid - + ") has BAL permission."); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ false, callingUid, + BAL_ALLOW_PENDING_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) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Activity start allowed: realCallingUid (" - + realCallingUid - + ") has visible (non-toast) window"); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ false, + callingUid, BAL_ALLOW_PENDING_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 && allowBackgroundActivityStart) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Activity start allowed: realCallingUid (" - + realCallingUid - + ") is persistent system process AND intent sender allowed " - + "(allowBackgroundActivityStart = true)"); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ false, + callingUid, + BAL_ALLOW_PENDING_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)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Activity start allowed: realCallingUid (" - + realCallingUid - + ") is companion app"); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ false, callingUid, + BAL_ALLOW_PENDING_INTENT, "realCallingUid is a companion app. " + + "realCallingUid: " + realCallingUid); } } if (useCallingUidState) { // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission - if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) - == PERMISSION_GRANTED) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND " - + "permission granted for uid " - + callingUid); - } - return false; + if (ActivityTaskManagerService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, + callingPid, callingUid) == PERMISSION_GRANTED) { + return logStartAllowedAndReturnCode(/*background*/ true, callingUid, + BAL_ALLOW_BAL_PERMISSION, + "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)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Background activity start allowed: callingUid (" - + callingUid - + ") is recents"); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ true, callingUid, + BAL_ALLOW_ALLOWLISTED_COMPONENT, "Recents Component"); } // don't abort if the callingUid is the device owner if (mService.isDeviceOwner(callingUid)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Background activity start allowed: callingUid (" - + callingUid - + ") is device owner"); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ true, callingUid, + BAL_ALLOW_ALLOWLISTED_COMPONENT, "Device Owner"); } // don't abort if the callingUid has companion device final int callingUserId = UserHandle.getUserId(callingUid); if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Background activity start allowed: callingUid (" - + callingUid - + ") is companion app"); - } - return false; + return logStartAllowedAndReturnCode(/*background*/ true, callingUid, + BAL_ALLOW_ALLOWLISTED_COMPONENT, "Companion App"); } // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) { @@ -311,7 +329,8 @@ public class BackgroundActivityStartController { "Background activity start for " + callingPackage + " allowed because SYSTEM_ALERT_WINDOW permission is granted."); - return false; + return logStartAllowedAndReturnCode(/*background*/ true, callingUid, + BAL_ALLOW_SAW_PERMISSION, "SYSTEM_ALERT_WINDOW permission is granted"); } } // If we don't have callerApp at this point, no caller was provided to startActivity(). @@ -326,17 +345,12 @@ public class BackgroundActivityStartController { // 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 - if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Background activity start allowed: callerApp process (pid = " - + callerApp.getPid() - + ", uid = " - + callerAppUid - + ") is allowed"); - } - return false; + @BalCode int balAllowedForCaller = callerApp + .areBackgroundActivityStartsAllowed(appSwitchState); + if (balAllowedForCaller != BAL_BLOCK) { + return logStartAllowedAndReturnCode(/*background*/ true, balAllowedForCaller, + "callerApp process (pid = " + callerApp.getPid() + + ", uid = " + callerAppUid + ") is allowed"); } // only if that one wasn't allowed, check the other ones final ArraySet uidProcesses = @@ -344,18 +358,12 @@ public class BackgroundActivityStartController { 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 - && proc.areBackgroundActivityStartsAllowed(appSwitchState)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d( - TAG, - "Background activity start allowed: process " - + proc.getPid() - + " from uid " - + callerAppUid - + " is allowed"); - } - return false; + && balAllowedForUid != BAL_BLOCK) { + return logStartAllowedAndReturnCode(/*background*/ true, balAllowedForUid, + "process" + proc.getPid() + + " from uid " + callerAppUid + " is allowed"); } } } @@ -416,6 +424,34 @@ public class BackgroundActivityStartController { realCallingUidHasAnyVisibleWindow, (originatingPendingIntent != null)); } - return true; + return BAL_BLOCK; + } + + private int logStartAllowedAndReturnCode(boolean background, int callingUid, int code, + String msg) { + if (DEBUG_ACTIVITY_STARTS) { + return logStartAllowedAndReturnCode(background, code, + msg, "callingUid: " + callingUid); + } + return code; + } + + private int logStartAllowedAndReturnCode(boolean background, int code, + String... msg) { + if (DEBUG_ACTIVITY_STARTS) { + StringBuilder builder = new StringBuilder(); + if (background) { + builder.append("Background "); + } + builder.append("Activity start allowed: "); + for (int i = 0; i < msg.length; i++) { + builder.append(msg[i]); + builder.append(". "); + } + builder.append("BAL Code: "); + builder.append(code); + Slog.d(TAG, builder.toString()); + } + return code; } } diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java index 0afd872827834..020e9c582ebea 100644 --- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java +++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java @@ -22,6 +22,10 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS; import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW; import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_BAL_PERMISSION; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_FOREGROUND; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD; +import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK; import android.annotation.NonNull; import android.annotation.Nullable; @@ -71,7 +75,8 @@ class BackgroundLaunchProcessController { mBackgroundActivityStartCallback = callback; } - boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName, + @BackgroundActivityStartController.BalCode + int areBackgroundActivityStartsAllowed(int pid, int uid, String packageName, int appSwitchState, boolean isCheckingForFgsStart, boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges, long lastStopAppSwitchesTime, long lastActivityLaunchTime, @@ -93,7 +98,7 @@ class BackgroundLaunchProcessController { + ")] Activity start allowed: within " + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period"); } - return true; + return BAL_ALLOW_GRACE_PERIOD; } if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "[Process(" + pid + ")] Activity start within " @@ -110,7 +115,7 @@ class BackgroundLaunchProcessController { + ")] Activity start allowed: process instrumenting with background " + "activity starts privileges"); } - return true; + return BAL_ALLOW_BAL_PERMISSION; } // Allow if the caller has an activity in any foreground task. if (hasActivityInVisibleTask @@ -119,7 +124,7 @@ class BackgroundLaunchProcessController { Slog.d(TAG, "[Process(" + pid + ")] Activity start allowed: process has activity in foreground task"); } - return true; + return BAL_ALLOW_FOREGROUND; } // Allow if the caller is bound by a UID that's currently foreground. if (isBoundByForegroundUid()) { @@ -127,7 +132,7 @@ class BackgroundLaunchProcessController { Slog.d(TAG, "[Process(" + pid + ")] Activity start allowed: process bound by foreground uid"); } - return true; + return BAL_ALLOW_FOREGROUND; } // Allow if the flag was explicitly set. if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) { @@ -135,9 +140,9 @@ class BackgroundLaunchProcessController { Slog.d(TAG, "[Process(" + pid + ")] Activity start allowed: process allowed by token"); } - return true; + return BAL_ALLOW_BAL_PERMISSION; } - return false; + return BAL_BLOCK; } /** diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 682918b1e7ddc..04799204322d7 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -40,6 +40,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; +import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK; import static com.android.server.wm.WindowManagerService.MY_PID; import android.Manifest; @@ -544,15 +545,17 @@ public class WindowProcessController extends ConfigurationContainer Date: Thu, 19 Jan 2023 09:34:35 -0800 Subject: [PATCH 3/3] Merge "Log BAL and full screen intent to statsd." into tm-qpr-dev This is only for the BAL part. Full screen intent logging is being merged in another patch. Test: atest ActivityStarterTests Bug: 266010511 Change-Id: I4fdfbe80ea0c368a4809e5613e5f9c3a8369e2a3 Merged-In: I4fdfbe80ea0c368a4809e5613e5f9c3a8369e2a3 --- .../wm/BackgroundActivityStartController.java | 116 +++++++++++------- .../wm/BackgroundLaunchProcessController.java | 42 +++---- .../server/wm/ActivityStarterTests.java | 66 ++++++++++ 3 files changed, 153 insertions(+), 71 deletions(-) diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 2315795a003b2..c7e44b347f6b4 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -40,6 +40,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; import java.lang.annotation.Retention; @@ -169,22 +170,24 @@ public class BackgroundActivityStartController { if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID || callingAppId == Process.NFC_UID) { - return logStartAllowedAndReturnCode(/*background*/ false, callingUid, - BAL_ALLOW_ALLOWLISTED_UID, "Important callingUid"); + return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_UID, /*background*/ false, + callingUid, realCallingUid, intent, "Important callingUid"); } // Always allow home application to start activities. if (isHomeApp(callingUid, callingPackage)) { - return logStartAllowedAndReturnCode(/*background*/ false, callingUid, - BAL_ALLOW_ALLOWLISTED_COMPONENT, "Home app"); + return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT, + /*background*/ false, callingUid, realCallingUid, intent, + "Home app"); } // IME should always be allowed to start activity, like IME settings. final WindowState imeWindow = mService.mRootWindowContainer.getCurrentInputMethodWindow(); if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) { - return logStartAllowedAndReturnCode(/*background*/ false, callingUid, - BAL_ALLOW_ALLOWLISTED_COMPONENT, "Active ime"); + return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT, + /*background*/ false, callingUid, realCallingUid, intent, + "Active ime"); } } @@ -211,8 +214,8 @@ public class BackgroundActivityStartController { && callingUidHasAnyVisibleWindow) || isCallingUidPersistentSystemProcess; if (useCallingUidState && allowCallingUidStartActivity) { - return logStartAllowedAndReturnCode(/*background*/ false, - BAL_ALLOW_VISIBLE_WINDOW, + return logStartAllowedAndReturnCode(BAL_ALLOW_VISIBLE_WINDOW, + /*background*/ false, callingUid, realCallingUid, intent, "callingUidHasAnyVisibleWindow = " + callingUid + ", isCallingUidPersistentSystemProcess = " @@ -247,8 +250,8 @@ public class BackgroundActivityStartController { Process.getAppUidForSdkSandboxUid(UserHandle.getAppId(realCallingUid)); if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) { - return logStartAllowedAndReturnCode(/*background*/ false, realCallingUid, - BAL_ALLOW_SDK_SANDBOX, + return logStartAllowedAndReturnCode(BAL_ALLOW_SDK_SANDBOX, + /*background*/ false, callingUid, realCallingUid, intent, "uid in SDK sandbox has visible (non-toast) window"); } } @@ -267,25 +270,24 @@ public class BackgroundActivityStartController { -1, true) == PackageManager.PERMISSION_GRANTED) { - return logStartAllowedAndReturnCode(/*background*/ false, callingUid, - BAL_ALLOW_PENDING_INTENT, + 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(/*background*/ false, - callingUid, BAL_ALLOW_PENDING_INTENT, + 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 && allowBackgroundActivityStart) { - return logStartAllowedAndReturnCode(/*background*/ false, - callingUid, - BAL_ALLOW_PENDING_INTENT, + return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT, + /*background*/ false, callingUid, realCallingUid, intent, "realCallingUid is persistent system process AND intent " + "sender allowed (allowBackgroundActivityStart = true). " + "realCallingUid: " + realCallingUid); @@ -293,8 +295,9 @@ public class BackgroundActivityStartController { // don't abort if the realCallingUid is an associated companion app if (mService.isAssociatedCompanionApp( UserHandle.getUserId(realCallingUid), realCallingUid)) { - return logStartAllowedAndReturnCode(/*background*/ false, callingUid, - BAL_ALLOW_PENDING_INTENT, "realCallingUid is a companion app. " + return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT, + /*background*/ false, callingUid, realCallingUid, intent, + "realCallingUid is a companion app. " + "realCallingUid: " + realCallingUid); } } @@ -302,25 +305,28 @@ public class BackgroundActivityStartController { // 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(/*background*/ true, callingUid, - BAL_ALLOW_BAL_PERMISSION, + return logStartAllowedAndReturnCode(BAL_ALLOW_BAL_PERMISSION, + /*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(/*background*/ true, callingUid, - BAL_ALLOW_ALLOWLISTED_COMPONENT, "Recents Component"); + return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT, + /*background*/ true, callingUid, realCallingUid, + intent, "Recents Component"); } // don't abort if the callingUid is the device owner if (mService.isDeviceOwner(callingUid)) { - return logStartAllowedAndReturnCode(/*background*/ true, callingUid, - BAL_ALLOW_ALLOWLISTED_COMPONENT, "Device Owner"); + return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT, + /*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(/*background*/ true, callingUid, - BAL_ALLOW_ALLOWLISTED_COMPONENT, "Companion App"); + return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT, + /*background*/ true, callingUid, realCallingUid, + intent, "Companion App"); } // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) { @@ -329,8 +335,9 @@ public class BackgroundActivityStartController { "Background activity start for " + callingPackage + " allowed because SYSTEM_ALERT_WINDOW permission is granted."); - return logStartAllowedAndReturnCode(/*background*/ true, callingUid, - BAL_ALLOW_SAW_PERMISSION, "SYSTEM_ALERT_WINDOW permission is granted"); + return logStartAllowedAndReturnCode(BAL_ALLOW_SAW_PERMISSION, + /*background*/ true, callingUid, realCallingUid, + intent, "SYSTEM_ALERT_WINDOW permission is granted"); } } // If we don't have callerApp at this point, no caller was provided to startActivity(). @@ -348,7 +355,8 @@ public class BackgroundActivityStartController { @BalCode int balAllowedForCaller = callerApp .areBackgroundActivityStartsAllowed(appSwitchState); if (balAllowedForCaller != BAL_BLOCK) { - return logStartAllowedAndReturnCode(/*background*/ true, balAllowedForCaller, + return logStartAllowedAndReturnCode(balAllowedForCaller, + /*background*/ true, callingUid, realCallingUid, intent, "callerApp process (pid = " + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed"); } @@ -361,7 +369,8 @@ public class BackgroundActivityStartController { int balAllowedForUid = proc.areBackgroundActivityStartsAllowed(appSwitchState); if (proc != callerApp && balAllowedForUid != BAL_BLOCK) { - return logStartAllowedAndReturnCode(/*background*/ true, balAllowedForUid, + return logStartAllowedAndReturnCode(balAllowedForUid, + /*background*/ true, callingUid, realCallingUid, intent, "process" + proc.getPid() + " from uid " + callerAppUid + " is allowed"); } @@ -427,31 +436,48 @@ public class BackgroundActivityStartController { return BAL_BLOCK; } - private int logStartAllowedAndReturnCode(boolean background, int callingUid, int code, - String msg) { - if (DEBUG_ACTIVITY_STARTS) { - return logStartAllowedAndReturnCode(background, code, - msg, "callingUid: " + callingUid); - } - return code; + 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, + DEBUG_ACTIVITY_STARTS ? ("[Process(" + pid + ")]" + msg) : ""); } - private int logStartAllowedAndReturnCode(boolean background, int code, - String... msg) { + static @BalCode int logStartAllowedAndReturnCode(@BalCode int code, boolean background, + int callingUid, int realCallingUid, Intent intent, String msg) { + statsLogBalAllowed(code, callingUid, realCallingUid, intent); if (DEBUG_ACTIVITY_STARTS) { StringBuilder builder = new StringBuilder(); if (background) { builder.append("Background "); } - builder.append("Activity start allowed: "); - for (int i = 0; i < msg.length; i++) { - builder.append(msg[i]); - builder.append(". "); - } + builder.append("Activity start allowed: " + msg + ". callingUid: " + callingUid + ". "); builder.append("BAL Code: "); builder.append(code); Slog.d(TAG, builder.toString()); } return code; } + + private static void statsLogBalAllowed( + @BalCode int code, int callingUid, int realCallingUid, Intent intent) { + if (code == BAL_ALLOW_PENDING_INTENT + && (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) { + String activityName = + intent != null ? intent.getComponent().flattenToShortString() : ""; + FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED, + activityName, + code, + callingUid, + realCallingUid); + } + if (code == BAL_ALLOW_BAL_PERMISSION || code == BAL_ALLOW_FOREGROUND + || code == BAL_ALLOW_SAW_PERMISSION) { + // We don't need to know which activity in this case. + FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED, + /*activityName*/ "", + code, + callingUid, + realCallingUid); + } + } } diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java index 020e9c582ebea..cc47528f9950c 100644 --- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java +++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java @@ -93,12 +93,10 @@ class BackgroundLaunchProcessController { // let app to be able to start background activity even it's in grace period. if (lastActivityLaunchTime > lastStopAppSwitchesTime || lastActivityFinishTime > lastStopAppSwitchesTime) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "[Process(" + pid - + ")] Activity start allowed: within " - + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period"); - } - return BAL_ALLOW_GRACE_PERIOD; + return BackgroundActivityStartController.logStartAllowedAndReturnCode( + BAL_ALLOW_GRACE_PERIOD, /*background*/ true, uid, uid, /*intent*/ null, + pid, "Activity start allowed: within " + + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period"); } if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "[Process(" + pid + ")] Activity start within " @@ -110,37 +108,29 @@ class BackgroundLaunchProcessController { } // Allow if the proc is instrumenting with background activity starts privs. if (hasBackgroundActivityStartPrivileges) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "[Process(" + pid - + ")] Activity start allowed: process instrumenting with background " + return BackgroundActivityStartController.logStartAllowedAndReturnCode( + BAL_ALLOW_BAL_PERMISSION, /*background*/ true, uid, uid, /*intent*/ null, + pid, "Activity start allowed: process instrumenting with background " + "activity starts privileges"); - } - return BAL_ALLOW_BAL_PERMISSION; } // Allow if the caller has an activity in any foreground task. if (hasActivityInVisibleTask && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "[Process(" + pid - + ")] Activity start allowed: process has activity in foreground task"); - } - return BAL_ALLOW_FOREGROUND; + return BackgroundActivityStartController.logStartAllowedAndReturnCode( + BAL_ALLOW_FOREGROUND, /*background*/ false, uid, uid, /*intent*/ null, + pid, "Activity start allowed: process has activity in foreground task"); } // Allow if the caller is bound by a UID that's currently foreground. if (isBoundByForegroundUid()) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "[Process(" + pid - + ")] Activity start allowed: process bound by foreground uid"); - } - return BAL_ALLOW_FOREGROUND; + return BackgroundActivityStartController.logStartAllowedAndReturnCode( + BAL_ALLOW_FOREGROUND, /*background*/ false, uid, uid, /*intent*/ null, + pid, "Activity start allowed: process bound by foreground uid"); } // Allow if the flag was explicitly set. if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "[Process(" + pid - + ")] Activity start allowed: process allowed by token"); - } - return BAL_ALLOW_BAL_PERMISSION; + return BackgroundActivityStartController.logStartAllowedAndReturnCode( + BAL_ALLOW_BAL_PERMISSION, /*background*/ true, uid, uid, /*intent*/ null, + pid, "Activity start allowed: process allowed by token"); } return BAL_BLOCK; } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 1d859bf0198a5..0a6cb37a7fa99 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -16,7 +16,9 @@ package com.android.server.wm; +import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.app.Activity.RESULT_CANCELED; +import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManager.START_ABORTED; import static android.app.ActivityManager.START_CANCELED; @@ -42,6 +44,7 @@ import static android.content.pm.ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBE import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.SYSTEM_UID; import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations; @@ -49,6 +52,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -73,6 +77,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyObject; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.notNull; @@ -103,6 +108,8 @@ import android.window.TaskFragmentOrganizerToken; import androidx.test.filters.SmallTest; +import com.android.internal.util.FrameworkStatsLog; +import com.android.server.am.PendingIntentRecord; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.wm.LaunchParamsController.LaunchParamsModifier; import com.android.server.wm.utils.MockTracker; @@ -110,6 +117,8 @@ import com.android.server.wm.utils.MockTracker; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; import java.util.Arrays; import java.util.HashSet; @@ -729,6 +738,63 @@ public class ActivityStarterTests extends WindowTestsBase { isCallingUidDeviceOwner, false /* isPinnedSingleInstance */); } + /** + * This test ensures proper logging for BAL_ALLOW_PERMISSION. + */ + @Test + public void testBackgroundActivityStartsAllowed_logging() { + doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); + MockitoSession mockingSession = mockitoSession() + .mockStatic(ActivityTaskManagerService.class) + .mockStatic(FrameworkStatsLog.class) + .strictness(Strictness.LENIENT) + .startMocking(); + doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission( + eq(START_ACTIVITIES_FROM_BACKGROUND), + anyInt(), anyInt())); + runAndVerifyBackgroundActivityStartsSubtest( + "allowed_notAborted", false, + UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, + UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, + false, true, false, false, false); + verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED, + "", // activity name + BackgroundActivityStartController.BAL_ALLOW_BAL_PERMISSION, + UNIMPORTANT_UID, + UNIMPORTANT_UID2)); + mockingSession.finishMocking(); + } + + /** + * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT. + */ + @Test + public void testBackgroundActivityStartsAllowed_loggingPendingIntentAllowed() { + doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); + MockitoSession mockingSession = mockitoSession() + .mockStatic(ActivityTaskManagerService.class) + .mockStatic(FrameworkStatsLog.class) + .mockStatic(PendingIntentRecord.class) + .strictness(Strictness.LENIENT) + .startMocking(); + doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission( + eq(START_ACTIVITIES_FROM_BACKGROUND), + anyInt(), anyInt())); + doReturn(true).when( + () -> PendingIntentRecord.isPendingIntentBalAllowedByCaller(anyObject())); + runAndVerifyBackgroundActivityStartsSubtest( + "allowed_notAborted", false, + UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, + Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP, + false, true, false, false, false); + verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED, + DEFAULT_COMPONENT_PACKAGE_NAME + "/" + DEFAULT_COMPONENT_PACKAGE_NAME, + BackgroundActivityStartController.BAL_ALLOW_PENDING_INTENT, + UNIMPORTANT_UID, + Process.SYSTEM_UID)); + mockingSession.finishMocking(); + } + private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted, int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState, int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,