Merge "Prepare to restrict PI sender BAL privileges."
diff --git a/core/api/current.txt b/core/api/current.txt
index 4b1335e..a6446ff 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4635,8 +4635,9 @@
method @Nullable public android.graphics.Rect getLaunchBounds();
method public int getLaunchDisplayId();
method public boolean getLockTaskMode();
+ method public int getPendingIntentBackgroundActivityStartMode();
method public int getSplashScreenStyle();
- method public boolean isPendingIntentBackgroundActivityLaunchAllowed();
+ method @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed();
method public boolean isShareIdentityEnabled();
method public static android.app.ActivityOptions makeBasic();
method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
@@ -4653,13 +4654,17 @@
method public android.app.ActivityOptions setLaunchBounds(@Nullable android.graphics.Rect);
method public android.app.ActivityOptions setLaunchDisplayId(int);
method public android.app.ActivityOptions setLockTaskEnabled(boolean);
- method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
+ method @Deprecated public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
+ method @NonNull public android.app.ActivityOptions setPendingIntentBackgroundActivityStartMode(int);
method @NonNull public android.app.ActivityOptions setShareIdentityEnabled(boolean);
method @NonNull public android.app.ActivityOptions setSplashScreenStyle(int);
method public android.os.Bundle toBundle();
method public void update(android.app.ActivityOptions);
field public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
field public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+ field public static final int MODE_BACKGROUND_ACTIVITY_START_ALLOWED = 1; // 0x1
+ field public static final int MODE_BACKGROUND_ACTIVITY_START_DENIED = 2; // 0x2
+ field public static final int MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED = 0; // 0x0
}
public class AlarmManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a77ee08..4e0e4b9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -808,8 +808,9 @@
method @Nullable public android.content.IntentFilter getDeliveryGroupMatchingFilter();
method @Nullable public String getDeliveryGroupMatchingKey();
method public int getDeliveryGroupPolicy();
+ method public int getPendingIntentBackgroundActivityStartMode();
method public boolean isDeferUntilActive();
- method public boolean isPendingIntentBackgroundActivityLaunchAllowed();
+ method @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed();
method public static android.app.BroadcastOptions makeBasic();
method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
@@ -818,7 +819,8 @@
method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String, @NonNull String);
method @NonNull public android.app.BroadcastOptions setDeliveryGroupPolicy(int);
method public void setDontSendToRestrictedApps(boolean);
- method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
+ method @Deprecated public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
+ method @NonNull public android.app.BroadcastOptions setPendingIntentBackgroundActivityStartMode(int);
method public void setRequireAllOfPermissions(@Nullable String[]);
method public void setRequireCompatChange(long, boolean);
method public void setRequireNoneOfPermissions(@Nullable String[]);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index a381fea..ae032db 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -440,14 +440,14 @@
IApplicationThread resultToThread, IIntentReceiver resultTo, int resultCode,
String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions,
boolean serialized, boolean sticky, @UserIdInt int userId,
- boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken,
+ BackgroundStartPrivileges backgroundStartPrivileges,
@Nullable int[] broadcastAllowList);
public abstract ComponentName startServiceInPackage(int uid, Intent service,
String resolvedType, boolean fgRequired, String callingPackage,
@Nullable String callingFeatureId, @UserIdInt int userId,
- boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken) throws TransactionTooLargeException;
+ BackgroundStartPrivileges backgroundStartPrivileges)
+ throws TransactionTooLargeException;
public abstract void disconnectActivityFromServices(Object connectionHolder);
public abstract void cleanUpServices(@UserIdInt int userId, ComponentName component,
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 2214c8e..57214e0 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -2402,6 +2402,32 @@
}
+ /**
+ * Sets the mode for allowing or denying the senders privileges to start background activities
+ * to the PendingIntent.
+ *
+ * This is typically used in when executing {@link PendingIntent#send(Context, int, Intent,
+ * PendingIntent.OnFinished, Handler, String, Bundle)} or similar
+ * methods. A privileged sender of a PendingIntent should only grant
+ * {@link #MODE_BACKGROUND_ACTIVITY_START_ALLOWED} if the PendingIntent is from a trusted source
+ * and/or executed on behalf the user.
+ */
+ public @NonNull ActivityOptions setPendingIntentBackgroundActivityStartMode(
+ @BackgroundActivityStartMode int state) {
+ super.setPendingIntentBackgroundActivityStartMode(state);
+ return this;
+ }
+
+ /**
+ * Get the mode for allowing or denying the senders privileges to start background activities
+ * to the PendingIntent.
+ *
+ * @see #setPendingIntentBackgroundActivityStartMode(int)
+ */
+ public @BackgroundActivityStartMode int getPendingIntentBackgroundActivityStartMode() {
+ return super.getPendingIntentBackgroundActivityStartMode();
+ }
+
/** @hide */
@Override
public String toString() {
diff --git a/core/java/android/app/BackgroundStartPrivileges.java b/core/java/android/app/BackgroundStartPrivileges.java
new file mode 100644
index 0000000..76c0ccf
--- /dev/null
+++ b/core/java/android/app/BackgroundStartPrivileges.java
@@ -0,0 +1,184 @@
+/*
+ * 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 android.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Privileges granted to a Process that allows it to execute starts from the background.
+ * @hide
+ */
+public class BackgroundStartPrivileges {
+ /** No privileges. */
+ public static final BackgroundStartPrivileges NONE = new BackgroundStartPrivileges(
+ false, false, null);
+ /** Allow activity starts (and implies allowing foreground service starts). */
+ public static final BackgroundStartPrivileges ALLOW_BAL = new BackgroundStartPrivileges(
+ true, true, null);
+ /** Allow foreground service starts. */
+ public static final BackgroundStartPrivileges ALLOW_FGS = new BackgroundStartPrivileges(
+ false, true, null);
+
+ private final boolean mAllowsBackgroundActivityStarts;
+ private final boolean mAllowsBackgroundForegroundServiceStarts;
+ private final IBinder mOriginatingToken;
+
+ private BackgroundStartPrivileges(boolean allowsBackgroundActivityStarts,
+ boolean allowsBackgroundForegroundServiceStarts, @Nullable IBinder originatingToken) {
+ Preconditions.checkArgument(
+ !allowsBackgroundActivityStarts || allowsBackgroundForegroundServiceStarts,
+ "backgroundActivityStarts implies bgFgServiceStarts");
+ mAllowsBackgroundActivityStarts = allowsBackgroundActivityStarts;
+ mAllowsBackgroundForegroundServiceStarts = allowsBackgroundForegroundServiceStarts;
+ mOriginatingToken = originatingToken;
+ }
+
+ /**
+ * Return a token that allows background activity starts and attributes it to a specific
+ * originatingToken.
+ */
+ public static BackgroundStartPrivileges allowBackgroundActivityStarts(
+ @Nullable IBinder originatingToken) {
+ if (originatingToken == null) {
+ // try to avoid creating new instances
+ return ALLOW_BAL;
+ }
+ return new BackgroundStartPrivileges(true, true, originatingToken);
+ }
+
+ /**
+ * Merge this {@link BackgroundStartPrivileges} with another {@link BackgroundStartPrivileges}.
+ *
+ * The resulting object will grant the union of the privileges of the merged objects.
+ * The originating tokens is retained only if both {@link BackgroundStartPrivileges} are the
+ * same.
+ *
+ * If one of the merged objects is {@link #NONE} then the other object is returned and the
+ * originating token is NOT cleared.
+ */
+ public @NonNull BackgroundStartPrivileges merge(@Nullable BackgroundStartPrivileges other) {
+ // shortcuts in case
+ if (other == NONE || other == null) {
+ return this;
+ }
+ if (this == NONE) {
+ return other;
+ }
+
+ boolean allowsBackgroundActivityStarts =
+ this.allowsBackgroundActivityStarts() || other.allowsBackgroundActivityStarts();
+ boolean allowsBackgroundFgsStarts =
+ this.allowsBackgroundFgsStarts() || other.allowsBackgroundFgsStarts();
+ if (this.mOriginatingToken == other.mOriginatingToken) {
+ // can reuse this?
+ if (this.mAllowsBackgroundActivityStarts == allowsBackgroundActivityStarts
+ && this.mAllowsBackgroundForegroundServiceStarts == allowsBackgroundFgsStarts) {
+ return this;
+ }
+ // can reuse other?
+ if (other.mAllowsBackgroundActivityStarts == allowsBackgroundActivityStarts
+ && other.mAllowsBackgroundForegroundServiceStarts == allowsBackgroundFgsStarts) {
+ return other;
+ }
+ // need to create a new instance (this should never happen)
+ return new BackgroundStartPrivileges(allowsBackgroundActivityStarts,
+ allowsBackgroundFgsStarts, this.mOriginatingToken);
+ } else {
+ // no originating token -> can use standard instance
+ if (allowsBackgroundActivityStarts) {
+ return ALLOW_BAL;
+ } else if (allowsBackgroundFgsStarts) {
+ return ALLOW_FGS;
+ } else {
+ return NONE;
+ }
+ }
+ }
+
+ /**
+ * Merge a collection of {@link BackgroundStartPrivileges} into a single token.
+ *
+ * The resulting object will grant the union of the privileges of the merged objects.
+ * The originating tokens is retained only if all {@link BackgroundStartPrivileges} are the
+ * same.
+ *
+ * If the list contains {@link #NONE}s these are ignored.
+ */
+ public static @NonNull BackgroundStartPrivileges merge(
+ @Nullable List<BackgroundStartPrivileges> list) {
+ if (list == null || list.isEmpty()) {
+ return NONE;
+ }
+ BackgroundStartPrivileges current = list.get(0);
+ for (int i = list.size(); i-- > 1; ) {
+ current = current.merge(list.get(i));
+ }
+ return current;
+ }
+
+ /**
+ * @return {@code true} if this grants the permission to start background activities from the
+ * background.
+ */
+ public boolean allowsBackgroundActivityStarts() {
+ return mAllowsBackgroundActivityStarts;
+ }
+
+ /**
+ * @return {@code true} this grants the permission to start foreground services from the
+ * background. */
+ public boolean allowsBackgroundFgsStarts() {
+ return mAllowsBackgroundForegroundServiceStarts;
+ }
+
+ /** @return true if this grants any privileges. */
+ public boolean allowsAny() {
+ return mAllowsBackgroundActivityStarts || mAllowsBackgroundForegroundServiceStarts;
+ }
+
+ /** Return true if this grants no privileges. */
+ public boolean allowsNothing() {
+ return !allowsAny();
+ }
+
+ /**
+ * Gets the originating token.
+ *
+ * The originating token is optional information that allows to trace back the origin of this
+ * object. Besides debugging, this is used to e.g. identify privileges created by the
+ * notification service.
+ */
+ public @Nullable IBinder getOriginatingToken() {
+ return mOriginatingToken;
+ }
+
+ @Override
+ public String toString() {
+ return "BackgroundStartPrivileges["
+ + "allowsBackgroundActivityStarts=" + mAllowsBackgroundActivityStarts
+ + ", allowsBackgroundForegroundServiceStarts="
+ + mAllowsBackgroundForegroundServiceStarts
+ + ", originatingToken=" + mOriginatingToken
+ + ']';
+ }
+}
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 5cf10d0..9ecf8ff 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -389,26 +389,6 @@
}
/**
- * Set PendingIntent activity is allowed to be started in the background if the caller
- * can start background activities.
- * @hide
- */
- @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
- public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) {
- super.setPendingIntentBackgroundActivityLaunchAllowed(allowed);
- }
-
- /**
- * Get PendingIntent activity is allowed to be started in the background if the caller
- * can start background activities.
- * @hide
- */
- @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
- public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
- return super.isPendingIntentBackgroundActivityLaunchAllowed();
- }
-
- /**
* Return {@link #setTemporaryAppAllowlist}.
* @hide
*/
@@ -937,6 +917,25 @@
}
/**
+ * Sets the mode for allowing or denying the senders privileges to start background activities
+ * to the PendingIntent.
+ *
+ * This is typically used when executing {@link PendingIntent#send(Bundle)} or similar
+ * methods. A privileged sender of a PendingIntent should only grant
+ * MODE_BACKGROUND_ACTIVITY_START_ALLOWED if the PendingIntent is from a trusted source and/or
+ * executed on behalf the user.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @Override // to narrow down the return type
+ public BroadcastOptions setPendingIntentBackgroundActivityStartMode(int state) {
+ super.setPendingIntentBackgroundActivityStartMode(state);
+ return this;
+ }
+
+ /**
* Returns the created options as a Bundle, which can be passed to
* {@link android.content.Context#sendBroadcast(android.content.Intent)
* Context.sendBroadcast(Intent)} and related methods.
diff --git a/core/java/android/app/ComponentOptions.java b/core/java/android/app/ComponentOptions.java
index 74db39f..3776369 100644
--- a/core/java/android/app/ComponentOptions.java
+++ b/core/java/android/app/ComponentOptions.java
@@ -16,21 +16,22 @@
package android.app;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.os.Bundle;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
+ * Base class for {@link ActivityOptions} and {@link BroadcastOptions}.
* @hide
*/
public class ComponentOptions {
/**
- * Default value for KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED.
- * @hide
- **/
- public static final boolean PENDING_INTENT_BAL_ALLOWED_DEFAULT = true;
-
- /**
* PendingIntent caller allows activity start even if PendingIntent creator is in background.
* This only works if the PendingIntent caller is allowed to start background activities,
* for example if it's in the foreground, or has BAL permission.
@@ -52,10 +53,23 @@
*/
public static final String KEY_INTERACTIVE = "android:component.isInteractive";
- private boolean mPendingIntentBalAllowed = PENDING_INTENT_BAL_ALLOWED_DEFAULT;
+ private @Nullable Boolean mPendingIntentBalAllowed = null;
private boolean mPendingIntentBalAllowedByPermission = false;
private boolean mIsInteractive = false;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"MODE_BACKGROUND_ACTIVITY_START_"}, value = {
+ MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED,
+ MODE_BACKGROUND_ACTIVITY_START_ALLOWED,
+ MODE_BACKGROUND_ACTIVITY_START_DENIED})
+ public @interface BackgroundActivityStartMode {}
+ /** No explicit value chosen. The system will decide whether to grant privileges. */
+ public static final int MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED = 0;
+ /** Allow the {@link PendingIntent} to use the background activity start privileges. */
+ public static final int MODE_BACKGROUND_ACTIVITY_START_ALLOWED = 1;
+ /** Deny the {@link PendingIntent} to use the background activity start privileges. */
+ public static final int MODE_BACKGROUND_ACTIVITY_START_DENIED = 2;
+
ComponentOptions() {
}
@@ -63,12 +77,16 @@
// If the remote side sent us bad parcelables, they won't get the
// results they want, which is their loss.
opts.setDefusable(true);
- setPendingIntentBackgroundActivityLaunchAllowed(
- opts.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
- PENDING_INTENT_BAL_ALLOWED_DEFAULT));
+
+ boolean pendingIntentBalAllowedIsSetExplicitly =
+ opts.containsKey(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED);
+ if (pendingIntentBalAllowedIsSetExplicitly) {
+ mPendingIntentBalAllowed =
+ opts.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED);
+ }
setPendingIntentBackgroundActivityLaunchAllowedByPermission(
- opts.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION,
- false));
+ opts.getBoolean(
+ KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, false));
mIsInteractive = opts.getBoolean(KEY_INTERACTIVE, false);
}
@@ -97,20 +115,74 @@
/**
* Set PendingIntent activity is allowed to be started in the background if the caller
* can start background activities.
+ *
+ * @deprecated use #setPendingIntentBackgroundActivityStartMode(int) to set the full range
+ * of states
*/
- public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) {
+ @Deprecated public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) {
mPendingIntentBalAllowed = allowed;
}
/**
- * Get PendingIntent activity is allowed to be started in the background if the caller
- * can start background activities.
+ * Get PendingIntent activity is allowed to be started in the background if the caller can start
+ * background activities.
+ *
+ * @deprecated use {@link #getPendingIntentBackgroundActivityStartMode()} since for apps
+ * targeting {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or higher this value might
+ * not match the actual behavior if the value was not explicitly set.
*/
- public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
+ @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
+ if (mPendingIntentBalAllowed == null) {
+ // cannot return null, so return the value used up to API level 33 for compatibility
+ return true;
+ }
return mPendingIntentBalAllowed;
}
/**
+ * Sets the mode for allowing or denying the senders privileges to start background activities
+ * to the PendingIntent.
+ *
+ * This is typically used in when executing {@link PendingIntent#send(Bundle)} or similar
+ * methods. A privileged sender of a PendingIntent should only grant
+ * {@link #MODE_BACKGROUND_ACTIVITY_START_ALLOWED} if the PendingIntent is from a trusted source
+ * and/or executed on behalf the user.
+ */
+ public @NonNull ComponentOptions setPendingIntentBackgroundActivityStartMode(
+ @BackgroundActivityStartMode int state) {
+ switch (state) {
+ case MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
+ mPendingIntentBalAllowed = null;
+ break;
+ case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
+ mPendingIntentBalAllowed = true;
+ break;
+ case MODE_BACKGROUND_ACTIVITY_START_DENIED:
+ mPendingIntentBalAllowed = false;
+ break;
+ default:
+ throw new IllegalArgumentException(state + " is not valid");
+ }
+ return this;
+ }
+
+ /**
+ * Gets the mode for allowing or denying the senders privileges to start background activities
+ * to the PendingIntent.
+ *
+ * @see #setPendingIntentBackgroundActivityStartMode(int)
+ */
+ public @BackgroundActivityStartMode int getPendingIntentBackgroundActivityStartMode() {
+ if (mPendingIntentBalAllowed == null) {
+ return MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
+ } else if (mPendingIntentBalAllowed) {
+ return MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+ } else {
+ return MODE_BACKGROUND_ACTIVITY_START_DENIED;
+ }
+ }
+
+ /**
* Set PendingIntent activity can be launched from background if caller has BAL permission.
* @hide
*/
@@ -129,7 +201,9 @@
public Bundle toBundle() {
Bundle b = new Bundle();
- b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, mPendingIntentBalAllowed);
+ if (mPendingIntentBalAllowed != null) {
+ b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, mPendingIntentBalAllowed);
+ }
if (mPendingIntentBalAllowedByPermission) {
b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION,
mPendingIntentBalAllowedByPermission);
diff --git a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
new file mode 100644
index 0000000..982ad63
--- /dev/null
+++ b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2023 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 android.app;
+
+import static android.app.BackgroundStartPrivileges.ALLOW_BAL;
+import static android.app.BackgroundStartPrivileges.ALLOW_FGS;
+import static android.app.BackgroundStartPrivileges.NONE;
+import static android.app.BackgroundStartPrivileges.allowBackgroundActivityStarts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Binder;
+import android.os.IBinder;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BackgroundStartPrivilegesTest {
+
+ private static final IBinder BINDER_A = new Binder();
+ private static final IBinder BINDER_B = new Binder();
+ private static final BackgroundStartPrivileges BSP_ALLOW_A =
+ allowBackgroundActivityStarts(BINDER_A);
+ private static final BackgroundStartPrivileges BSP_ALLOW_B =
+ allowBackgroundActivityStarts(BINDER_B);
+
+ @Test
+ public void backgroundStartPrivilege_getters_work() {
+ assertThat(ALLOW_BAL.getOriginatingToken()).isNull();
+ assertThat(ALLOW_BAL.allowsBackgroundActivityStarts()).isEqualTo(true);
+ assertThat(ALLOW_BAL.allowsBackgroundFgsStarts()).isEqualTo(true);
+ assertThat(ALLOW_BAL.allowsAny()).isEqualTo(true);
+ assertThat(ALLOW_BAL.allowsNothing()).isEqualTo(false);
+
+ assertThat(ALLOW_FGS.getOriginatingToken()).isNull();
+ assertThat(ALLOW_FGS.allowsBackgroundActivityStarts()).isEqualTo(false);
+ assertThat(ALLOW_FGS.allowsBackgroundFgsStarts()).isEqualTo(true);
+ assertThat(ALLOW_FGS.allowsAny()).isEqualTo(true);
+ assertThat(ALLOW_FGS.allowsNothing()).isEqualTo(false);
+
+ assertThat(NONE.getOriginatingToken()).isNull();
+ assertThat(NONE.allowsBackgroundActivityStarts()).isEqualTo(false);
+ assertThat(NONE.allowsBackgroundFgsStarts()).isEqualTo(false);
+ assertThat(NONE.allowsAny()).isEqualTo(false);
+ assertThat(NONE.allowsNothing()).isEqualTo(true);
+
+ assertThat(BSP_ALLOW_A.getOriginatingToken()).isEqualTo(BINDER_A);
+ assertThat(BSP_ALLOW_A.allowsBackgroundActivityStarts()).isEqualTo(true);
+ assertThat(BSP_ALLOW_A.allowsBackgroundFgsStarts()).isEqualTo(true);
+ assertThat(BSP_ALLOW_A.allowsAny()).isEqualTo(true);
+ assertThat(BSP_ALLOW_A.allowsNothing()).isEqualTo(false);
+ }
+
+ @Test
+ public void backgroundStartPrivilege_toString_returnsSomething() {
+ assertThat(ALLOW_BAL.toString()).isNotEmpty();
+ assertThat(ALLOW_FGS.toString()).isNotEmpty();
+ assertThat(NONE.toString()).isNotEmpty();
+ assertThat(BSP_ALLOW_A.toString()).isNotEmpty();
+ }
+
+ @Test
+ public void backgroundStartPrivilege_mergeAA_resultsInA() {
+ assertThat(BSP_ALLOW_A.merge(BSP_ALLOW_A)).isEqualTo(BSP_ALLOW_A);
+ }
+
+ @Test
+ public void backgroundStartPrivilege_mergeAB_resultsInAllowBal() {
+ assertThat(BSP_ALLOW_A.merge(BSP_ALLOW_B)).isEqualTo(ALLOW_BAL);
+ }
+
+ @Test
+ public void backgroundStartPrivilege_mergeAwithAllowBal_resultsInAllowBal() {
+ assertThat(BSP_ALLOW_A.merge(ALLOW_BAL)).isEqualTo(ALLOW_BAL);
+ }
+
+ @Test
+ public void backgroundStartPrivilege_mergeAwithAllowFgs_resultsInAllowBal() {
+ assertThat(BSP_ALLOW_A.merge(ALLOW_FGS)).isEqualTo(ALLOW_BAL);
+ }
+
+ @Test
+ public void backgroundStartPrivilege_mergeAwithNone_resultsInA() {
+ assertThat(BSP_ALLOW_A.merge(NONE)).isEqualTo(BSP_ALLOW_A);
+ }
+
+ @Test
+ public void backgroundStartPrivilege_mergeManyWithDifferentToken_resultsInAllowBal() {
+ assertThat(BackgroundStartPrivileges.merge(
+ Arrays.asList(BSP_ALLOW_A, BSP_ALLOW_B, NONE, BSP_ALLOW_A, ALLOW_FGS)))
+ .isEqualTo(ALLOW_BAL);
+ }
+
+ @Test
+ public void backgroundStartPrivilege_mergeManyWithSameToken_resultsInAllowBal() {
+ assertThat(BackgroundStartPrivileges.merge(
+ Arrays.asList(BSP_ALLOW_A, BSP_ALLOW_A, BSP_ALLOW_A, BSP_ALLOW_A)))
+ .isEqualTo(BSP_ALLOW_A);
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 9154231..6719fdb 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -112,6 +112,7 @@
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.BackgroundStartPrivileges;
import android.app.ForegroundServiceDelegationOptions;
import android.app.ForegroundServiceStartNotAllowedException;
import android.app.ForegroundServiceTypePolicy;
@@ -733,13 +734,13 @@
@Nullable String callingFeatureId, final int userId)
throws TransactionTooLargeException {
return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
- callingPackage, callingFeatureId, userId, false, null);
+ callingPackage, callingFeatureId, userId, BackgroundStartPrivileges.NONE);
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired,
String callingPackage, @Nullable String callingFeatureId, final int userId,
- boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken)
+ BackgroundStartPrivileges backgroundStartPrivileges)
throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
@@ -777,7 +778,7 @@
// the timeout)
ServiceRecord r = res.record;
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, userId,
- allowBackgroundActivityStarts, false /* isBindService */);
+ backgroundStartPrivileges, false /* isBindService */);
if (!mAm.mUserController.exists(r.userId)) {
Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
@@ -894,8 +895,8 @@
// The package could be frozen (meaning it's doing surgery), defer the actual
// start until the package is unfrozen.
if (deferServiceBringupIfFrozenLocked(r, service, callingPackage, callingFeatureId,
- callingUid, callingPid, fgRequired, callerFg, userId, allowBackgroundActivityStarts,
- backgroundActivityStartsToken, false, null)) {
+ callingUid, callingPid, fgRequired, callerFg, userId,
+ backgroundStartPrivileges, false, null)) {
return null;
}
@@ -916,8 +917,8 @@
final ComponentName realResult =
startServiceInnerLocked(r, service, callingUid, callingPid,
getCallingProcessNameLocked(callingUid, callingPid, callingPackage),
- fgRequired, callerFg, allowBackgroundActivityStarts,
- backgroundActivityStartsToken);
+ fgRequired, callerFg,
+ backgroundStartPrivileges);
if (res.aliasComponent != null
&& !realResult.getPackageName().startsWith("!")
&& !realResult.getPackageName().startsWith("?")) {
@@ -937,8 +938,9 @@
private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service,
int callingUid, int callingPid, String callingProcessName, boolean fgRequired,
- boolean callerFg, boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken) throws TransactionTooLargeException {
+ boolean callerFg,
+ BackgroundStartPrivileges backgroundStartPrivileges)
+ throws TransactionTooLargeException {
NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent(
service, callingUid, r.packageName, r.userId);
if (unscheduleServiceRestartLocked(r, callingUid, false)) {
@@ -1031,8 +1033,8 @@
"Not potential delay (user " + r.userId + " not started): " + r);
}
}
- if (allowBackgroundActivityStarts) {
- r.allowBgActivityStartsOnServiceStart(backgroundActivityStartsToken);
+ if (backgroundStartPrivileges.allowsAny()) {
+ r.allowBgActivityStartsOnServiceStart(backgroundStartPrivileges);
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting,
callingUid, callingProcessName, wasStartRequested);
@@ -1140,7 +1142,7 @@
private boolean deferServiceBringupIfFrozenLocked(ServiceRecord s, Intent serviceIntent,
String callingPackage, @Nullable String callingFeatureId,
int callingUid, int callingPid, boolean fgRequired, boolean callerFg, int userId,
- boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken,
+ BackgroundStartPrivileges backgroundStartPrivileges,
boolean isBinding, IServiceConnection connection) {
final PackageManagerInternal pm = mAm.getPackageManagerInternal();
final boolean frozen = pm.isPackageFrozen(s.packageName, callingUid, s.userId);
@@ -1188,7 +1190,7 @@
try {
startServiceInnerLocked(s, serviceIntent, callingUid, callingPid,
callingProcessName, fgRequired, callerFg,
- allowBackgroundActivityStarts, backgroundActivityStartsToken);
+ backgroundStartPrivileges);
} catch (TransactionTooLargeException e) {
/* ignore - local call */
}
@@ -2007,7 +2009,7 @@
resetFgsRestrictionLocked(r);
setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
- false /* allowBackgroundActivityStarts */,
+ BackgroundStartPrivileges.NONE,
false /* isBindService */);
final String temp = "startForegroundDelayMs:" + delayMs;
if (r.mInfoAllowStartForeground != null) {
@@ -2027,7 +2029,7 @@
// started. Check for app state again.
setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
- false /* allowBackgroundActivityStarts */,
+ BackgroundStartPrivileges.NONE,
false /* isBindService */);
}
// If the foreground service is not started from TOP process, do not allow it to
@@ -3301,7 +3303,8 @@
// The package could be frozen (meaning it's doing surgery), defer the actual
// binding until the package is unfrozen.
boolean packageFrozen = deferServiceBringupIfFrozenLocked(s, service, callingPackage, null,
- callingUid, callingPid, false, callerFg, userId, false, null, true, connection);
+ callingUid, callingPid, false, callerFg, userId, BackgroundStartPrivileges.NONE,
+ true, connection);
// If permissions need a review before any of the app components can run,
// we schedule binding to the service but do not start its process, then
@@ -3400,7 +3403,7 @@
}
}
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
- false /* allowBackgroundActivityStarts */, true /* isBindService */);
+ BackgroundStartPrivileges.NONE, true /* isBindService */);
if (s.app != null) {
ProcessServiceRecord servicePsr = s.app.mServices;
@@ -7056,7 +7059,7 @@
*/
private void setFgsRestrictionLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
- boolean allowBackgroundActivityStarts, boolean isBindService) {
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime();
// Check DeviceConfig flag.
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
@@ -7066,7 +7069,7 @@
if (!r.mAllowWhileInUsePermissionInFgs
|| (r.mAllowStartForeground == REASON_DENIED)) {
final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
- callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts,
+ callingPackage, callingPid, callingUid, r, backgroundStartPrivileges,
isBindService);
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
@@ -7074,7 +7077,7 @@
if (r.mAllowStartForeground == REASON_DENIED) {
r.mAllowStartForeground = shouldAllowFgsStartForegroundWithBindingCheckLocked(
allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,
- userId, isBindService);
+ backgroundStartPrivileges, isBindService);
}
}
}
@@ -7094,10 +7097,10 @@
}
final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
callingPackage, callingPid, callingUid, null /* serviceRecord */,
- false /* allowBackgroundActivityStarts */, false);
+ BackgroundStartPrivileges.NONE, false);
@ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundNoBindingCheckLocked(
allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */,
- false /* isBindService */);
+ BackgroundStartPrivileges.NONE);
if (allowStartFgs == REASON_DENIED) {
if (canBindingClientStartFgsLocked(callingUid) != null) {
@@ -7117,7 +7120,7 @@
*/
private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
int callingPid, int callingUid, @Nullable ServiceRecord targetService,
- boolean allowBackgroundActivityStarts, boolean isBindService) {
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
int ret = REASON_DENIED;
final int uidState = mAm.getUidStateLocked(callingUid);
@@ -7138,7 +7141,7 @@
if (ret == REASON_DENIED) {
// Is the allow activity background start flag on?
- if (allowBackgroundActivityStarts) {
+ if (backgroundStartPrivileges.allowsBackgroundActivityStarts()) {
ret = REASON_START_ACTIVITY_FLAG;
}
}
@@ -7271,12 +7274,13 @@
shouldAllowFgsWhileInUsePermissionLocked(
clientPackageName,
clientPid, clientUid, null /* serviceRecord */,
- false /* allowBackgroundActivityStarts */, false);
+ BackgroundStartPrivileges.NONE, false);
final @ReasonCode int allowStartFgs =
shouldAllowFgsStartForegroundNoBindingCheckLocked(
allowWhileInUse2,
clientPid, clientUid, clientPackageName,
- null /* targetService */, false);
+ null /* targetService */,
+ BackgroundStartPrivileges.NONE);
if (allowStartFgs != REASON_DENIED) {
return new Pair<>(allowStartFgs, clientPackageName);
} else {
@@ -7308,11 +7312,12 @@
*/
private @ReasonCode int shouldAllowFgsStartForegroundWithBindingCheckLocked(
@ReasonCode int allowWhileInUse, String callingPackage, int callingPid,
- int callingUid, Intent intent, ServiceRecord r, int userId, boolean isBindService) {
+ int callingUid, Intent intent, ServiceRecord r,
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
ActivityManagerService.FgsTempAllowListItem tempAllowListReason =
r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid);
int ret = shouldAllowFgsStartForegroundNoBindingCheckLocked(allowWhileInUse, callingPid,
- callingUid, callingPackage, r, isBindService);
+ callingUid, callingPackage, r, backgroundStartPrivileges);
String bindFromPackage = null;
if (ret == REASON_DENIED) {
@@ -7358,7 +7363,8 @@
private @ReasonCode int shouldAllowFgsStartForegroundNoBindingCheckLocked(
@ReasonCode int allowWhileInUse, int callingPid, int callingUid, String callingPackage,
- @Nullable ServiceRecord targetService, boolean isBindService) {
+ @Nullable ServiceRecord targetService,
+ BackgroundStartPrivileges backgroundStartPrivileges) {
int ret = allowWhileInUse;
if (ret == REASON_DENIED) {
@@ -7406,6 +7412,12 @@
}
if (ret == REASON_DENIED) {
+ if (backgroundStartPrivileges.allowsBackgroundFgsStarts()) {
+ ret = REASON_START_ACTIVITY_FLAG;
+ }
+ }
+
+ if (ret == REASON_DENIED) {
if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
callingPackage)) {
ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
@@ -7489,6 +7501,7 @@
ret = REASON_OPT_OUT_REQUESTED;
}
}
+
return ret;
}
@@ -7652,7 +7665,7 @@
String callingPackage) {
return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid,
/* targetService */ null,
- /* allowBackgroundActivityStarts */ false, false)
+ BackgroundStartPrivileges.NONE, false)
!= REASON_DENIED;
}
@@ -7759,7 +7772,7 @@
r.mFgsEnterTime = SystemClock.uptimeMillis();
r.foregroundServiceType = options.mForegroundServiceTypes;
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId,
- false, false);
+ BackgroundStartPrivileges.NONE, false);
final ProcessServiceRecord psr = callerApp.mServices;
final boolean newService = psr.startService(r);
// updateOomAdj.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 09c4eb2..9752e8b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -183,6 +183,7 @@
import android.app.ApplicationErrorReport;
import android.app.ApplicationExitInfo;
import android.app.ApplicationThreadConstants;
+import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.ComponentOptions;
import android.app.ContentProviderHolder;
@@ -3990,8 +3991,8 @@
null /* resultData */, null /* resultExtras */,
isInstantApp ? permission.ACCESS_INSTANT_APPS : null,
null /* bOptions */, false /* serialized */, false /* sticky */,
- resolvedUserId, false /* allowBackgroundActivityStarts */,
- null /* backgroundActivityStartsToken */, visibilityAllowList);
+ resolvedUserId, BackgroundStartPrivileges.NONE,
+ visibilityAllowList);
}
if (observer != null) {
@@ -4538,8 +4539,7 @@
null /* requiredPermissions */, null /* excludedPermissions */,
null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* ordered */,
false /* sticky */, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
- Binder.getCallingPid(), userId, false /* allowBackgroundActivityStarts */,
- null /* backgroundActivityStartsToken */,
+ Binder.getCallingPid(), userId, BackgroundStartPrivileges.NONE,
broadcastAllowList, null /* filterExtrasForReceiver */);
}
@@ -13774,8 +13774,9 @@
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, null, -1, -1, false, null, null, null, null, OP_NONE, null,
- receivers, null, null, 0, null, null, false, true, true, -1, false,
- null, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */,
+ receivers, null, null, 0, null, null, false, true, true, -1,
+ BackgroundStartPrivileges.NONE,
+ false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */,
null /* filterExtrasForReceiver */);
queue.enqueueBroadcastLocked(r);
}
@@ -14052,8 +14053,7 @@
resolvedType, null, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, excludedPermissions, excludedPackages, appOp, bOptions,
ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
- false /* allowBackgroundActivityStarts */,
- null /* tokenNeededForBackgroundActivityStarts */,
+ BackgroundStartPrivileges.NONE,
null /* broadcastAllowList */, null /* filterExtrasForReceiver */);
}
@@ -14065,8 +14065,7 @@
String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int realCallingUid, int realCallingPid, int userId,
- boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken,
+ BackgroundStartPrivileges backgroundStartPrivileges,
@Nullable int[] broadcastAllowList,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
final int cookie = BroadcastQueue.traceBegin("broadcastIntentLockedTraced");
@@ -14074,7 +14073,7 @@
intent, resolvedType, resultToApp, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, excludedPermissions, excludedPackages, appOp, bOptions,
ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
- allowBackgroundActivityStarts, backgroundActivityStartsToken, broadcastAllowList,
+ backgroundStartPrivileges, broadcastAllowList,
filterExtrasForReceiver);
BroadcastQueue.traceEnd(cookie);
return res;
@@ -14088,8 +14087,7 @@
String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int realCallingUid, int realCallingPid, int userId,
- boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken,
+ BackgroundStartPrivileges backgroundStartPrivileges,
@Nullable int[] broadcastAllowList,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
// Ensure all internal loopers are registered for idle checks
@@ -14210,9 +14208,8 @@
Slog.w(TAG, msg);
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;
+ backgroundStartPrivileges = BackgroundStartPrivileges.ALLOW_BAL;
}
}
@@ -14731,8 +14728,8 @@
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
registeredReceivers, resultToApp, resultTo, resultCode, resultData,
- resultExtras, ordered, sticky, false, userId, allowBackgroundActivityStarts,
- backgroundActivityStartsToken, timeoutExempt, filterExtrasForReceiver);
+ resultExtras, ordered, sticky, false, userId,
+ backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
queue.enqueueBroadcastLocked(r);
registeredReceivers = null;
@@ -14825,8 +14822,8 @@
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
receivers, resultToApp, resultTo, resultCode, resultData, resultExtras,
- ordered, sticky, false, userId, allowBackgroundActivityStarts,
- backgroundActivityStartsToken, timeoutExempt, filterExtrasForReceiver);
+ ordered, sticky, false, userId,
+ backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
queue.enqueueBroadcastLocked(r);
@@ -14971,7 +14968,7 @@
intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
appOp, bOptions, serialized, sticky, callingPid, callingUid, callingUid,
- callingPid, userId, false, null, null, null);
+ callingPid, userId, BackgroundStartPrivileges.NONE, null, null);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -14983,8 +14980,9 @@
int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode,
String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions,
- boolean serialized, boolean sticky, int userId, boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken, @Nullable int[] broadcastAllowList) {
+ boolean serialized, boolean sticky, int userId,
+ BackgroundStartPrivileges backgroundStartPrivileges,
+ @Nullable int[] broadcastAllowList) {
synchronized(this) {
intent = verifyBroadcastLocked(intent);
@@ -14995,8 +14993,8 @@
return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
resultToApp, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, null, null, OP_NONE, bOptions, serialized, sticky, -1,
- uid, realCallingUid, realCallingPid, userId, allowBackgroundActivityStarts,
- backgroundActivityStartsToken, broadcastAllowList,
+ uid, realCallingUid, realCallingPid, userId,
+ backgroundStartPrivileges, broadcastAllowList,
null /* filterExtrasForReceiver */);
} finally {
Binder.restoreCallingIdentity(origId);
@@ -17583,16 +17581,16 @@
IApplicationThread resultToThread, IIntentReceiver resultTo, int resultCode,
String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions,
boolean serialized, boolean sticky, int userId,
- boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken,
+ BackgroundStartPrivileges backgroundStartPrivileges,
@Nullable int[] broadcastAllowList) {
synchronized (ActivityManagerService.this) {
final ProcessRecord resultToApp = getRecordForAppLOSP(resultToThread);
return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId,
uid, realCallingUid, realCallingPid, intent, resolvedType, resultToApp,
resultTo, resultCode, resultData, resultExtras, requiredPermission,
- bOptions, serialized, sticky, userId, allowBackgroundActivityStarts,
- backgroundActivityStartsToken, broadcastAllowList);
+ bOptions, serialized, sticky, userId,
+ backgroundStartPrivileges,
+ broadcastAllowList);
}
}
@@ -17618,8 +17616,7 @@
null /*excludedPermissions*/, null /*excludedPackages*/,
AppOpsManager.OP_NONE, bOptions /*options*/, serialized,
false /*sticky*/, callingPid, callingUid, callingUid, callingPid,
- userId, false /*allowBackgroundStarts*/,
- null /*tokenNeededForBackgroundActivityStarts*/,
+ userId, BackgroundStartPrivileges.NONE,
appIdAllowList, filterExtrasForReceiver);
} finally {
Binder.restoreCallingIdentity(origId);
@@ -17646,8 +17643,7 @@
@Override
public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
boolean fgRequired, String callingPackage, @Nullable String callingFeatureId,
- int userId, boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken)
+ int userId, BackgroundStartPrivileges backgroundStartPrivileges)
throws TransactionTooLargeException {
if (DEBUG_SERVICE) {
Slog.v(TAG_SERVICE,
@@ -17664,8 +17660,8 @@
synchronized (ActivityManagerService.this) {
res = mServices.startServiceLocked(null, service,
resolvedType, -1, uid, fgRequired, callingPackage,
- callingFeatureId, userId, allowBackgroundActivityStarts,
- backgroundActivityStartsToken);
+ callingFeatureId, userId,
+ backgroundStartPrivileges);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index a86efa3..b1a01cc 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -561,7 +561,7 @@
// ...then schedule the removal of the token after the extended timeout
mHandler.postAtTime(() -> {
synchronized (mService) {
- app.removeAllowBackgroundActivityStartsToken(r);
+ app.removeBackgroundStartPrivileges(r);
}
}, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
}
@@ -603,11 +603,11 @@
if (state == BroadcastRecord.IDLE) {
Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
}
- if (r.allowBackgroundActivityStarts && r.curApp != null) {
+ if (r.mBackgroundStartPrivileges.allowsAny() && r.curApp != null) {
if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
// if the receiver has run for more than allowed bg activity start timeout,
// just remove the token for this process now and we're done
- r.curApp.removeAllowBackgroundActivityStartsToken(r);
+ r.curApp.removeBackgroundStartPrivileges(r);
} else {
// It gets more time; post the removal to happen at the appropriate moment
postActivityStartTokenRemoval(r.curApp, r);
@@ -840,7 +840,7 @@
} else {
r.receiverTime = SystemClock.uptimeMillis();
r.scheduledTime[index] = r.receiverTime;
- maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+ maybeAddBackgroundStartPrivileges(filter.receiverList.app, r);
maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid);
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
@@ -852,7 +852,8 @@
// parallel broadcasts are fire-and-forget, not bookended by a call to
// finishReceiverLocked(), so we manage their activity-start token here
if (filter.receiverList.app != null
- && r.allowBackgroundActivityStarts && !r.ordered) {
+ && r.mBackgroundStartPrivileges.allowsAny()
+ && !r.ordered) {
postActivityStartTokenRemoval(filter.receiverList.app, r);
}
}
@@ -863,7 +864,7 @@
Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
// Clean up ProcessRecord state related to this broadcast attempt
if (filter.receiverList.app != null) {
- filter.receiverList.app.removeAllowBackgroundActivityStartsToken(r);
+ filter.receiverList.app.removeBackgroundStartPrivileges(r);
if (ordered) {
filter.receiverList.app.mReceivers.removeCurReceiver(r);
// Something wrong, its oom adj could be downgraded, but not in a hurry.
@@ -1303,7 +1304,7 @@
scheduleBroadcastsLocked();
} else {
if (filter.receiverList != null) {
- maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+ maybeAddBackgroundStartPrivileges(filter.receiverList.app, r);
// r is guaranteed ordered at this point, so we know finishReceiverLocked()
// will get a callback and handle the activity start token lifecycle.
}
@@ -1393,7 +1394,7 @@
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
- maybeAddAllowBackgroundActivityStartsToken(app, r);
+ maybeAddBackgroundStartPrivileges(app, r);
r.mIsReceiverAppRunning = true;
processCurBroadcastLocked(r, app);
return;
@@ -1450,7 +1451,7 @@
return;
}
- maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
+ maybeAddBackgroundStartPrivileges(r.curApp, r);
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
@@ -1537,8 +1538,8 @@
mService.getUidStateLocked(targetUid));
}
- private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
- if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
+ private void maybeAddBackgroundStartPrivileges(ProcessRecord proc, BroadcastRecord r) {
+ if (r == null || proc == null || !r.mBackgroundStartPrivileges.allowsAny()) {
return;
}
String msgToken = (proc.toShortString() + r.toString()).intern();
@@ -1546,7 +1547,7 @@
// that request - we don't want the token to be swept from under our feet...
mHandler.removeCallbacksAndMessages(msgToken);
// ...then add the token
- proc.addOrUpdateAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
+ proc.addOrUpdateBackgroundStartPrivileges(r, r.mBackgroundStartPrivileges);
}
final void setBroadcastTimeoutLocked(long timeoutTime) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 8fc4a21..99e2ac7 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -281,7 +281,7 @@
final ProcessRecord app = (ProcessRecord) args.arg1;
final BroadcastRecord r = (BroadcastRecord) args.arg2;
args.recycle();
- app.removeAllowBackgroundActivityStartsToken(r);
+ app.removeBackgroundStartPrivileges(r);
}
return true;
}
@@ -1020,8 +1020,8 @@
MSG_DELIVERY_TIMEOUT_SOFT, softTimeoutMillis, 0, queue), softTimeoutMillis);
}
- if (r.allowBackgroundActivityStarts) {
- app.addOrUpdateAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
+ if (r.mBackgroundStartPrivileges.allowsAny()) {
+ app.addOrUpdateBackgroundStartPrivileges(r, r.mBackgroundStartPrivileges);
final long timeout = r.isForeground() ? mFgConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT
: mBgConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT;
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 9679fb8..4304377 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -32,6 +32,7 @@
import android.annotation.UptimeMillisLong;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
+import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.compat.CompatChanges;
import android.content.ComponentName;
@@ -42,7 +43,6 @@
import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.PrintWriterPrinter;
@@ -135,12 +135,8 @@
int deferredCount; // number of receivers in deferred state.
@Nullable BroadcastQueue queue; // the outbound queue handling this broadcast
- // 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;
+ // Determines the privileges the app's process has in regard to background starts.
+ final BackgroundStartPrivileges mBackgroundStartPrivileges;
// Filter the intent extras by using the rules of the package visibility before broadcasting
// the intent to the receiver.
@@ -371,8 +367,9 @@
BroadcastOptions _options, List _receivers,
ProcessRecord _resultToApp, IIntentReceiver _resultTo, int _resultCode,
String _resultData, Bundle _resultExtras, boolean _serialized, boolean _sticky,
- boolean _initialSticky, int _userId, boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken, boolean timeoutExempt,
+ boolean _initialSticky, int _userId,
+ @NonNull BackgroundStartPrivileges backgroundStartPrivileges,
+ boolean timeoutExempt,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
if (_intent == null) {
throw new NullPointerException("Can't construct with a null intent");
@@ -414,8 +411,7 @@
userId = _userId;
nextReceiver = 0;
state = IDLE;
- this.allowBackgroundActivityStarts = allowBackgroundActivityStarts;
- mBackgroundActivityStartsToken = backgroundActivityStartsToken;
+ mBackgroundStartPrivileges = backgroundStartPrivileges;
this.timeoutExempt = timeoutExempt;
alarm = options != null && options.isAlarmBroadcast();
pushMessage = options != null && options.isPushMessagingBroadcast();
@@ -479,8 +475,7 @@
manifestCount = from.manifestCount;
manifestSkipCount = from.manifestSkipCount;
queue = from.queue;
- allowBackgroundActivityStarts = from.allowBackgroundActivityStarts;
- mBackgroundActivityStartsToken = from.mBackgroundActivityStartsToken;
+ mBackgroundStartPrivileges = from.mBackgroundStartPrivileges;
timeoutExempt = from.timeoutExempt;
alarm = from.alarm;
pushMessage = from.pushMessage;
@@ -521,8 +516,8 @@
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, options,
splitReceivers, resultToApp, resultTo, resultCode, resultData, resultExtras,
- ordered, sticky, initialSticky, userId, allowBackgroundActivityStarts,
- mBackgroundActivityStartsToken, timeoutExempt, filterExtrasForReceiver);
+ ordered, sticky, initialSticky, userId,
+ mBackgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver);
split.enqueueTime = this.enqueueTime;
split.enqueueRealTime = this.enqueueRealTime;
split.enqueueClockTime = this.enqueueClockTime;
@@ -601,7 +596,7 @@
requiredPermissions, excludedPermissions, excludedPackages, appOp, options,
uid2receiverList.valueAt(i), null /* _resultToApp */, null /* _resultTo */,
resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId,
- allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt,
+ mBackgroundStartPrivileges, timeoutExempt,
filterExtrasForReceiver);
br.enqueueTime = this.enqueueTime;
br.enqueueRealTime = this.enqueueRealTime;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index fed0b11..874fda3 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.PendingIntent;
@@ -330,20 +331,42 @@
return activityOptions.isPendingIntentBackgroundActivityLaunchAllowedByPermission();
}
- public static boolean isPendingIntentBalAllowedByCaller(
- @Nullable ActivityOptions activityOptions) {
+ /**
+ * Return the {@link BackgroundStartPrivileges} the activity options grant the PendingIntent to
+ * use caller's BAL permission.
+ */
+ public static BackgroundStartPrivileges getBackgroundStartPrivilegesAllowedByCaller(
+ @Nullable ActivityOptions activityOptions, int callingUid) {
if (activityOptions == null) {
- return ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT;
+ // since the ActivityOptions were not created by the app itself, determine the default
+ // for the app
+ return getDefaultBackgroundStartPrivileges(callingUid);
}
- return isPendingIntentBalAllowedByCaller(activityOptions.toBundle());
+ return getBackgroundStartPrivilegesAllowedByCaller(activityOptions.toBundle(),
+ callingUid);
}
- private static boolean isPendingIntentBalAllowedByCaller(@Nullable Bundle options) {
- if (options == null) {
- return ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT;
+ private static BackgroundStartPrivileges getBackgroundStartPrivilegesAllowedByCaller(
+ @Nullable Bundle options, int callingUid) {
+ if (options == null || !options.containsKey(
+ ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED)) {
+ return getDefaultBackgroundStartPrivileges(callingUid);
}
- return options.getBoolean(ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
- ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT);
+ return options.getBoolean(ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED)
+ ? BackgroundStartPrivileges.ALLOW_BAL
+ : BackgroundStartPrivileges.NONE;
+ }
+
+ /**
+ * Default {@link BackgroundStartPrivileges} to be used if the intent sender has not made an
+ * explicit choice.
+ *
+ * @hide
+ */
+ public static BackgroundStartPrivileges getDefaultBackgroundStartPrivileges(int callingUid) {
+ // TODO: In the next step this will return ALLOW_FGS instead, if the app that sent the
+ // PendingIntent is targeting Android U
+ return BackgroundStartPrivileges.ALLOW_BAL;
}
@Deprecated
@@ -493,11 +516,6 @@
if (userId == UserHandle.USER_CURRENT) {
userId = controller.mUserController.getCurrentOrTargetUserId();
}
- // temporarily allow receivers and services to open activities from background if the
- // PendingIntent.send() caller was foreground at the time of sendInner() call
- final boolean allowTrampoline = uid != callingUid
- && controller.mAtmInternal.isUidForeground(callingUid)
- && isPendingIntentBalAllowedByCaller(options);
// note: we on purpose don't pass in the information about the PendingIntent's creator,
// like pid or ProcessRecord, to the ActivityTaskManagerInternal calls below, because
@@ -515,8 +533,7 @@
allIntents, allResolvedTypes, resultTo, mergedOptions, userId,
false /* validateIncomingUser */,
this /* originatingPendingIntent */,
- mAllowBgActivityStartsForActivitySender.contains(
- allowlistToken));
+ getBackgroundStartPrivilegesForActivitySender(allowlistToken));
} else {
res = controller.mAtmInternal.startActivityInPackage(uid, callingPid,
callingUid, key.packageName, key.featureId, finalIntent,
@@ -524,8 +541,7 @@
mergedOptions, userId, null, "PendingIntentRecord",
false /* validateIncomingUser */,
this /* originatingPendingIntent */,
- mAllowBgActivityStartsForActivitySender.contains(
- allowlistToken));
+ getBackgroundStartPrivilegesForActivitySender(allowlistToken));
}
} catch (RuntimeException e) {
Slog.w(TAG, "Unable to send startActivity intent", e);
@@ -537,17 +553,17 @@
break;
case ActivityManager.INTENT_SENDER_BROADCAST:
try {
- final boolean allowedByToken =
- mAllowBgActivityStartsForBroadcastSender.contains(allowlistToken);
- final IBinder bgStartsToken = (allowedByToken) ? allowlistToken : null;
-
+ final BackgroundStartPrivileges backgroundStartPrivileges =
+ getBackgroundStartPrivilegesForActivitySender(
+ mAllowBgActivityStartsForBroadcastSender, allowlistToken,
+ options, callingUid);
// 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, finishedReceiverThread, finishedReceiver, code, null,
null, requiredPermission, options, (finishedReceiver != null),
- false, userId, allowedByToken || allowTrampoline, bgStartsToken,
+ false, userId, backgroundStartPrivileges,
null /* broadcastAllowList */);
if (sent == ActivityManager.BROADCAST_SUCCESS) {
sendFinish = false;
@@ -559,14 +575,14 @@
case ActivityManager.INTENT_SENDER_SERVICE:
case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
try {
- final boolean allowedByToken =
- mAllowBgActivityStartsForServiceSender.contains(allowlistToken);
- final IBinder bgStartsToken = (allowedByToken) ? allowlistToken : null;
-
+ final BackgroundStartPrivileges backgroundStartPrivileges =
+ getBackgroundStartPrivilegesForActivitySender(
+ mAllowBgActivityStartsForServiceSender, allowlistToken,
+ options, callingUid);
controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
key.packageName, key.featureId, userId,
- allowedByToken || allowTrampoline, bgStartsToken);
+ backgroundStartPrivileges);
} catch (RuntimeException e) {
Slog.w(TAG, "Unable to send startService intent", e);
} catch (TransactionTooLargeException e) {
@@ -589,6 +605,27 @@
return res;
}
+ private BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
+ IBinder allowlistToken) {
+ return mAllowBgActivityStartsForActivitySender.contains(allowlistToken)
+ ? BackgroundStartPrivileges.allowBackgroundActivityStarts(allowlistToken)
+ : BackgroundStartPrivileges.NONE;
+ }
+
+ private BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
+ ArraySet<IBinder> allowedTokenSet, IBinder allowlistToken,
+ Bundle options, int callingUid) {
+ if (allowedTokenSet.contains(allowlistToken)) {
+ return BackgroundStartPrivileges.allowBackgroundActivityStarts(allowlistToken);
+ }
+ // temporarily allow receivers and services to open activities from background if the
+ // PendingIntent.send() caller was foreground at the time of sendInner() call
+ if (uid != callingUid && controller.mAtmInternal.isUidForeground(callingUid)) {
+ return getBackgroundStartPrivilegesAllowedByCaller(options, callingUid);
+ }
+ return BackgroundStartPrivileges.NONE;
+ }
+
@Override
protected void finalize() throws Throwable {
try {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index cf91429..e6cb596 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 android.app.ApplicationExitInfo;
import android.app.ApplicationExitInfo.Reason;
import android.app.ApplicationExitInfo.SubReason;
+import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
@@ -1290,16 +1291,16 @@
* {@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 addOrUpdateAllowBackgroundActivityStartsToken(Binder entity,
- @Nullable IBinder originatingToken) {
+ void addOrUpdateBackgroundStartPrivileges(Binder entity,
+ BackgroundStartPrivileges backgroundStartPrivileges) {
Objects.requireNonNull(entity);
- mWindowProcessController.addOrUpdateAllowBackgroundActivityStartsToken(entity,
- originatingToken);
+ mWindowProcessController.addOrUpdateBackgroundStartPrivileges(entity,
+ backgroundStartPrivileges);
}
- void removeAllowBackgroundActivityStartsToken(Binder entity) {
+ void removeBackgroundStartPrivileges(Binder entity) {
Objects.requireNonNull(entity);
- mWindowProcessController.removeAllowBackgroundActivityStartsToken(entity);
+ mWindowProcessController.removeBackgroundStartPrivileges(entity);
}
@Override
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index def51b0..f667e33 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -21,6 +21,7 @@
import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.os.Process.INVALID_UID;
+import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -28,6 +29,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.app.Notification;
import android.app.PendingIntent;
@@ -154,18 +156,20 @@
// any current binding to this service has BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag?
private boolean mIsAllowedBgActivityStartsByBinding;
- // is this service currently allowed to start activities from background by providing
- // allowBackgroundActivityStarts=true to startServiceLocked()?
- private boolean mIsAllowedBgActivityStartsByStart;
// used to clean up the state of mIsAllowedBgActivityStartsByStart after a timeout
private Runnable mCleanUpAllowBgActivityStartsByStartCallback;
private ProcessRecord mAppForAllowingBgActivityStartsByStart;
- // These are the originating tokens that currently allow bg activity starts by service start.
- // This is used to trace back the grant when starting activities. We only pass such token to the
- // ProcessRecord if it's the *only* cause for bg activity starts exemption, otherwise we pass
- // null.
+ // These are the privileges that currently allow bg activity starts by service start.
+ // Each time the contents of this list change #mBackgroundStartPrivilegesByStartMerged has to
+ // be updated to reflect the merged state. The merged state retains the attribution to the
+ // originating token only if it is the only cause for being privileged.
@GuardedBy("ams")
- private List<IBinder> mBgActivityStartsByStartOriginatingTokens = new ArrayList<>();
+ private ArrayList<BackgroundStartPrivileges> mBackgroundStartPrivilegesByStart =
+ new ArrayList<>();
+
+ // merged privileges for mBackgroundStartPrivilegesByStart (for performance)
+ private BackgroundStartPrivileges mBackgroundStartPrivilegesByStartMerged =
+ BackgroundStartPrivileges.NONE;
// allow while-in-use permissions in foreground service or not.
// while-in-use permissions in FGS started from background might be restricted.
@@ -584,9 +588,9 @@
pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByBinding=");
pw.println(mIsAllowedBgActivityStartsByBinding);
}
- if (mIsAllowedBgActivityStartsByStart) {
+ if (mBackgroundStartPrivilegesByStartMerged.allowsAny()) {
pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart=");
- pw.println(mIsAllowedBgActivityStartsByStart);
+ pw.println(mBackgroundStartPrivilegesByStartMerged);
}
pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
pw.println(mAllowWhileInUsePermissionInFgs);
@@ -822,27 +826,28 @@
if (mAppForAllowingBgActivityStartsByStart != null) {
if (mAppForAllowingBgActivityStartsByStart != proc) {
mAppForAllowingBgActivityStartsByStart
- .removeAllowBackgroundActivityStartsToken(this);
+ .removeBackgroundStartPrivileges(this);
ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
}
}
// Make sure the cleanup callback knows about the new process.
- mAppForAllowingBgActivityStartsByStart = mIsAllowedBgActivityStartsByStart
+ mAppForAllowingBgActivityStartsByStart =
+ mBackgroundStartPrivilegesByStartMerged.allowsAny()
? proc : null;
- if (mIsAllowedBgActivityStartsByStart
+ if (mBackgroundStartPrivilegesByStartMerged.allowsAny()
|| mIsAllowedBgActivityStartsByBinding) {
- proc.addOrUpdateAllowBackgroundActivityStartsToken(this,
- getExclusiveOriginatingToken());
+ proc.addOrUpdateBackgroundStartPrivileges(this,
+ getBackgroundStartPrivilegesWithExclusiveToken());
} else {
- proc.removeAllowBackgroundActivityStartsToken(this);
+ proc.removeBackgroundStartPrivileges(this);
}
}
if (app != null && app != proc) {
// If the old app is allowed to start bg activities because of a service start, leave it
// that way until the cleanup callback runs. Otherwise we can remove its bg activity
// start ability immediately (it can't be bound now).
- if (!mIsAllowedBgActivityStartsByStart) {
- app.removeAllowBackgroundActivityStartsToken(this);
+ if (mBackgroundStartPrivilegesByStartMerged.allowsNothing()) {
+ app.removeBackgroundStartPrivileges(this);
}
app.mServices.updateBoundClientUids();
app.mServices.updateHostingComonentTypeForBindingsLocked();
@@ -942,9 +947,11 @@
* timeout. Note that the ability for starting background activities persists for the process
* even if the service is subsequently stopped.
*/
- void allowBgActivityStartsOnServiceStart(@Nullable IBinder originatingToken) {
- mBgActivityStartsByStartOriginatingTokens.add(originatingToken);
- setAllowedBgActivityStartsByStart(true);
+ void allowBgActivityStartsOnServiceStart(BackgroundStartPrivileges backgroundStartPrivileges) {
+ checkArgument(backgroundStartPrivileges.allowsAny());
+ mBackgroundStartPrivilegesByStart.add(backgroundStartPrivileges);
+ setAllowedBgActivityStartsByStart(
+ backgroundStartPrivileges.merge(mBackgroundStartPrivilegesByStartMerged));
if (app != null) {
mAppForAllowingBgActivityStartsByStart = app;
}
@@ -953,26 +960,31 @@
if (mCleanUpAllowBgActivityStartsByStartCallback == null) {
mCleanUpAllowBgActivityStartsByStartCallback = () -> {
synchronized (ams) {
- mBgActivityStartsByStartOriginatingTokens.remove(0);
- if (!mBgActivityStartsByStartOriginatingTokens.isEmpty()) {
+ mBackgroundStartPrivilegesByStart.remove(0);
+ if (!mBackgroundStartPrivilegesByStart.isEmpty()) {
+ // recalculate the merged token
+ mBackgroundStartPrivilegesByStartMerged =
+ BackgroundStartPrivileges.merge(mBackgroundStartPrivilegesByStart);
+
// There are other callbacks in the queue, let's just update the originating
// token
- if (mIsAllowedBgActivityStartsByStart) {
+ if (mBackgroundStartPrivilegesByStartMerged.allowsAny()) {
// mAppForAllowingBgActivityStartsByStart can be null here for example
// if get 2 calls to allowBgActivityStartsOnServiceStart() without a
// process attached to this ServiceRecord, so we need to perform a null
// check here.
if (mAppForAllowingBgActivityStartsByStart != null) {
mAppForAllowingBgActivityStartsByStart
- .addOrUpdateAllowBackgroundActivityStartsToken(
- this, getExclusiveOriginatingToken());
+ .addOrUpdateBackgroundStartPrivileges(this,
+ getBackgroundStartPrivilegesWithExclusiveToken());
}
} else {
Slog.wtf(TAG,
"Service callback to revoke bg activity starts by service "
+ "start triggered but "
- + "mIsAllowedBgActivityStartsByStart = false. This "
- + "should never happen.");
+ + "mBackgroundStartPrivilegesByStartMerged = "
+ + mBackgroundStartPrivilegesByStartMerged
+ + ". This should never happen.");
}
} else {
// Last callback on the queue
@@ -980,12 +992,12 @@
// The process we allowed is still running the service. We remove
// the ability by start, but it may still be allowed via bound
// connections.
- setAllowedBgActivityStartsByStart(false);
+ setAllowedBgActivityStartsByStart(BackgroundStartPrivileges.NONE);
} else if (mAppForAllowingBgActivityStartsByStart != null) {
// The process we allowed is not running the service. It therefore can't
// be bound so we can unconditionally remove the ability.
mAppForAllowingBgActivityStartsByStart
- .removeAllowBackgroundActivityStartsToken(ServiceRecord.this);
+ .removeBackgroundStartPrivileges(ServiceRecord.this);
}
mAppForAllowingBgActivityStartsByStart = null;
}
@@ -999,8 +1011,8 @@
ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
}
- private void setAllowedBgActivityStartsByStart(boolean newValue) {
- mIsAllowedBgActivityStartsByStart = newValue;
+ private void setAllowedBgActivityStartsByStart(BackgroundStartPrivileges newValue) {
+ mBackgroundStartPrivilegesByStartMerged = newValue;
updateParentProcessBgActivityStartsToken();
}
@@ -1011,46 +1023,42 @@
* {@code mIsAllowedBgActivityStartsByBinding}. If either is true, this ServiceRecord
* should be contributing as a token in parent ProcessRecord.
*
- * @see com.android.server.am.ProcessRecord#addOrUpdateAllowBackgroundActivityStartsToken(
- * Binder, IBinder)
- * @see com.android.server.am.ProcessRecord#removeAllowBackgroundActivityStartsToken(Binder)
+ * @see com.android.server.am.ProcessRecord#addOrUpdateBackgroundStartPrivileges(Binder,
+ * BackgroundStartPrivileges)
+ * @see com.android.server.am.ProcessRecord#removeBackgroundStartPrivileges(Binder)
*/
private void updateParentProcessBgActivityStartsToken() {
if (app == null) {
return;
}
- if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) {
+ if (mBackgroundStartPrivilegesByStartMerged.allowsAny()
+ || mIsAllowedBgActivityStartsByBinding) {
// if the token is already there it's safe to "re-add it" - we're dealing with
// a set of Binder objects
- app.addOrUpdateAllowBackgroundActivityStartsToken(this, getExclusiveOriginatingToken());
+ app.addOrUpdateBackgroundStartPrivileges(this,
+ getBackgroundStartPrivilegesWithExclusiveToken());
} else {
- app.removeAllowBackgroundActivityStartsToken(this);
+ app.removeBackgroundStartPrivileges(this);
}
}
/**
- * Returns the originating token if that's the only reason background activity starts are
- * allowed. In order for that to happen the service has to be allowed only due to starts, since
- * bindings are not associated with originating tokens, and all the start tokens have to be the
- * same and there can't be any null originating token in the queue.
+ * Returns {@link BackgroundStartPrivileges} that represents the privileges a specific
+ * originating token or a generic aggregate token.
*
- * Originating tokens are optional, so the caller could provide null when it allows bg activity
- * starts.
+ * If all privileges are associated with the same token (i.e. the service is only allowed due
+ * to starts) the token will be retained, otherwise (e.g. the privileges were granted by
+ * bindings) the originating token will be empty.
*/
@Nullable
- private IBinder getExclusiveOriginatingToken() {
- if (mIsAllowedBgActivityStartsByBinding
- || mBgActivityStartsByStartOriginatingTokens.isEmpty()) {
- return null;
+ private BackgroundStartPrivileges getBackgroundStartPrivilegesWithExclusiveToken() {
+ if (mIsAllowedBgActivityStartsByBinding) {
+ return BackgroundStartPrivileges.ALLOW_BAL;
}
- IBinder firstToken = mBgActivityStartsByStartOriginatingTokens.get(0);
- for (int i = 1, n = mBgActivityStartsByStartOriginatingTokens.size(); i < n; i++) {
- IBinder token = mBgActivityStartsByStartOriginatingTokens.get(i);
- if (token != firstToken) {
- return null;
- }
+ if (mBackgroundStartPrivilegesByStart.isEmpty()) {
+ return BackgroundStartPrivileges.NONE;
}
- return firstToken;
+ return mBackgroundStartPrivilegesByStartMerged;
}
@GuardedBy("ams")
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 43d3111..a7883cb 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -33,6 +33,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -277,7 +278,7 @@
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart) {
+ BackgroundStartPrivileges backgroundStartPrivileges) {
userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
reason);
@@ -298,7 +299,7 @@
.setUserId(userId)
.setInTask(inTask)
.setOriginatingPendingIntent(originatingPendingIntent)
- .setAllowBackgroundActivityStart(allowBackgroundActivityStart)
+ .setBackgroundStartPrivileges(backgroundStartPrivileges)
.execute();
}
@@ -317,10 +318,11 @@
final int startActivitiesInPackage(int uid, String callingPackage,
@Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes,
IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser,
- PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
+ PendingIntentRecord originatingPendingIntent,
+ BackgroundStartPrivileges backgroundStartPrivileges) {
return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */,
callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId,
- validateIncomingUser, originatingPendingIntent, allowBackgroundActivityStart);
+ validateIncomingUser, originatingPendingIntent, backgroundStartPrivileges);
}
/**
@@ -340,7 +342,7 @@
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart) {
+ BackgroundStartPrivileges backgroundStartPrivileges) {
final String reason = "startActivityInPackage";
@@ -350,14 +352,14 @@
// TODO: Switch to user app stacks here.
return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage,
callingFeatureId, intents, resolvedTypes, resultTo, options, userId, reason,
- originatingPendingIntent, allowBackgroundActivityStart);
+ originatingPendingIntent, backgroundStartPrivileges);
}
int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid,
int incomingRealCallingUid, String callingPackage, @Nullable String callingFeatureId,
Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
int userId, String reason, PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart) {
+ BackgroundStartPrivileges backgroundStartPrivileges) {
if (intents == null) {
throw new NullPointerException("intents is null");
}
@@ -465,7 +467,7 @@
// top one as otherwise an activity below might consume it.
.setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
.setOriginatingPendingIntent(originatingPendingIntent)
- .setAllowBackgroundActivityStart(allowBackgroundActivityStart);
+ .setBackgroundStartPrivileges(backgroundStartPrivileges);
}
// Log if the activities to be started have different uids.
if (startingUidPkgs.size() > 1) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 40432dc..d88f719 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -95,6 +95,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.app.PendingIntent;
import android.app.ProfilerInfo;
@@ -389,7 +390,8 @@
WaitResult waitResult;
int filterCallingUid;
PendingIntentRecord originatingPendingIntent;
- boolean allowBackgroundActivityStart;
+ BackgroundStartPrivileges backgroundStartPrivileges;
+
/**
* The error callback token passed in {@link android.window.WindowContainerTransaction}
* for TaskFragment operation error handling via
@@ -449,7 +451,7 @@
allowPendingRemoteAnimationRegistryLookup = true;
filterCallingUid = UserHandle.USER_NULL;
originatingPendingIntent = null;
- allowBackgroundActivityStart = false;
+ backgroundStartPrivileges = BackgroundStartPrivileges.NONE;
errorCallbackToken = null;
}
@@ -492,7 +494,7 @@
= request.allowPendingRemoteAnimationRegistryLookup;
filterCallingUid = request.filterCallingUid;
originatingPendingIntent = request.originatingPendingIntent;
- allowBackgroundActivityStart = request.allowBackgroundActivityStart;
+ backgroundStartPrivileges = request.backgroundStartPrivileges;
errorCallbackToken = request.errorCallbackToken;
}
@@ -1060,7 +1062,7 @@
realCallingPid,
callerApp,
request.originatingPendingIntent,
- request.allowBackgroundActivityStart,
+ request.backgroundStartPrivileges,
intent,
checkedOptions);
} finally {
@@ -3182,8 +3184,9 @@
return this;
}
- ActivityStarter setAllowBackgroundActivityStart(boolean allowBackgroundActivityStart) {
- mRequest.allowBackgroundActivityStart = allowBackgroundActivityStart;
+ ActivityStarter setBackgroundStartPrivileges(
+ BackgroundStartPrivileges backgroundStartPrivileges) {
+ mRequest.backgroundStartPrivileges = backgroundStartPrivileges;
return this;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index c51808a..bd4f1a6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -21,6 +21,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppProtoEnums;
+import android.app.BackgroundStartPrivileges;
import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.ProfilerInfo;
@@ -209,14 +210,14 @@
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart);
+ BackgroundStartPrivileges backgroundStartPrivileges);
public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
String callingPackage, @Nullable String callingFeaturId, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart);
+ BackgroundStartPrivileges backgroundStartPrivileges);
/**
* Callback to be called on certain activity start scenarios.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9a8ef19..070ad2d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -136,6 +136,7 @@
import android.app.AnrController;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.BackgroundStartPrivileges;
import android.app.Dialog;
import android.app.IActivityClientController;
import android.app.IActivityController;
@@ -1222,7 +1223,7 @@
return getActivityStartController().startActivities(caller, -1, 0, -1, callingPackage,
callingFeatureId, intents, resolvedTypes, resultTo,
SafeActivityOptions.fromBundle(bOptions), userId, reason,
- null /* originatingPendingIntent */, false /* allowBackgroundActivityStart */);
+ null /* originatingPendingIntent */, BackgroundStartPrivileges.NONE);
}
@Override
@@ -1527,7 +1528,7 @@
// To start the dream from background, we need to start it from a persistent
// system process. Here we set the real calling uid to the system server uid
.setRealCallingUid(Binder.getCallingUid())
- .setAllowBackgroundActivityStart(true)
+ .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
.execute();
return true;
} finally {
@@ -1677,7 +1678,7 @@
.setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
// The target may well be in the background, which would normally prevent it
// from starting an activity. Here we definitely want the start to succeed.
- .setAllowBackgroundActivityStart(true)
+ .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
.execute();
} catch (SecurityException e) {
// XXX need to figure out how to propagate to original app.
@@ -1723,7 +1724,7 @@
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setUserId(userId)
- .setAllowBackgroundActivityStart(true)
+ .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
.execute();
}
@@ -1750,7 +1751,7 @@
.setResolvedType(resolvedType)
.setActivityOptions(bOptions)
.setUserId(userId)
- .setAllowBackgroundActivityStart(true)
+ .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
.execute();
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2200,7 +2201,7 @@
-1,
callerApp,
null,
- false,
+ BackgroundStartPrivileges.NONE,
null,
null)) {
if (!isBackgroundActivityStartsEnabled()) {
@@ -5605,7 +5606,7 @@
intents, resolvedTypes, null /* resultTo */,
SafeActivityOptions.fromBundle(bOptions), userId,
false /* validateIncomingUser */, null /* originatingPendingIntent */,
- false /* allowBackgroundActivityStart */);
+ BackgroundStartPrivileges.NONE);
}
@Override
@@ -5613,12 +5614,12 @@
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart) {
+ BackgroundStartPrivileges backgroundStartPrivileges) {
assertPackageMatchesCallingUid(callingPackage);
return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes,
resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
- allowBackgroundActivityStart);
+ backgroundStartPrivileges);
}
@Override
@@ -5627,13 +5628,13 @@
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart) {
+ BackgroundStartPrivileges backgroundStartPrivileges) {
assertPackageMatchesCallingUid(callingPackage);
return getActivityStartController().startActivityInPackage(uid, realCallingPid,
realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
reason, validateIncomingUser, originatingPendingIntent,
- allowBackgroundActivityStart);
+ backgroundStartPrivileges);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 919bab8..8149e1c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -90,6 +90,7 @@
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
+import android.app.BackgroundStartPrivileges;
import android.app.IActivityClientController;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
@@ -2731,7 +2732,7 @@
callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
null, 0, 0, options, userId, task, "startActivityFromRecents",
false /* validateIncomingUser */, null /* originatingPendingIntent */,
- false /* allowBackgroundActivityStart */);
+ BackgroundStartPrivileges.NONE);
} finally {
PackageManagerServiceUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(false);
synchronized (mService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 7bd8c53..de38a20 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -20,6 +20,7 @@
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import android.app.ActivityManager;
+import android.app.BackgroundStartPrivileges;
import android.app.IAppTask;
import android.app.IApplicationThread;
import android.content.Intent;
@@ -131,7 +132,7 @@
-1,
callerApp,
null,
- false,
+ BackgroundStartPrivileges.NONE,
null,
null)) {
if (!mService.isBackgroundActivityStartsEnabled()) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 2315795..7aa734b 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -31,6 +31,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.BackgroundStartPrivileges;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -135,12 +136,12 @@
int realCallingPid,
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart,
+ BackgroundStartPrivileges backgroundStartPrivileges,
Intent intent,
ActivityOptions checkedOptions) {
return checkBackgroundActivityStart(callingUid, callingPid, callingPackage,
realCallingUid, realCallingPid, callerApp, originatingPendingIntent,
- allowBackgroundActivityStart, intent, checkedOptions) == BAL_BLOCK;
+ backgroundStartPrivileges, intent, checkedOptions) == BAL_BLOCK;
}
/**
@@ -156,7 +157,7 @@
int realCallingPid,
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart,
+ BackgroundStartPrivileges backgroundStartPrivileges,
Intent intent,
ActivityOptions checkedOptions) {
// don't abort for the most important UIDs
@@ -254,10 +255,12 @@
}
// Legacy behavior allows to use caller foreground state to bypass BAL restriction.
- final boolean balAllowedByPiSender =
- PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);
-
- if (balAllowedByPiSender && realCallingUid != callingUid) {
+ // The options here are the options passed by the sender and not those on the intent.
+ final BackgroundStartPrivileges balAllowedByPiSender =
+ PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+ checkedOptions, realCallingUid);
+ if (balAllowedByPiSender.allowsBackgroundActivityStarts()
+ && realCallingUid != callingUid) {
final boolean useCallerPermission =
PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions);
if (useCallerPermission
@@ -282,7 +285,8 @@
}
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
- if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
+ if (isRealCallingUidPersistentSystemProcess
+ && backgroundStartPrivileges.allowsBackgroundActivityStarts()) {
return logStartAllowedAndReturnCode(/*background*/ false,
callingUid,
BAL_ALLOW_PENDING_INTENT,
@@ -338,7 +342,7 @@
// 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) {
+ if (callerApp == null && balAllowedByPiSender.allowsBackgroundActivityStarts()) {
callerApp = mService.getProcessController(realCallingPid, realCallingUid);
callerAppUid = realCallingUid;
}
@@ -399,8 +403,8 @@
+ isRealCallingUidPersistentSystemProcess
+ "; originatingPendingIntent: "
+ originatingPendingIntent
- + "; allowBackgroundActivityStart: "
- + allowBackgroundActivityStart
+ + "; backgroundStartPrivileges: "
+ + backgroundStartPrivileges
+ "; intent: "
+ intent
+ "; callerApp: "
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 020e9c58..ecc8aad 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static com.android.internal.util.Preconditions.checkArgument;
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;
@@ -27,8 +28,11 @@
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD;
import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.BackgroundStartPrivileges;
import android.os.Binder;
import android.os.IBinder;
import android.os.SystemClock;
@@ -40,7 +44,9 @@
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.function.IntPredicate;
/**
@@ -63,7 +69,7 @@
* (can be null) and are used to trace back the grant to the notification token mechanism.
*/
@GuardedBy("this")
- private @Nullable ArrayMap<Binder, IBinder> mBackgroundActivityStartTokens;
+ private @Nullable ArrayMap<Binder, BackgroundStartPrivileges> mBackgroundStartPrivileges;
/** Set of UIDs of clients currently bound to this process. */
@GuardedBy("this")
@@ -153,25 +159,49 @@
private boolean isBackgroundStartAllowedByToken(int uid, String packageName,
boolean isCheckingForFgsStart) {
synchronized (this) {
- if (mBackgroundActivityStartTokens == null
- || mBackgroundActivityStartTokens.isEmpty()) {
+ if (mBackgroundStartPrivileges == null
+ || mBackgroundStartPrivileges.isEmpty()) {
+ // no tokens to allow anything
return false;
}
if (isCheckingForFgsStart) {
- // BG-FGS-start only checks if there is a token.
- return true;
+ // check if any token allows foreground service starts
+ for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) {
+ if (mBackgroundStartPrivileges.valueAt(i).allowsBackgroundFgsStarts()) {
+ return true;
+ }
+ }
+ return false;
}
-
if (mBackgroundActivityStartCallback == null) {
- // We have tokens but no callback to decide => allow.
- return true;
+ // without a callback just check if any token allows background activity starts
+ for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) {
+ if (mBackgroundStartPrivileges.valueAt(i)
+ .allowsBackgroundActivityStarts()) {
+ return true;
+ }
+ }
+ return false;
}
+ List<IBinder> binderTokens = getOriginatingTokensThatAllowBal();
// The callback will decide.
return mBackgroundActivityStartCallback.isActivityStartAllowed(
- mBackgroundActivityStartTokens.values(), uid, packageName);
+ binderTokens, uid, packageName);
}
}
+ private List<IBinder> getOriginatingTokensThatAllowBal() {
+ List<IBinder> originatingTokens = new ArrayList<>();
+ for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) {
+ BackgroundStartPrivileges privilege =
+ mBackgroundStartPrivileges.valueAt(i);
+ if (privilege.allowsBackgroundActivityStarts()) {
+ originatingTokens.add(privilege.getOriginatingToken());
+ }
+ }
+ return originatingTokens;
+ }
+
private boolean isBoundByForegroundUid() {
synchronized (this) {
if (mBoundClientUids != null) {
@@ -204,29 +234,34 @@
/**
* 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.
+ * {@code originatingToken} in the {@link BackgroundStartPrivileges} if you have one such
+ * originating token, this is useful for tracing back the grant in the case of the notification
+ * token.
*
* If {@code entity} is already added, this method will update its {@code originatingToken}.
*/
- void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity,
- @Nullable IBinder originatingToken) {
+ void addOrUpdateAllowBackgroundStartPrivileges(
+ Binder entity, BackgroundStartPrivileges backgroundStartPrivileges) {
+ requireNonNull(entity, "entity");
+ requireNonNull(backgroundStartPrivileges, "backgroundStartPrivileges");
+ checkArgument(backgroundStartPrivileges.allowsAny());
synchronized (this) {
- if (mBackgroundActivityStartTokens == null) {
- mBackgroundActivityStartTokens = new ArrayMap<>();
+ if (mBackgroundStartPrivileges == null) {
+ mBackgroundStartPrivileges = new ArrayMap<>();
}
- mBackgroundActivityStartTokens.put(entity, originatingToken);
+ mBackgroundStartPrivileges.put(entity, backgroundStartPrivileges);
}
}
/**
* Removes token {@code entity} that allowed background activity starts added via {@link
- * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}.
+ * #addOrUpdateAllowBackgroundStartPrivileges(Binder, BackgroundStartPrivileges)}.
*/
- void removeAllowBackgroundActivityStartsToken(Binder entity) {
+ void removeAllowBackgroundStartPrivileges(Binder entity) {
+ requireNonNull(entity, "entity");
synchronized (this) {
- if (mBackgroundActivityStartTokens != null) {
- mBackgroundActivityStartTokens.remove(entity);
+ if (mBackgroundStartPrivileges != null) {
+ mBackgroundStartPrivileges.remove(entity);
}
}
}
@@ -240,27 +275,27 @@
return false;
}
synchronized (this) {
- if (mBackgroundActivityStartTokens == null
- || mBackgroundActivityStartTokens.isEmpty()) {
+ if (mBackgroundStartPrivileges == null
+ || mBackgroundStartPrivileges.isEmpty()) {
return false;
}
return mBackgroundActivityStartCallback.canCloseSystemDialogs(
- mBackgroundActivityStartTokens.values(), uid);
+ getOriginatingTokensThatAllowBal(), uid);
}
}
void dump(PrintWriter pw, String prefix) {
synchronized (this) {
- if (mBackgroundActivityStartTokens != null
- && !mBackgroundActivityStartTokens.isEmpty()) {
+ if (mBackgroundStartPrivileges != null
+ && !mBackgroundStartPrivileges.isEmpty()) {
pw.print(prefix);
pw.println("Background activity start tokens (token: originating token):");
- for (int i = mBackgroundActivityStartTokens.size() - 1; i >= 0; i--) {
+ for (int i = mBackgroundStartPrivileges.size() - 1; i >= 0; i--) {
pw.print(prefix);
pw.print(" - ");
- pw.print(mBackgroundActivityStartTokens.keyAt(i));
+ pw.print(mBackgroundStartPrivileges.keyAt(i));
pw.print(": ");
- pw.println(mBackgroundActivityStartTokens.valueAt(i));
+ pw.println(mBackgroundStartPrivileges.valueAt(i));
}
}
if (mBoundClientUids != null && mBoundClientUids.size() > 0) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 3e279b2..8f6ac15 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -50,6 +50,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.app.ProfilerInfo;
import android.app.servertransaction.ConfigurationChangeItem;
@@ -64,7 +65,6 @@
import android.os.Binder;
import android.os.Build;
import android.os.FactoryTest;
-import android.os.IBinder;
import android.os.LocaleList;
import android.os.Message;
import android.os.Process;
@@ -547,17 +547,18 @@
}
/**
- * @see BackgroundLaunchProcessController#addOrUpdateAllowBackgroundActivityStartsToken(Binder,
- * IBinder)
+ * @see BackgroundLaunchProcessController#addOrUpdateAllowBackgroundStartPrivileges(Binder,
+ * BackgroundStartPrivileges)
*/
- public void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity,
- @Nullable IBinder originatingToken) {
- mBgLaunchController.addOrUpdateAllowBackgroundActivityStartsToken(entity, originatingToken);
+ public void addOrUpdateBackgroundStartPrivileges(Binder entity,
+ BackgroundStartPrivileges backgroundStartPrivileges) {
+ mBgLaunchController.addOrUpdateAllowBackgroundStartPrivileges(entity,
+ backgroundStartPrivileges);
}
- /** @see BackgroundLaunchProcessController#removeAllowBackgroundActivityStartsToken(Binder) */
- public void removeAllowBackgroundActivityStartsToken(Binder entity) {
- mBgLaunchController.removeAllowBackgroundActivityStartsToken(entity);
+ /** @see BackgroundLaunchProcessController#removeAllowBackgroundStartPrivileges(Binder) */
+ public void removeBackgroundStartPrivileges(Binder entity) {
+ mBgLaunchController.removeAllowBackgroundStartPrivileges(entity);
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 92570aa..e0f9be4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -50,6 +50,7 @@
import android.annotation.NonNull;
import android.app.Activity;
import android.app.AppOpsManager;
+import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.appwidget.AppWidgetManager;
import android.content.IIntentReceiver;
@@ -215,7 +216,7 @@
return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, 42, false, null,
null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM,
- false, null, false, null);
+ BackgroundStartPrivileges.NONE, false, null);
}
private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 72d8556..5f4ff1a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -49,6 +49,7 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.ReceiverInfo;
@@ -635,8 +636,8 @@
return new BroadcastRecord(mQueue, intent, callerApp, callerApp.info.packageName, null,
callerApp.getPid(), callerApp.info.uid, false, null, null, null, null,
AppOpsManager.OP_NONE, options, receivers, callerApp, resultTo,
- Activity.RESULT_OK, null, resultExtras, ordered, false, false, userId, false, null,
- false, null);
+ Activity.RESULT_OK, null, resultExtras, ordered, false, false, userId,
+ BackgroundStartPrivileges.NONE, false, null);
}
private static Map<String, Object> asMap(Bundle bundle) {
@@ -1707,20 +1708,21 @@
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
- final Binder backgroundActivityStartsToken = new Binder();
+ final BackgroundStartPrivileges backgroundStartPrivileges =
+ BackgroundStartPrivileges.allowBackgroundActivityStarts(new Binder());
final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
final BroadcastRecord r = new BroadcastRecord(mQueue, intent, callerApp,
callerApp.info.packageName, null, callerApp.getPid(), callerApp.info.uid, false,
null, null, null, null, AppOpsManager.OP_NONE, BroadcastOptions.makeBasic(),
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), null, null,
- Activity.RESULT_OK, null, null, false, false, false, UserHandle.USER_SYSTEM, true,
- backgroundActivityStartsToken, false, null);
+ Activity.RESULT_OK, null, null, false, false, false, UserHandle.USER_SYSTEM,
+ backgroundStartPrivileges, false, null);
enqueueBroadcast(r);
waitForIdle();
- verify(receiverApp).addOrUpdateAllowBackgroundActivityStartsToken(eq(r),
- eq(backgroundActivityStartsToken));
- verify(receiverApp).removeAllowBackgroundActivityStartsToken(eq(r));
+ verify(receiverApp).addOrUpdateBackgroundStartPrivileges(eq(r),
+ eq(backgroundStartPrivileges));
+ verify(receiverApp).removeBackgroundStartPrivileges(eq(r));
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
index 3864c88..01e2768 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -36,6 +36,7 @@
import static org.mockito.Mockito.doReturn;
import android.app.ActivityManagerInternal;
+import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.content.Intent;
import android.content.IntentFilter;
@@ -695,8 +696,7 @@
false /* sticky */,
false /* initialSticky */,
userId,
- false /* allowBackgroundActivityStarts */,
- null /* activityStartsToken */,
+ BackgroundStartPrivileges.NONE,
false /* timeoutExempt */,
filterExtrasForReceiver);
}
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 8f110a88..ca28558 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -81,6 +81,7 @@
import static org.mockito.ArgumentMatchers.notNull;
import android.app.ActivityOptions;
+import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
@@ -886,7 +887,8 @@
doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
// caller is temp allowed
if (callerIsTempAllowed) {
- callerApp.addOrUpdateAllowBackgroundActivityStartsToken(new Binder(), null);
+ callerApp.addOrUpdateBackgroundStartPrivileges(new Binder(),
+ BackgroundStartPrivileges.ALLOW_BAL);
}
// caller is instrumenting with background activity starts privileges
callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges,