[DO NOT MERGE] 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
This commit is contained in:
Christophe Pinelli
2022-12-27 20:29:33 +00:00
parent 28dae0e4c9
commit 1d737c2fbd
8 changed files with 168 additions and 20 deletions

View File

@@ -61,7 +61,7 @@ import java.util.ArrayList;
* {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
* Context.startActivity(Intent, Bundle)} and related methods. * Context.startActivity(Intent, Bundle)} and related methods.
*/ */
public class ActivityOptions { public class ActivityOptions extends ComponentOptions {
private static final String TAG = "ActivityOptions"; private static final String TAG = "ActivityOptions";
/** /**
@@ -963,13 +963,12 @@ public class ActivityOptions {
} }
private ActivityOptions() { private ActivityOptions() {
super();
} }
/** @hide */ /** @hide */
public ActivityOptions(Bundle opts) { public ActivityOptions(Bundle opts) {
// If the remote side sent us bad parcelables, they won't get the super(opts);
// results they want, which is their loss.
opts.setDefusable(true);
mPackageName = opts.getString(KEY_PACKAGE_NAME); mPackageName = opts.getString(KEY_PACKAGE_NAME);
try { try {
@@ -1575,8 +1574,9 @@ public class ActivityOptions {
* object; you must not modify it, but can supply it to the startActivity * object; you must not modify it, but can supply it to the startActivity
* methods that take an options Bundle. * methods that take an options Bundle.
*/ */
@Override
public Bundle toBundle() { public Bundle toBundle() {
Bundle b = new Bundle(); Bundle b = super.toBundle();
if (mPackageName != null) { if (mPackageName != null) {
b.putString(KEY_PACKAGE_NAME, mPackageName); b.putString(KEY_PACKAGE_NAME, mPackageName);
} }

View File

@@ -28,7 +28,7 @@ import android.os.Bundle;
* {@hide} * {@hide}
*/ */
@SystemApi @SystemApi
public class BroadcastOptions { public class BroadcastOptions extends ComponentOptions {
private long mTemporaryAppWhitelistDuration; private long mTemporaryAppWhitelistDuration;
private int mMinManifestReceiverApiLevel = 0; private int mMinManifestReceiverApiLevel = 0;
private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT; private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
@@ -72,10 +72,12 @@ public class BroadcastOptions {
} }
private BroadcastOptions() { private BroadcastOptions() {
super();
} }
/** @hide */ /** @hide */
public BroadcastOptions(Bundle opts) { public BroadcastOptions(Bundle opts) {
super(opts);
mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION); mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION);
mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0); mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0);
mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
@@ -173,6 +175,24 @@ public class BroadcastOptions {
return mAllowBackgroundActivityStarts; 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 * Returns the created options as a Bundle, which can be passed to
* {@link android.content.Context#sendBroadcast(android.content.Intent) * {@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 * object; you must not modify it, but can supply it to the sendBroadcast
* methods that take an options Bundle. * methods that take an options Bundle.
*/ */
@Override
public Bundle toBundle() { public Bundle toBundle() {
Bundle b = new Bundle(); Bundle b = super.toBundle();
if (mTemporaryAppWhitelistDuration > 0) { if (mTemporaryAppWhitelistDuration > 0) {
b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration); b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration);
} }

View File

@@ -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;
}
}

View File

@@ -28,6 +28,7 @@ import static android.os.UserHandle.USER_SYSTEM;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.app.BroadcastOptions; import android.app.BroadcastOptions;
@@ -271,6 +272,8 @@ class AlarmManagerService extends SystemService {
* Broadcast options to use for FLAG_ALLOW_WHILE_IDLE. * Broadcast options to use for FLAG_ALLOW_WHILE_IDLE.
*/ */
Bundle mIdleOptions; Bundle mIdleOptions;
ActivityOptions mActivityOptsRestrictBal = ActivityOptions.makeBasic();
BroadcastOptions mBroadcastOptsRestrictBal = BroadcastOptions.makeBasic();
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser = private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
new SparseArray<>(); new SparseArray<>();
@@ -487,6 +490,7 @@ class AlarmManagerService extends SystemService {
mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION; mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
BroadcastOptions opts = BroadcastOptions.makeBasic(); BroadcastOptions opts = BroadcastOptions.makeBasic();
opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION); opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
opts.setPendingIntentBackgroundActivityLaunchAllowed(false);
mIdleOptions = opts.toBundle(); mIdleOptions = opts.toBundle();
} }
} }
@@ -1481,6 +1485,8 @@ class AlarmManagerService extends SystemService {
@Override @Override
public void onStart() { public void onStart() {
mInjector.init(); mInjector.init();
mActivityOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
mBroadcastOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
mListenerDeathRecipient = new IBinder.DeathRecipient() { mListenerDeathRecipient = new IBinder.DeathRecipient() {
@Override @Override
@@ -4156,6 +4162,13 @@ class AlarmManagerService extends SystemService {
return alarm.creatorUid; return alarm.creatorUid;
} }
private Bundle getAlarmOperationBundle(Alarm alarm) {
if (alarm.operation.isActivity()) {
return mActivityOptsRestrictBal.toBundle();
}
return mBroadcastOptsRestrictBal.toBundle();
}
@VisibleForTesting @VisibleForTesting
class AlarmHandler extends Handler { class AlarmHandler extends Handler {
public static final int ALARM_EVENT = 1; public static final int ALARM_EVENT = 1;
@@ -4194,7 +4207,11 @@ class AlarmManagerService extends SystemService {
for (int i=0; i<triggerList.size(); i++) { for (int i=0; i<triggerList.size(); i++) {
Alarm alarm = triggerList.get(i); Alarm alarm = triggerList.get(i);
try { 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) { } catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) { if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this // This IntentSender is no longer valid, but this
@@ -4730,7 +4747,7 @@ class AlarmManagerService extends SystemService {
mBackgroundIntent.putExtra( mBackgroundIntent.putExtra(
Intent.EXTRA_ALARM_COUNT, alarm.count), Intent.EXTRA_ALARM_COUNT, alarm.count),
mDeliveryTracker, mHandler, null, mDeliveryTracker, mHandler, null,
allowWhileIdle ? mIdleOptions : null); allowWhileIdle ? mIdleOptions : getAlarmOperationBundle(alarm));
} catch (PendingIntent.CanceledException e) { } catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) { if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this // This IntentSender is no longer valid, but this

View File

@@ -286,6 +286,25 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
requiredPermission, null, null, 0, 0, 0, options); 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, public int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) { String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
@@ -403,7 +422,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
// temporarily allow receivers and services to open activities from background if the // temporarily allow receivers and services to open activities from background if the
// PendingIntent.send() caller was foreground at the time of sendInner() call // PendingIntent.send() caller was foreground at the time of sendInner() call
final boolean allowTrampoline = uid != callingUid 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, // 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 // like pid or ProcessRecord, to the ActivityTaskManagerInternal calls below, because

View File

@@ -1004,6 +1004,10 @@ class ActivityStarter {
abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid, abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
callingPackage); callingPackage);
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
boolean restrictedBgActivity = false; boolean restrictedBgActivity = false;
if (!abort) { if (!abort) {
try { try {
@@ -1012,15 +1016,12 @@ class ActivityStarter {
restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid, restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
request.originatingPendingIntent, request.allowBackgroundActivityStart, request.originatingPendingIntent, request.allowBackgroundActivityStart,
intent); intent, checkedOptions);
} finally { } finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
} }
} }
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
if (request.allowPendingRemoteAnimationRegistryLookup) { if (request.allowPendingRemoteAnimationRegistryLookup) {
checkedOptions = mService.getActivityStartController() checkedOptions = mService.getActivityStartController()
.getPendingRemoteAnimationRegistry() .getPendingRemoteAnimationRegistry()
@@ -1234,7 +1235,7 @@ class ActivityStarter {
boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
final String callingPackage, int realCallingUid, int realCallingPid, final String callingPackage, int realCallingUid, int realCallingPid,
WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
boolean allowBackgroundActivityStart, Intent intent) { boolean allowBackgroundActivityStart, Intent intent, ActivityOptions checkedOptions) {
// don't abort for the most important UIDs // don't abort for the most important UIDs
final int callingAppId = UserHandle.getAppId(callingUid); final int callingAppId = UserHandle.getAppId(callingUid);
if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
@@ -1278,7 +1279,11 @@ class ActivityStarter {
? isCallingUidPersistentSystemProcess ? isCallingUidPersistentSystemProcess
: (realCallingAppId == Process.SYSTEM_UID) : (realCallingAppId == Process.SYSTEM_UID)
|| realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; || 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 // don't abort if the realCallingUid has a visible window
if (realCallingUidHasAnyVisibleWindow) { if (realCallingUidHasAnyVisibleWindow) {
if (DEBUG_ACTIVITY_STARTS) { if (DEBUG_ACTIVITY_STARTS) {
@@ -1346,9 +1351,10 @@ class ActivityStarter {
// If we don't have callerApp at this point, no caller was provided to startActivity(). // 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 // 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() // 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; int callerAppUid = callingUid;
if (callerApp == null) { if (callerApp == null && balAllowedByPiSender) {
callerApp = mService.getProcessController(realCallingPid, realCallingUid); callerApp = mService.getProcessController(realCallingPid, realCallingUid);
callerAppUid = realCallingUid; callerAppUid = realCallingUid;
} }

View File

@@ -2527,7 +2527,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final ActivityStarter starter = getActivityStartController().obtainStarter( final ActivityStarter starter = getActivityStartController().obtainStarter(
null /* intent */, "moveTaskToFront"); null /* intent */, "moveTaskToFront");
if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1, if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1,
-1, callerApp, null, false, null)) { -1, callerApp, null, false, null, null)) {
if (!isBackgroundActivityStartsEnabled()) { if (!isBackgroundActivityStartsEnabled()) {
return; return;
} }

View File

@@ -112,7 +112,7 @@ class AppTaskImpl extends IAppTask.Stub {
final ActivityStarter starter = mService.getActivityStartController().obtainStarter( final ActivityStarter starter = mService.getActivityStartController().obtainStarter(
null /* intent */, "moveToFront"); null /* intent */, "moveToFront");
if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid,
callingPackage, -1, -1, callerApp, null, false, null)) { callingPackage, -1, -1, callerApp, null, false, null, null)) {
if (!mService.isBackgroundActivityStartsEnabled()) { if (!mService.isBackgroundActivityStartsEnabled()) {
return; return;
} }