diff options
15 files changed, 239 insertions, 62 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 9e15c1fca077..ef30cb46318c 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -318,12 +318,14 @@ public abstract class ActivityManagerInternal { int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky, - @UserIdInt int userId, boolean allowBackgroundActivityStarts); + @UserIdInt int userId, boolean allowBackgroundActivityStarts, + @Nullable IBinder backgroundActivityStartsToken); public abstract ComponentName startServiceInPackage(int uid, Intent service, String resolvedType, boolean fgRequired, String callingPackage, @Nullable String callingFeatureId, @UserIdInt int userId, - boolean allowBackgroundActivityStarts) throws TransactionTooLargeException; + boolean allowBackgroundActivityStarts, + @Nullable IBinder backgroundActivityStartsToken) throws TransactionTooLargeException; public abstract void disconnectActivityFromServices(Object connectionHolder); public abstract void cleanUpServices(@UserIdInt int userId, ComponentName component, diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index bd51c7a1773d..33a92e6ad0ac 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -4881,12 +4881,9 @@ public final class ActiveServices { if (instr != null && instr.mHasBackgroundActivityStartsPermission) { return true; } - } - - final boolean hasAllowBackgroundActivityStartsToken = r.app != null - ? !r.app.mAllowBackgroundActivityStartsTokens.isEmpty() : false; - if (hasAllowBackgroundActivityStartsToken) { - return true; + if (r.app.areBackgroundActivityStartsAllowedByToken()) { + return true; + } } if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index cfd2bf913b9c..555bf7d222f8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4325,11 +4325,11 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent, null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null, - false, false, resolvedUserId, false); + false, false, resolvedUserId, false, null); } else { broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent, null, null, 0, null, null, null, null, false, false, resolvedUserId, - false); + false, null); } if (observer != null) { @@ -15713,7 +15713,7 @@ public class ActivityManagerService extends IActivityManager.Stub BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, null, null, null, -1, -1, false, null, null, OP_NONE, null, receivers, - null, 0, null, null, false, true, true, -1, false, + null, 0, null, null, false, true, true, -1, false, null, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */); queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); @@ -15960,7 +15960,7 @@ public class ActivityManagerService extends IActivityManager.Stub resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId, false /* allowBackgroundActivityStarts */, - null /*broadcastWhitelist*/); + null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastWhitelist */); } @GuardedBy("this") @@ -15970,6 +15970,7 @@ public class ActivityManagerService extends IActivityManager.Stub Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, int realCallingPid, int userId, boolean allowBackgroundActivityStarts, + @Nullable IBinder backgroundActivityStartsToken, @Nullable int[] broadcastWhitelist) { intent = new Intent(intent); @@ -16060,6 +16061,8 @@ public class ActivityManagerService extends IActivityManager.Stub throw new SecurityException(msg); } else { allowBackgroundActivityStarts = true; + // We set the token to null since if it wasn't for it we'd allow anyway here + backgroundActivityStartsToken = null; } } } @@ -16528,7 +16531,8 @@ public class ActivityManagerService extends IActivityManager.Stub callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, - allowBackgroundActivityStarts, timeoutExempt); + allowBackgroundActivityStarts, backgroundActivityStartsToken, + timeoutExempt); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r); final boolean replaced = replacePending && (queue.replaceParallelBroadcastLocked(r) != null); @@ -16625,7 +16629,8 @@ public class ActivityManagerService extends IActivityManager.Stub callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, - allowBackgroundActivityStarts, timeoutExempt); + allowBackgroundActivityStarts, backgroundActivityStartsToken, + timeoutExempt); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r); @@ -16785,7 +16790,8 @@ public class ActivityManagerService extends IActivityManager.Stub int realCallingUid, int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky, - int userId, boolean allowBackgroundActivityStarts) { + int userId, boolean allowBackgroundActivityStarts, + @Nullable IBinder backgroundActivityStartsToken) { synchronized(this) { intent = verifyBroadcastLocked(intent); @@ -16797,6 +16803,7 @@ public class ActivityManagerService extends IActivityManager.Stub resultTo, resultCode, resultData, resultExtras, requiredPermissions, OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid, realCallingPid, userId, allowBackgroundActivityStarts, + backgroundActivityStartsToken, null /*broadcastWhitelist*/); } finally { Binder.restoreCallingIdentity(origId); @@ -19487,12 +19494,14 @@ public class ActivityManagerService extends IActivityManager.Stub int realCallingUid, int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky, - int userId, boolean allowBackgroundActivityStarts) { + int userId, boolean allowBackgroundActivityStarts, + @Nullable IBinder backgroundActivityStartsToken) { synchronized (ActivityManagerService.this) { return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId, uid, realCallingUid, realCallingPid, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermission, bOptions, - serialized, sticky, userId, allowBackgroundActivityStarts); + serialized, sticky, userId, allowBackgroundActivityStarts, + backgroundActivityStartsToken); } } @@ -19514,6 +19523,7 @@ public class ActivityManagerService extends IActivityManager.Stub null /*resultExtras*/, requiredPermissions, AppOpsManager.OP_NONE, null /*options*/, serialized, false /*sticky*/, callingPid, callingUid, callingUid, callingPid, userId, false /*allowBackgroundStarts*/, + null /*tokenNeededForBackgroundActivityStarts*/, appIdWhitelist); } finally { Binder.restoreCallingIdentity(origId); @@ -19525,7 +19535,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType, boolean fgRequired, String callingPackage, @Nullable String callingFeatureId, - int userId, boolean allowBackgroundActivityStarts) + int userId, boolean allowBackgroundActivityStarts, + @Nullable IBinder backgroundActivityStartsToken) throws TransactionTooLargeException { synchronized(ActivityManagerService.this) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 1fa62c6c40ba..12937b988beb 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -1677,7 +1677,7 @@ public final class BroadcastQueue { // that request - we don't want the token to be swept from under our feet... mHandler.removeCallbacksAndMessages(msgToken); // ...then add the token - proc.addAllowBackgroundActivityStartsToken(r); + proc.addAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken); } final void setBroadcastTimeoutLocked(long timeoutTime) { diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 40743b8be1ea..198ba34e3956 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -92,6 +92,9 @@ final class BroadcastRecord extends Binder { // if set to true, app's process will be temporarily allowed to start activities from background // for the duration of the broadcast dispatch final boolean allowBackgroundActivityStarts; + // token used to trace back the grant for activity starts, optional + @Nullable + final IBinder mBackgroundActivityStartsToken; static final int IDLE = 0; static final int APP_RECEIVE = 1; @@ -240,7 +243,8 @@ final class BroadcastRecord extends Binder { String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers, IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras, boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId, - boolean _allowBackgroundActivityStarts, boolean _timeoutExempt) { + boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken, + boolean timeoutExempt) { if (_intent == null) { throw new NullPointerException("Can't construct with a null intent"); } @@ -270,8 +274,9 @@ final class BroadcastRecord extends Binder { userId = _userId; nextReceiver = 0; state = IDLE; - allowBackgroundActivityStarts = _allowBackgroundActivityStarts; - timeoutExempt = _timeoutExempt; + this.allowBackgroundActivityStarts = allowBackgroundActivityStarts; + mBackgroundActivityStartsToken = backgroundActivityStartsToken; + this.timeoutExempt = timeoutExempt; } /** @@ -317,6 +322,7 @@ final class BroadcastRecord extends Binder { manifestSkipCount = from.manifestSkipCount; queue = from.queue; allowBackgroundActivityStarts = from.allowBackgroundActivityStarts; + mBackgroundActivityStartsToken = from.mBackgroundActivityStartsToken; timeoutExempt = from.timeoutExempt; } @@ -352,7 +358,7 @@ final class BroadcastRecord extends Binder { callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId, - allowBackgroundActivityStarts, timeoutExempt); + allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt); split.splitToken = this.splitToken; return split; diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 1997dbd6fc37..fbfed34d1bf0 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -437,15 +437,17 @@ public final class PendingIntentRecord extends IIntentSender.Stub { break; case ActivityManager.INTENT_SENDER_BROADCAST: try { + final boolean allowedByToken = + mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken); + final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null; + // If a completion callback has been requested, require // that the broadcast be delivered synchronously int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName, key.featureId, uid, callingUid, callingPid, finalIntent, resolvedType, finishedReceiver, code, null, null, requiredPermission, options, (finishedReceiver != null), false, - userId, - mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken) - || allowTrampoline); + userId, allowedByToken || allowTrampoline, bgStartsToken); if (sent == ActivityManager.BROADCAST_SUCCESS) { sendFinish = false; } @@ -456,11 +458,14 @@ public final class PendingIntentRecord extends IIntentSender.Stub { case ActivityManager.INTENT_SENDER_SERVICE: case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE: try { + final boolean allowedByToken = + mAllowBgActivityStartsForServiceSender.contains(whitelistToken); + final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null; + controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType, key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE, key.packageName, key.featureId, userId, - mAllowBgActivityStartsForServiceSender.contains(whitelistToken) - || allowTrampoline); + allowedByToken || allowTrampoline, bgStartsToken); } catch (RuntimeException e) { Slog.w(TAG, "Unable to send startService intent", e); } catch (TransactionTooLargeException e) { diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 6e1bd8faeaf9..cd4302bb42fa 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -25,6 +25,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityManagerService.MY_PID; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; @@ -274,9 +275,6 @@ class ProcessRecord implements WindowProcessListener { final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>(); // All ContentProviderRecord process is using final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>(); - // A set of tokens that currently contribute to this process being temporarily allowed - // to start activities even if it's not in the foreground - final ArraySet<Binder> mAllowBackgroundActivityStartsTokens = new ArraySet<>(); // a set of UIDs of all bound clients private ArraySet<Integer> mBoundClientUids = new ArraySet<>(); @@ -627,13 +625,6 @@ class ProcessRecord implements WindowProcessListener { pw.print(prefix); pw.print(" - "); pw.println(receivers.valueAt(i)); } } - if (mAllowBackgroundActivityStartsTokens.size() > 0) { - pw.print(prefix); pw.println("Background activity start tokens:"); - for (int i = 0; i < mAllowBackgroundActivityStartsTokens.size(); i++) { - pw.print(prefix); pw.print(" - "); - pw.println(mAllowBackgroundActivityStartsTokens.valueAt(i)); - } - } } ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName, @@ -1331,17 +1322,23 @@ class ProcessRecord implements WindowProcessListener { return mUsingWrapper; } - void addAllowBackgroundActivityStartsToken(Binder entity) { + /** + * Allows background activity starts using token {@param entity}. Optionally, you can provide + * {@param originatingToken} if you have one such originating token, this is useful for tracing + * back the grant in the case of the notification token. + */ + void addAllowBackgroundActivityStartsToken(Binder entity, @Nullable IBinder originatingToken) { if (entity == null) return; - mAllowBackgroundActivityStartsTokens.add(entity); - mWindowProcessController.setAllowBackgroundActivityStarts(true); + mWindowProcessController.addAllowBackgroundActivityStartsToken(entity, originatingToken); } void removeAllowBackgroundActivityStartsToken(Binder entity) { if (entity == null) return; - mAllowBackgroundActivityStartsTokens.remove(entity); - mWindowProcessController.setAllowBackgroundActivityStarts( - !mAllowBackgroundActivityStartsTokens.isEmpty()); + mWindowProcessController.removeAllowBackgroundActivityStartsToken(entity); + } + + boolean areBackgroundActivityStartsAllowedByToken() { + return mWindowProcessController.areBackgroundActivityStartsAllowedByToken(); } void addBoundClientUid(int clientUid) { diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index db05d65b92fe..022b04d89774 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -575,7 +575,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN ? _proc : null; if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) { - _proc.addAllowBackgroundActivityStartsToken(this); + _proc.addAllowBackgroundActivityStartsToken(this, null); } else { _proc.removeAllowBackgroundActivityStartsToken(this); } @@ -723,7 +723,9 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN * {@code mIsAllowedBgActivityStartsByBinding}. If either is true, this ServiceRecord * should be contributing as a token in parent ProcessRecord. * - * @see com.android.server.am.ProcessRecord#mAllowBackgroundActivityStartsTokens + * @see com.android.server.am.ProcessRecord#addAllowBackgroundActivityStartsToken(Binder, + * IBinder) + * @see com.android.server.am.ProcessRecord#removeAllowBackgroundActivityStartsToken(Binder) */ private void updateParentProcessBgActivityStartsToken() { if (app == null) { @@ -732,7 +734,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) { // if the token is already there it's safe to "re-add it" - we're dealing with // a set of Binder objects - app.addAllowBackgroundActivityStartsToken(this); + app.addAllowBackgroundActivityStartsToken(this, null); } else { app.removeAllowBackgroundActivityStartsToken(this); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 37cc6b2453fe..cf48e026e830 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -274,6 +274,7 @@ import com.android.server.policy.PhoneWindowManager; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; +import com.android.server.wm.BackgroundActivityStartCallback; import com.android.server.wm.WindowManagerInternal; import libcore.io.IoUtils; @@ -1894,6 +1895,7 @@ public class NotificationManagerService extends SystemService { (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); mAm = am; mAtm = atm; + mAtm.setBackgroundActivityStartCallback(new NotificationTrampolineCallback()); mUgm = ugm; mUgmInternal = ugmInternal; mPackageManager = packageManager; @@ -9924,4 +9926,36 @@ public class NotificationManagerService extends SystemService { if (TextUtils.isEmpty(val)) return defValue; return Boolean.parseBoolean(val); } + + /** + * Shows a warning on logcat. Shows the toast only once per package. This is to avoid being too + * aggressive and annoying the user. + * + * TODO(b/161957908): Remove dogfooder toast. + */ + private class NotificationTrampolineCallback implements BackgroundActivityStartCallback { + private Set<String> mPackagesShown = new ArraySet<>(); + + @Override + public IBinder getToken() { + return WHITELIST_TOKEN; + } + + @Override + public void onExclusiveTokenActivityStart(String packageName) { + Slog.w(TAG, "Indirect notification activity start from " + packageName); + boolean isFirstOccurrence = mPackagesShown.add(packageName); + if (!isFirstOccurrence) { + return; + } + + mUiHandler.post(() -> + Toast.makeText(getUiContext(), + "Indirect activity start from " + + packageName + ". " + + "This will be blocked in S.\n" + + "See go/s-trampolines.", + Toast.LENGTH_LONG).show()); + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index a903bcd3d728..777ddda89e9d 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -221,6 +221,14 @@ public abstract class ActivityTaskManagerInternal { boolean allowBackgroundActivityStart); /** + * Callback to be called on certain activity start scenarios. + * + * @see BackgroundActivityStartCallback + */ + public abstract void setBackgroundActivityStartCallback( + @Nullable BackgroundActivityStartCallback callback); + + /** * Start activity {@code intent} without calling user-id check. * * - DO NOT call it with the calling UID cleared. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 07c11a1c9332..c4af3e2c04c9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -674,6 +674,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { WindowOrganizerController mWindowOrganizerController; TaskOrganizerController mTaskOrganizerController; + @Nullable + private BackgroundActivityStartCallback mBackgroundActivityStartCallback; + private int mDeviceOwnerUid = Process.INVALID_UID; private final class FontScaleSettingObserver extends ContentObserver { @@ -1000,6 +1003,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return config; } + @Nullable + public BackgroundActivityStartCallback getBackgroundActivityStartCallback() { + return mBackgroundActivityStartCallback; + } + private void start() { LocalServices.addService(ActivityTaskManagerInternal.class, mInternal); } @@ -6136,6 +6144,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return false; } + public void setBackgroundActivityStartCallback( + @Nullable BackgroundActivityStartCallback backgroundActivityStartCallback) { + mBackgroundActivityStartCallback = backgroundActivityStartCallback; + } + @Override public int startActivitiesAsPackage(String packageName, @Nullable String featureId, int userId, Intent[] intents, Bundle bOptions) { diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java new file mode 100644 index 000000000000..4e742b96c8d0 --- /dev/null +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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 android.os.IBinder; + +/** + * Callback to be called when a background activity start is allowed exclusively because of the + * token provided in {@link #getToken()}. + */ +public interface BackgroundActivityStartCallback { + /** + * The token that allowed the activity start that triggered {@link + * #onExclusiveTokenActivityStart()}. + * + * Ideally this should just return a final variable, don't do anything costly here (don't hold + * any locks). + */ + IBinder getToken(); + + /** + * Called when the background activity start happens. + */ + void onExclusiveTokenActivityStart(String packageName); +} diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index da9c7f3ea1b5..6ba8769842f6 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -55,11 +55,14 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; +import android.os.Binder; import android.os.Build; +import android.os.IBinder; import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -98,6 +101,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio final ApplicationInfo mInfo; final String mName; final int mUid; + + // A set of tokens that currently contribute to this process being temporarily allowed + // to start activities even if it's not in the foreground. The values of this map are optional + // (can be null) and are used to trace back the grant to the notification token mechanism. + private final ArrayMap<Binder, IBinder> mBackgroundActivityStartTokens = new ArrayMap<>(); // The process of this application; 0 if none private volatile int mPid; // user of process. @@ -160,9 +168,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private volatile boolean mPerceptible; // Set to true when process was launched with a wrapper attached private volatile boolean mUsingWrapper; - // Set to true if this process is currently temporarily allowed to start activities even if - // it's not in the foreground - private volatile boolean mAllowBackgroundActivityStarts; // Set of UIDs of clients currently bound to this process private volatile ArraySet<Integer> mBoundClientUids = new ArraySet<Integer>(); @@ -208,6 +213,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** Whether our process is currently running a {@link IRemoteAnimationRunner} */ private boolean mRunningRemoteAnimation; + @Nullable + private BackgroundActivityStartCallback mBackgroundActivityStartCallback; + public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info, String name, int uid, int userId, Object owner, WindowProcessListener listener) { mInfo = info; @@ -218,6 +226,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mListener = listener; mAtm = atm; mDisplayId = INVALID_DISPLAY; + mBackgroundActivityStartCallback = mAtm.getBackgroundActivityStartCallback(); boolean isSysUiPackage = info.packageName.equals( mAtm.getSysUiServiceComponentLocked().getPackageName()); @@ -449,19 +458,39 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mLastActivityFinishTime = finishTime; } - public void setAllowBackgroundActivityStarts(boolean allowBackgroundActivityStarts) { - mAllowBackgroundActivityStarts = allowBackgroundActivityStarts; + /** + * Allows background activity starts using token {@code entity}. Optionally, you can provide + * {@code originatingToken} if you have one such originating token, this is useful for tracing + * back the grant in the case of the notification token. + */ + public void addAllowBackgroundActivityStartsToken(Binder entity, + @Nullable IBinder originatingToken) { + synchronized (mAtm.mGlobalLock) { + mBackgroundActivityStartTokens.put(entity, originatingToken); + } } - boolean areBackgroundActivityStartsAllowed() { - // allow if the flag was explicitly set - if (mAllowBackgroundActivityStarts) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "[WindowProcessController(" + mPid - + ")] Activity start allowed: mAllowBackgroundActivityStarts = true"); - } - return true; + /** + * Removes token {@code entity} that allowed background activity starts added via {@link + * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}. + */ + public void removeAllowBackgroundActivityStartsToken(Binder entity) { + synchronized (mAtm.mGlobalLock) { + mBackgroundActivityStartTokens.remove(entity); + } + } + + /** + * Returns true if background activity starts are allowed by any token added via {@link + * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}. + */ + public boolean areBackgroundActivityStartsAllowedByToken() { + synchronized (mAtm.mGlobalLock) { + return !mBackgroundActivityStartTokens.isEmpty(); } + } + + boolean areBackgroundActivityStartsAllowed() { // allow if any activity in the caller has either started or finished very recently, and // it must be started or finished after last stop app switches time. final long now = SystemClock.uptimeMillis(); @@ -510,9 +539,32 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } return true; } + // allow if the flag was explicitly set + if (!mBackgroundActivityStartTokens.isEmpty()) { + onBackgroundStartAllowedByToken(); + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "[WindowProcessController(" + mPid + + ")] Activity start allowed: process allowed by token"); + } + return true; + } return false; } + private void onBackgroundStartAllowedByToken() { + if (mBackgroundActivityStartCallback == null) { + return; + } + IBinder callbackToken = mBackgroundActivityStartCallback.getToken(); + for (IBinder token : mBackgroundActivityStartTokens.values()) { + if (token != callbackToken) { + return; + } + } + mAtm.mH.post(() -> + mBackgroundActivityStartCallback.onExclusiveTokenActivityStart(mInfo.packageName)); + } + private boolean isBoundByForegroundUid() { for (int i = mBoundClientUids.size() - 1; i >= 0; --i) { if (mAtm.isUidForeground(mBoundClientUids.valueAt(i))) { @@ -1434,6 +1486,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio if (mVrThreadTid != 0) { pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid); } + if (mBackgroundActivityStartTokens.size() > 0) { + pw.print(prefix); pw.println("Background activity start tokens:"); + for (int i = 0; i < mBackgroundActivityStartTokens.size(); i++) { + pw.print(prefix); pw.print(" - "); + pw.println(mBackgroundActivityStartTokens.keyAt(i)); + } + } } pw.println(prefix + " Configuration=" + getConfiguration()); pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration()); diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java index a871ec64af3b..5bef877e2f39 100644 --- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java +++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java @@ -195,7 +195,8 @@ public class BroadcastRecordTest { false /* sticky */, false /* initialSticky */, userId, - false, /* allowBackgroundActivityStarts */ + false /* allowBackgroundActivityStarts */, + null /* activityStartsToken */, false /* timeoutExempt */ ); } } 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 3772e2500c1b..d07000f30046 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -74,6 +74,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManagerInternal; import android.graphics.Rect; +import android.os.Binder; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; @@ -665,7 +666,9 @@ public class ActivityStarterTests extends ActivityTestsBase { mService.mStackSupervisor.setRecentTasks(recentTasks); doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid); // caller is temp allowed - callerApp.setAllowBackgroundActivityStarts(callerIsTempAllowed); + if (callerIsTempAllowed) { + callerApp.addAllowBackgroundActivityStartsToken(new Binder(), null); + } // caller is instrumenting with background activity starts privileges callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges, callerIsInstrumentingWithBackgroundActivityStartPrivileges); |