From 6e921d0d566fc87665c1d4d29acc4bd9e5506c34 Mon Sep 17 00:00:00 2001 From: Achim Thesmann Date: Mon, 19 Dec 2022 16:36:47 -0800 Subject: [PATCH] Restrict PI sender BAL privileges by default. This is a behavior change for an app that - sends a PendingIntent created by another app - targets Android U+ - does not explicitly set a value for `isPendingIntentBackgroundActivityLaunchAllowed` While before U the system would automatically consider the state of the sending app to allow the PendingIntent to start an activity from the background, now the system automatically reduces the privilege to start only foreground services instead. Test: atest ActivityStarterTests BackgroundActivityLaunchTest Bug: 244637991 Change-Id: I4860780f2ad4461ac88bf18f9774343b4ba27255 --- .../server/am/PendingIntentRecord.java | 39 +++++++++++++++++-- .../server/wm/ActivityStarterTests.java | 12 +++--- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 874fda32d7844..95061ab71d6c1 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -22,16 +22,21 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; import android.app.IApplicationThread; import android.app.PendingIntent; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.PowerWhitelistManager; @@ -40,6 +45,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.TransactionTooLargeException; import android.os.UserHandle; +import android.provider.DeviceConfig; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -56,6 +62,13 @@ import java.util.Objects; public final class PendingIntentRecord extends IIntentSender.Stub { private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentRecord" : TAG_AM; + /** If enabled BAL are prevented by default in applications targeting U and later. */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER = 244637991; + private static final String ENABLE_DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER = + "enable_default_rescind_bal_privileges_from_pending_intent_sender"; + public static final int FLAG_ACTIVITY_SENDER = 1 << 0; public static final int FLAG_BROADCAST_SENDER = 1 << 1; public static final int FLAG_SERVICE_SENDER = 1 << 2; @@ -357,16 +370,34 @@ public final class PendingIntentRecord extends IIntentSender.Stub { : BackgroundStartPrivileges.NONE; } + private static boolean isDefaultRescindBalPrivilegesFromPendingIntentSenderEnabled() { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_WINDOW_MANAGER, + ENABLE_DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER, + true); // assume true if the property is unknown + } + /** * 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; + @RequiresPermission( + allOf = { + android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG, + android.Manifest.permission.LOG_COMPAT_CHANGE + }) + public static BackgroundStartPrivileges getDefaultBackgroundStartPrivileges( + int callingUid) { + boolean isFlagEnabled = isDefaultRescindBalPrivilegesFromPendingIntentSenderEnabled(); + boolean isChangeEnabledForApp = CompatChanges.isChangeEnabled( + DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER, callingUid); + if (isFlagEnabled && isChangeEnabledForApp) { + return BackgroundStartPrivileges.ALLOW_FGS; + } else { + return BackgroundStartPrivileges.ALLOW_BAL; + } } @Deprecated 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 ca285580b33a2..51f81f18cd504 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -71,6 +71,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -749,16 +750,15 @@ public class ActivityStarterTests extends WindowTestsBase { } /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the real calling process (pending intent) has a visible window. + * The sending app has a visible window, but does not (by default) allow the pending intent to + * start the background activity. */ @Test - public void - testBackgroundActivityStartsDisallowed_realCallingUidHasVisibleWindowNotAborted() { + public void testBackgroundActivityStartsDisallowed_realCallingUidHasVisibleWindowAborted() { doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); + runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_realCallingUidHasVisibleWindow_notAborted", false, + "disallowed_realCallingUidHasVisibleWindow_abortedInU", true, UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, UNIMPORTANT_UID2, true, PROCESS_STATE_BOUND_TOP, false, false, false, false, false, false);