summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java9
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java31
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java14
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java15
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java29
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java8
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java34
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java13
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java39
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java85
-rw-r--r--services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java5
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);