DivestOS/Patches/LineageOS-17.1/android_frameworks_base/355355.patch

431 lines
20 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Christophe Pinelli <cpinelli@google.com>
Date: Tue, 27 Dec 2022 20:29:33 +0000
Subject: [PATCH] Backport BAL restrictions from S to R, this blocks apps from
using Alarm Manager to bypass BAL restrictions.
Test: atest BackgroundActivityLaunchTest
Bug: 195756028
Change-Id: Ifa3f79bc74c10d0ac8322079f2e6e3e0ba476b0f
(cherry picked from commit 1d737c2fbdc71570bbcaca0f44da4ee132fa545f)
Merged-In: Ifa3f79bc74c10d0ac8322079f2e6e3e0ba476b0f
---
core/java/android/app/ActivityOptions.java | 10 +--
core/java/android/app/BroadcastOptions.java | 25 +++++-
core/java/android/app/ComponentOptions.java | 84 +++++++++++++++++++
.../android/server/AlarmManagerService.java | 21 ++++-
.../server/am/PendingIntentRecord.java | 23 ++++-
.../android/server/wm/ActivityStarter.java | 22 +++--
.../server/wm/ActivityTaskManagerService.java | 2 +-
.../com/android/server/wm/AppTaskImpl.java | 2 +-
8 files changed, 169 insertions(+), 20 deletions(-)
create mode 100644 core/java/android/app/ComponentOptions.java
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 926044bffdd0..36ab62aedc09 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -59,7 +59,7 @@ import java.util.ArrayList;
* {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
* Context.startActivity(Intent, Bundle)} and related methods.
*/
-public class ActivityOptions {
+public class ActivityOptions extends ComponentOptions {
private static final String TAG = "ActivityOptions";
/**
@@ -881,13 +881,12 @@ public class ActivityOptions {
}
private ActivityOptions() {
+ super();
}
/** @hide */
public ActivityOptions(Bundle opts) {
- // If the remote side sent us bad parcelables, they won't get the
- // results they want, which is their loss.
- opts.setDefusable(true);
+ super(opts);
mPackageName = opts.getString(KEY_PACKAGE_NAME);
try {
@@ -1439,8 +1438,9 @@ public class ActivityOptions {
* object; you must not modify it, but can supply it to the startActivity
* methods that take an options Bundle.
*/
+ @Override
public Bundle toBundle() {
- Bundle b = new Bundle();
+ Bundle b = super.toBundle();
if (mPackageName != null) {
b.putString(KEY_PACKAGE_NAME, mPackageName);
}
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 161e2ad06ec0..8947fb44b07b 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -28,7 +28,7 @@ import android.os.Bundle;
* {@hide}
*/
@SystemApi
-public class BroadcastOptions {
+public class BroadcastOptions extends ComponentOptions {
private long mTemporaryAppWhitelistDuration;
private int mMinManifestReceiverApiLevel = 0;
private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
@@ -72,10 +72,12 @@ public class BroadcastOptions {
}
private BroadcastOptions() {
+ super();
}
/** @hide */
public BroadcastOptions(Bundle opts) {
+ super(opts);
mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION);
mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0);
mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
@@ -173,6 +175,24 @@ public class BroadcastOptions {
return mAllowBackgroundActivityStarts;
}
+ /**
+ * Set PendingIntent activity is allowed to be started in the background if the caller
+ * can start background activities.
+ * @hide
+ */
+ 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
+ */
+ public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
+ return super.isPendingIntentBackgroundActivityLaunchAllowed();
+ }
+
/**
* Returns the created options as a Bundle, which can be passed to
* {@link android.content.Context#sendBroadcast(android.content.Intent)
@@ -181,8 +201,9 @@ public class BroadcastOptions {
* object; you must not modify it, but can supply it to the sendBroadcast
* methods that take an options Bundle.
*/
+ @Override
public Bundle toBundle() {
- Bundle b = new Bundle();
+ Bundle b = super.toBundle();
if (mTemporaryAppWhitelistDuration > 0) {
b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration);
}
diff --git a/core/java/android/app/ComponentOptions.java b/core/java/android/app/ComponentOptions.java
new file mode 100644
index 000000000000..34ee9138a364
--- /dev/null
+++ b/core/java/android/app/ComponentOptions.java
@@ -0,0 +1,84 @@
+/*
+ * 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.os.Bundle;
+
+/**
+ * @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.
+ * @hide
+ */
+ public static final String KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED =
+ "android.pendingIntent.backgroundActivityAllowed";
+
+ private boolean mPendingIntentBalAllowed = PENDING_INTENT_BAL_ALLOWED_DEFAULT;
+
+ ComponentOptions() {
+ }
+
+ ComponentOptions(Bundle opts) {
+ // 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));
+ }
+
+ /**
+ * Set PendingIntent activity is allowed to be started in the background if the caller
+ * can start background activities.
+ *
+ * @hide
+ */
+ 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.
+ *
+ * @hide
+ */
+ public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
+ return mPendingIntentBalAllowed;
+ }
+
+ /**
+ * @hide
+ */
+ public Bundle toBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
+ mPendingIntentBalAllowed);
+ return bundle;
+ }
+}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index a65603cb4020..d82b435b4612 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -26,6 +26,7 @@ import static android.app.AlarmManager.RTC_WAKEUP;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
@@ -272,6 +273,8 @@ class AlarmManagerService extends SystemService {
* Broadcast options to use for FLAG_ALLOW_WHILE_IDLE.
*/
Bundle mIdleOptions;
+ ActivityOptions mActivityOptsRestrictBal = ActivityOptions.makeBasic();
+ BroadcastOptions mBroadcastOptsRestrictBal = BroadcastOptions.makeBasic();
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
new SparseArray<>();
@@ -497,6 +500,7 @@ class AlarmManagerService extends SystemService {
mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
BroadcastOptions opts = BroadcastOptions.makeBasic();
opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+ opts.setPendingIntentBackgroundActivityLaunchAllowed(false);
mIdleOptions = opts.toBundle();
}
}
@@ -1495,6 +1499,8 @@ class AlarmManagerService extends SystemService {
@Override
public void onStart() {
mInjector.init();
+ mActivityOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ mBroadcastOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
synchronized (mLock) {
mHandler = new AlarmHandler();
@@ -4143,6 +4149,13 @@ class AlarmManagerService extends SystemService {
return alarm.creatorUid;
}
+ private Bundle getAlarmOperationBundle(Alarm alarm) {
+ if (alarm.operation.isActivity()) {
+ return mActivityOptsRestrictBal.toBundle();
+ }
+ return mBroadcastOptsRestrictBal.toBundle();
+ }
+
@VisibleForTesting
class AlarmHandler extends Handler {
public static final int ALARM_EVENT = 1;
@@ -4181,7 +4194,11 @@ class AlarmManagerService extends SystemService {
for (int i=0; i<triggerList.size(); i++) {
Alarm alarm = triggerList.get(i);
try {
- alarm.operation.send();
+ // Disallow AlarmManager to start random background activity.
+ final Bundle bundle = getAlarmOperationBundle(alarm);
+ alarm.operation.send(/* context */ null, /* code */0, /* intent */
+ null, /* onFinished */null, /* handler */
+ null, /* requiredPermission */ null, bundle);
} catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
@@ -4696,7 +4713,7 @@ class AlarmManagerService extends SystemService {
mBackgroundIntent.putExtra(
Intent.EXTRA_ALARM_COUNT, alarm.count),
mDeliveryTracker, mHandler, null,
- allowWhileIdle ? mIdleOptions : null);
+ allowWhileIdle ? mIdleOptions : getAlarmOperationBundle(alarm));
} catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 54504c3c1e24..20d87e6882ac 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -20,6 +20,7 @@ import static android.app.ActivityManager.START_SUCCESS;
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.app.ActivityManager;
import android.app.ActivityOptions;
import android.content.IIntentSender;
@@ -277,6 +278,25 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
requiredPermission, null, null, 0, 0, 0, options);
}
+ /**
+ * Return true if the activity options allows PendingIntent to use caller's BAL permission.
+ */
+ public static boolean isPendingIntentBalAllowedByCaller(
+ @Nullable ActivityOptions activityOptions) {
+ if (activityOptions == null) {
+ return ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT;
+ }
+ return isPendingIntentBalAllowedByCaller(activityOptions.toBundle());
+ }
+
+ private static boolean isPendingIntentBalAllowedByCaller(@Nullable Bundle options) {
+ if (options == null) {
+ return ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT;
+ }
+ return options.getBoolean(ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
+ ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT);
+ }
+
public int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
@@ -389,7 +409,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
// 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);
+ && 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
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f37698de34d5..44fef5427cc3 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -767,6 +767,10 @@ class ActivityStarter {
abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
callingPackage);
+ // Merge the two options bundles, while realCallerOptions takes precedence.
+ ActivityOptions checkedOptions = options != null
+ ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
+
boolean restrictedBgActivity = false;
if (!abort) {
try {
@@ -774,15 +778,12 @@ class ActivityStarter {
"shouldAbortBackgroundActivityStart");
restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
- originatingPendingIntent, allowBackgroundActivityStart, intent);
+ originatingPendingIntent, allowBackgroundActivityStart, intent, checkedOptions);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
- // Merge the two options bundles, while realCallerOptions takes precedence.
- ActivityOptions checkedOptions = options != null
- ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
if (allowPendingRemoteAnimationRegistryLookup) {
checkedOptions = mService.getActivityStartController()
.getPendingRemoteAnimationRegistry()
@@ -941,7 +942,7 @@ class ActivityStarter {
boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
final String callingPackage, int realCallingUid, int realCallingPid,
WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart, Intent intent) {
+ boolean allowBackgroundActivityStart, Intent intent, ActivityOptions checkedOptions) {
// don't abort for the most important UIDs
final int callingAppId = UserHandle.getAppId(callingUid);
if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
@@ -976,7 +977,11 @@ class ActivityStarter {
? isCallingUidPersistentSystemProcess
: (realCallingAppId == Process.SYSTEM_UID)
|| realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
- if (realCallingUid != callingUid) {
+ // Legacy behavior allows to use caller foreground state to bypass BAL restriction.
+ final boolean balAllowedByPiSender =
+ PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);
+
+ if (balAllowedByPiSender && realCallingUid != callingUid) {
// don't abort if the realCallingUid has a visible window
if (realCallingUidHasAnyVisibleWindow) {
return false;
@@ -1013,9 +1018,10 @@ class ActivityStarter {
// If we don't have callerApp at this point, no caller was provided to startActivity().
// That's the case for PendingIntent-based starts, since the creator's process might not be
// up and alive. If that's the case, we retrieve the WindowProcessController for the send()
- // caller, so that we can make the decision based on its foreground/whitelisted state.
+ // caller if caller allows, so that we can make the decision
+ // based on its foreground/whitelisted state.
int callerAppUid = callingUid;
- if (callerApp == null) {
+ if (callerApp == null && balAllowedByPiSender) {
callerApp = mService.getProcessController(realCallingPid, realCallingUid);
callerAppUid = realCallingUid;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5e2626b9a7dd..d0dad0a23729 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2398,7 +2398,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final ActivityStarter starter = getActivityStartController().obtainStarter(
null /* intent */, "moveTaskToFront");
if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1,
- -1, callerApp, null, false, null)) {
+ -1, callerApp, null, false, null, null)) {
if (!isBackgroundActivityStartsEnabled()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 1eb7455135c7..f221c3a4573f 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -117,7 +117,7 @@ class AppTaskImpl extends IAppTask.Stub {
final ActivityStarter starter = mService.getActivityStartController().obtainStarter(
null /* intent */, "moveToFront");
if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid,
- callingPackage, -1, -1, callerApp, null, false, null)) {
+ callingPackage, -1, -1, callerApp, null, false, null, null)) {
if (!mService.isBackgroundActivityStartsEnabled()) {
return;
}