diff options
7 files changed, 481 insertions, 302 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 4663662c5585..39106f61d515 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 aad89b441732..d6c1d7ffe47f 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<WindowProcessController> 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 491e58b63f76..9236ab63e7f9 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 0bfc48b4b54c..b160af6a3e11 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 000000000000..d515a277e692 --- /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. + * + * <p>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<WindowProcessController> 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 6602d29883e4..4f506a5dd945 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 b4ffc2adbeea..e8d5c0014711 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); |