summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java25
-rw-r--r--core/java/android/app/ActivityOptions.java10
-rw-r--r--core/java/android/app/BroadcastOptions.java25
-rw-r--r--core/java/android/app/ComponentOptions.java83
-rw-r--r--core/java/android/content/Intent.java2
-rw-r--r--core/java/android/content/IntentSender.java41
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java15
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java34
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java22
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java1
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java14
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java22
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java24
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/AppTaskImpl.java4
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java8
-rw-r--r--services/core/java/com/android/server/wm/RunningTasks.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java48
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java59
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java47
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java28
24 files changed, 489 insertions, 55 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index d1e3481b1107..e8cfb8d8623e 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -56,6 +56,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
@@ -317,6 +318,8 @@ public class AlarmManagerService extends SystemService {
private final SparseBooleanArray mPendingSendNextAlarmClockChangedForUser =
new SparseBooleanArray();
private boolean mNextAlarmClockMayChange;
+ ActivityOptions mActivityOptsRestrictBal = ActivityOptions.makeBasic();
+ BroadcastOptions mBroadcastOptsRestrictBal = BroadcastOptions.makeBasic();
@GuardedBy("mLock")
private final Runnable mAlarmClockUpdater = () -> mNextAlarmClockMayChange = true;
@@ -1611,6 +1614,11 @@ public class AlarmManagerService extends SystemService {
@Override
public void onStart() {
mInjector.init();
+ mOptsWithFgs.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ mOptsWithoutFgs.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ mOptsTimeBroadcast.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ mActivityOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ mBroadcastOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
mMetricsHelper = new MetricsHelper(getContext(), mLock);
mListenerDeathRecipient = new IBinder.DeathRecipient() {
@@ -4306,6 +4314,14 @@ public class AlarmManagerService extends SystemService {
return alarm.creatorUid;
}
+ private Bundle getAlarmOperationBundle(Alarm alarm) {
+ if (alarm.mIdleOptions != null) {
+ return alarm.mIdleOptions;
+ } else if (alarm.operation.isActivity()) {
+ return mActivityOptsRestrictBal.toBundle();
+ }
+ return mBroadcastOptsRestrictBal.toBundle();
+ }
@VisibleForTesting
class AlarmHandler extends Handler {
@@ -4344,7 +4360,11 @@ public 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
@@ -4906,9 +4926,10 @@ public class AlarmManagerService extends SystemService {
mSendCount++;
try {
+ final Bundle bundle = getAlarmOperationBundle(alarm);
alarm.operation.send(getContext(), 0,
mBackgroundIntent.putExtra(Intent.EXTRA_ALARM_COUNT, alarm.count),
- mDeliveryTracker, mHandler, null, alarm.mIdleOptions);
+ mDeliveryTracker, mHandler, null, bundle);
} catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 8e1f263ebf03..4076fa7db3fb 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -68,7 +68,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";
/**
@@ -1071,13 +1071,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 {
@@ -1804,8 +1803,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 bd7162c1bf3b..38c94ec20670 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -34,7 +34,7 @@ import android.os.PowerExemptionManager.TempAllowListType;
* {@hide}
*/
@SystemApi
-public class BroadcastOptions {
+public class BroadcastOptions extends ComponentOptions {
private long mTemporaryAppAllowlistDuration;
private @TempAllowListType int mTemporaryAppAllowlistType;
private @ReasonCode int mTemporaryAppAllowlistReasonCode;
@@ -108,12 +108,14 @@ public class BroadcastOptions {
}
private BroadcastOptions() {
+ super();
resetTemporaryAppAllowlist();
}
/** @hide */
@TestApi
public BroadcastOptions(@NonNull Bundle opts) {
+ super(opts);
// Match the logic in toBundle().
if (opts.containsKey(KEY_TEMPORARY_APP_ALLOWLIST_DURATION)) {
mTemporaryAppAllowlistDuration = opts.getLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION);
@@ -191,6 +193,24 @@ public class BroadcastOptions {
}
/**
+ * 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();
+ }
+
+ /**
* Return {@link #setTemporaryAppAllowlist}.
* @hide
*/
@@ -308,8 +328,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 (isTemporaryAppAllowlistSet()) {
b.putLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION, mTemporaryAppAllowlistDuration);
b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE, mTemporaryAppAllowlistType);
diff --git a/core/java/android/app/ComponentOptions.java b/core/java/android/app/ComponentOptions.java
new file mode 100644
index 000000000000..86efd622cb82
--- /dev/null
+++ b/core/java/android/app/ComponentOptions.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 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/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9e35a32638a8..76c67fa6645b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -10995,7 +10995,7 @@ public class Intent implements Parcelable, Cloneable {
private void toUriInner(StringBuilder uri, String scheme, String defAction,
String defPackage, int flags) {
if (scheme != null) {
- uri.append("scheme=").append(scheme).append(';');
+ uri.append("scheme=").append(Uri.encode(scheme)).append(';');
}
if (mAction != null && !mAction.equals(defAction)) {
uri.append("action=").append(Uri.encode(mAction)).append(';');
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index b1252fd0b21f..49d3cac63124 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -19,6 +19,7 @@ package android.content;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.PendingIntentInfo;
+import android.app.ActivityOptions;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.Handler;
@@ -158,7 +159,7 @@ public class IntentSender implements Parcelable {
*/
public void sendIntent(Context context, int code, Intent intent,
OnFinished onFinished, Handler handler) throws SendIntentException {
- sendIntent(context, code, intent, onFinished, handler, null);
+ sendIntent(context, code, intent, onFinished, handler, null, null /* options */);
}
/**
@@ -190,6 +191,42 @@ public class IntentSender implements Parcelable {
public void sendIntent(Context context, int code, Intent intent,
OnFinished onFinished, Handler handler, String requiredPermission)
throws SendIntentException {
+ sendIntent(context, code, intent, onFinished, handler, requiredPermission,
+ null /* options */);
+ }
+
+ /**
+ * Perform the operation associated with this IntentSender, allowing the
+ * caller to specify information about the Intent to use and be notified
+ * when the send has completed.
+ *
+ * @param context The Context of the caller. This may be null if
+ * <var>intent</var> is also null.
+ * @param code Result code to supply back to the IntentSender's target.
+ * @param intent Additional Intent data. See {@link Intent#fillIn
+ * Intent.fillIn()} for information on how this is applied to the
+ * original Intent. Use null to not modify the original Intent.
+ * @param onFinished The object to call back on when the send has
+ * completed, or null for no callback.
+ * @param handler Handler identifying the thread on which the callback
+ * should happen. If null, the callback will happen from the thread
+ * pool of the process.
+ * @param requiredPermission Name of permission that a recipient of the PendingIntent
+ * is required to hold. This is only valid for broadcast intents, and
+ * corresponds to the permission argument in
+ * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}.
+ * If null, no permission is required.
+ * @param options Additional options the caller would like to provide to modify the sending
+ * behavior. May be built from an {@link ActivityOptions} to apply to an activity start.
+ *
+ * @throws SendIntentException Throws CanceledIntentException if the IntentSender
+ * is no longer allowing more intents to be sent through it.
+ * @hide
+ */
+ public void sendIntent(Context context, int code, Intent intent,
+ OnFinished onFinished, Handler handler, String requiredPermission,
+ @Nullable Bundle options)
+ throws SendIntentException {
try {
String resolvedType = intent != null ?
intent.resolveTypeIfNeeded(context.getContentResolver())
@@ -199,7 +236,7 @@ public class IntentSender implements Parcelable {
onFinished != null
? new FinishedDispatcher(this, onFinished, handler)
: null,
- requiredPermission, null);
+ requiredPermission, options);
if (res < 0) {
throw new SendIntentException();
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a8672ff38e9d..8c80dfb94d53 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3513,8 +3513,7 @@ public class AccountManagerService
Bundle.setDefusable(result, true);
mNumResults++;
Intent intent = null;
- if (result != null
- && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
+ if (result != null) {
if (!checkKeyIntent(
Binder.getCallingUid(),
result)) {
@@ -4873,8 +4872,10 @@ public class AccountManagerService
EventLog.writeEvent(0x534e4554, "250588548", authUid, "");
return false;
}
-
Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
+ if (intent == null) {
+ return true;
+ }
// Explicitly set an empty ClipData to ensure that we don't offer to
// promote any Uris contained inside for granting purposes
if (intent.getClipData() == null) {
@@ -4927,7 +4928,10 @@ public class AccountManagerService
p.recycle();
Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
Intent simulateIntent = simulateBundle.getParcelable(AccountManager.KEY_INTENT);
- return (intent.filterEquals(simulateIntent));
+ if (intent == null) {
+ return (simulateIntent == null);
+ }
+ return intent.filterEquals(simulateIntent);
}
private boolean isExportedSystemActivity(ActivityInfo activityInfo) {
@@ -5072,8 +5076,7 @@ public class AccountManagerService
}
}
}
- if (result != null
- && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
+ if (result != null) {
if (!checkKeyIntent(
Binder.getCallingUid(),
result)) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 49bfd4dcfd38..21cfd1db17c3 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3200,6 +3200,11 @@ public final class ActiveServices {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, "
+ className + " is not an isolatedProcess");
}
+ if (AppGlobals.getPackageManager().getPackageUid(callingPackage,
+ 0, userId) != callingUid) {
+ throw new SecurityException("BIND_EXTERNAL_SERVICE failed, "
+ + "calling package not owned by calling UID ");
+ }
// Run the service under the calling package's application.
ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 710d0db3c542..5e2180d5f40c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13785,6 +13785,17 @@ public class ActivityManagerService extends IActivityManager.Stub
throw new SecurityException(msg);
}
}
+ if (!Build.IS_DEBUGGABLE && callingUid != ROOT_UID && callingUid != SHELL_UID
+ && callingUid != SYSTEM_UID && !hasActiveInstrumentationLocked(callingPid)) {
+ // If it's not debug build and not called from root/shell/system uid, reject it.
+ final String msg = "Permission Denial: instrumentation test "
+ + className + " from pid=" + callingPid + ", uid=" + callingUid
+ + ", pkgName=" + getPackageNameByPid(callingPid)
+ + " not allowed because it's not started from SHELL";
+ Slog.wtfQuiet(TAG, msg);
+ reportStartInstrumentationFailureLocked(watcher, className, msg);
+ throw new SecurityException(msg);
+ }
ActiveInstrumentation activeInstr = new ActiveInstrumentation(this);
activeInstr.mClass = className;
@@ -13883,6 +13894,29 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ @GuardedBy("this")
+ private boolean hasActiveInstrumentationLocked(int pid) {
+ if (pid == 0) {
+ return false;
+ }
+ synchronized (mPidsSelfLocked) {
+ ProcessRecord process = mPidsSelfLocked.get(pid);
+ return process != null && process.getActiveInstrumentation() != null;
+ }
+ }
+
+ private String getPackageNameByPid(int pid) {
+ synchronized (mPidsSelfLocked) {
+ final ProcessRecord app = mPidsSelfLocked.get(pid);
+
+ if (app != null && app.info != null) {
+ return app.info.packageName;
+ }
+
+ return null;
+ }
+ }
+
private boolean isCallerShell() {
final int callingUid = Binder.getCallingUid();
return callingUid == SHELL_UID || callingUid == ROOT_UID;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index dc924c11f867..7e8dcd8db7b1 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -310,6 +310,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 allowlistToken,
IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
@@ -431,7 +450,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/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 8955c288391f..f29c737e60dc 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -258,6 +258,7 @@ public class LocationProviderManager extends
public void deliverOnFlushComplete(int requestCode) throws PendingIntent.CanceledException {
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setDontSendToRestrictedApps(true);
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
mPendingIntent.send(mContext, 0, new Intent().putExtra(KEY_FLUSH_COMPLETE, requestCode),
null, null, null, options.toBundle());
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index c6cb3c0d2ab7..da8b1f4130ac 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -98,6 +98,8 @@ public class PreferencesHelper implements RankingConfig {
@VisibleForTesting
static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
+ @VisibleForTesting
+ static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 6000;
private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000;
private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000;
@@ -239,6 +241,7 @@ public class PreferencesHelper implements RankingConfig {
}
}
boolean skipWarningLogged = false;
+ boolean skipGroupWarningLogged = false;
boolean hasSAWPermission = false;
if (upgradeForBubbles && uid != UNKNOWN_UID) {
hasSAWPermission = mAppOps.noteOpNoThrow(
@@ -289,6 +292,14 @@ public class PreferencesHelper implements RankingConfig {
String tagName = parser.getName();
// Channel groups
if (TAG_GROUP.equals(tagName)) {
+ if (r.groups.size() >= NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT) {
+ if (!skipGroupWarningLogged) {
+ Slog.w(TAG, "Skipping further groups for " + r.pkg
+ + "; app has too many");
+ skipGroupWarningLogged = true;
+ }
+ continue;
+ }
String id = parser.getAttributeValue(null, ATT_ID);
CharSequence groupName = parser.getAttributeValue(null,
ATT_NAME);
@@ -799,6 +810,9 @@ public class PreferencesHelper implements RankingConfig {
}
if (fromTargetApp) {
group.setBlocked(false);
+ if (r.groups.size() >= NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT) {
+ throw new IllegalStateException("Limit exceed; cannot create more groups");
+ }
}
final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
if (oldGroup != null) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 708b2129ad29..2ca3e8f1bc1b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PackageDeleteObserver;
@@ -1243,7 +1244,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
PackageInstaller.STATUS_PENDING_USER_ACTION);
fillIn.putExtra(Intent.EXTRA_INTENT, intent);
try {
- mTarget.sendIntent(mContext, 0, fillIn, null, null);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ mTarget.sendIntent(mContext, 0, fillIn, null /* onFinished*/,
+ null /* handler */, null /* requiredPermission */, options.toBundle());
} catch (SendIntentException ignored) {
}
}
@@ -1268,7 +1272,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
PackageManager.deleteStatusToString(returnCode, msg));
fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
try {
- mTarget.sendIntent(mContext, 0, fillIn, null, null);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ mTarget.sendIntent(mContext, 0, fillIn, null /* onFinished*/,
+ null /* handler */, null /* requiredPermission */, options.toBundle());
} catch (SendIntentException ignored) {
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 3ddcf17d0a47..1137fc5b5a86 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -50,6 +50,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyEventLogger;
@@ -1872,7 +1873,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
} else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) {
try {
- mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ mStatusReceiver.sendIntent(mContext, 0, intent,
+ null /* onFinished*/, null /* handler */,
+ null /* requiredPermission */, options.toBundle());
} catch (IntentSender.SendIntentException ignore) {
}
} else { // failure, let's forward and clean up this session.
@@ -4375,7 +4380,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
fillIn.putExtra(Intent.EXTRA_INTENT, intent);
try {
- target.sendIntent(context, 0, fillIn, null, null);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ target.sendIntent(context, 0, fillIn, null /* onFinished */,
+ null /* handler */, null /* requiredPermission */, options.toBundle());
} catch (IntentSender.SendIntentException ignored) {
}
}
@@ -4418,7 +4426,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
try {
- target.sendIntent(context, 0, fillIn, null, null);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ target.sendIntent(context, 0, fillIn, null /* onFinished */,
+ null /* handler */, null /* requiredPermission */, options.toBundle());
} catch (IntentSender.SendIntentException ignored) {
}
}
@@ -4443,7 +4454,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
}
try {
- target.sendIntent(context, 0, intent, null, null);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ target.sendIntent(context, 0, intent, null /* onFinished */,
+ null /* handler */, null /* requiredPermission */, options.toBundle());
} catch (IntentSender.SendIntentException ignored) {
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 139d44928d91..508a23a35548 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8871,7 +8871,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (pi != null) {
try {
- pi.sendIntent(null, success ? 1 : 0, null, null, null);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ pi.sendIntent(null, success ? 1 : 0, null /* intent */, null /* onFinished*/,
+ null /* handler */, null /* requiredPermission */, options.toBundle());
} catch (SendIntentException e) {
Slog.w(TAG, e);
}
@@ -16259,7 +16262,10 @@ public class PackageManagerService extends IPackageManager.Stub
fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
PackageManager.installStatusToPublicStatus(returnCode));
try {
- target.sendIntent(context, 0, fillIn, null, null);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ target.sendIntent(context, 0, fillIn, null /* onFinished*/,
+ null /* handler */, null /* requiredPermission */, options.toBundle());
} catch (SendIntentException ignored) {
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a9a25fc2d272..1df218ab0597 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -981,6 +981,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 {
@@ -989,15 +993,12 @@ class ActivityStarter {
restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
request.originatingPendingIntent, request.allowBackgroundActivityStart,
- intent);
+ intent, checkedOptions);
} finally {
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) {
checkedOptions = mService.getActivityStartController()
.getPendingRemoteAnimationRegistry()
@@ -1239,7 +1240,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
@@ -1308,9 +1309,12 @@ class ActivityStarter {
? isCallingUidPersistentSystemProcess
: (realCallingAppId == Process.SYSTEM_UID)
|| realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
- if (realCallingUid != callingUid) {
- // don't abort if the realCallingUid has a visible window
- // TODO(b/171459802): We should check appSwitchAllowed also
+
+ // Legacy behavior allows to use caller foreground state to bypass BAL restriction.
+ final boolean balAllowedByPiSender =
+ PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);
+
+ if (balAllowedByPiSender && realCallingUid != callingUid) {
if (realCallingUidHasAnyVisibleWindow) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
@@ -1383,9 +1387,9 @@ 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 state.
+ // caller if caller allows, so that we can make the decision based on its 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 f3ba56a03aef..987cff95bba3 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2048,7 +2048,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 7f0adcacc951..d4e8541789c7 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -84,7 +84,7 @@ class AppTaskImpl extends IAppTask.Stub {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
return mService.getRecentTasks().createRecentTaskInfo(task,
- false /* stripExtras */);
+ false /* stripExtras */, true /* getTasksAllowed */);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -108,7 +108,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;
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 455f568d7523..9726c2c838c6 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -974,7 +974,7 @@ class RecentTasks {
continue;
}
- res.add(createRecentTaskInfo(task, true /* stripExtras */));
+ res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed));
}
return res;
}
@@ -1886,7 +1886,8 @@ class RecentTasks {
/**
* Creates a new RecentTaskInfo from a Task.
*/
- ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) {
+ ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras,
+ boolean getTasksAllowed) {
final ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
// If the recent Task is detached, we consider it will be re-attached to the default
// TaskDisplayArea because we currently only support recent overview in the default TDA.
@@ -1898,6 +1899,9 @@ class RecentTasks {
rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
rti.persistentId = rti.taskId;
rti.lastSnapshotData.set(tr.mLastTaskSnapshotData);
+ if (!getTasksAllowed) {
+ Task.trimIneffectiveInfo(tr, rti);
+ }
// Fill in organized child task info for the task created by organizer.
if (tr.mCreatedByOrganizer) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 7ba772c18455..bd201557e75f 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -137,6 +137,10 @@ class RunningTasks {
task.fillTaskInfo(rti, !mKeepIntentExtra);
// Fill in some deprecated values
rti.id = rti.taskId;
+
+ if (!mAllowed) {
+ Task.trimIneffectiveInfo(task, rti);
+ }
return rti;
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 12ee8ce9ba9c..1dbb3366461b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4156,6 +4156,54 @@ class Task extends WindowContainer<WindowContainer> {
info.mTopActivityLocusId = topRecord != null ? topRecord.getLocusId() : null;
}
+ /**
+ * Removes the activity info if the activity belongs to a different uid, which is
+ * different from the app that hosts the task.
+ */
+ static void trimIneffectiveInfo(Task task, TaskInfo info) {
+ final ActivityRecord baseActivity = task.getActivity(r -> !r.finishing,
+ false /* traverseTopToBottom */);
+ final int baseActivityUid =
+ baseActivity != null ? baseActivity.getUid() : task.effectiveUid;
+
+ if (info.topActivityInfo != null
+ && task.effectiveUid != info.topActivityInfo.applicationInfo.uid) {
+ // Making a copy to prevent eliminating the info in the original ActivityRecord.
+ info.topActivityInfo = new ActivityInfo(info.topActivityInfo);
+ info.topActivityInfo.applicationInfo =
+ new ApplicationInfo(info.topActivityInfo.applicationInfo);
+
+ // Strip the sensitive info.
+ info.topActivity = new ComponentName("", "");
+ info.topActivityInfo.packageName = "";
+ info.topActivityInfo.taskAffinity = "";
+ info.topActivityInfo.processName = "";
+ info.topActivityInfo.name = "";
+ info.topActivityInfo.parentActivityName = "";
+ info.topActivityInfo.targetActivity = "";
+ info.topActivityInfo.splitName = "";
+ info.topActivityInfo.applicationInfo.className = "";
+ info.topActivityInfo.applicationInfo.credentialProtectedDataDir = "";
+ info.topActivityInfo.applicationInfo.dataDir = "";
+ info.topActivityInfo.applicationInfo.deviceProtectedDataDir = "";
+ info.topActivityInfo.applicationInfo.manageSpaceActivityName = "";
+ info.topActivityInfo.applicationInfo.nativeLibraryDir = "";
+ info.topActivityInfo.applicationInfo.nativeLibraryRootDir = "";
+ info.topActivityInfo.applicationInfo.processName = "";
+ info.topActivityInfo.applicationInfo.publicSourceDir = "";
+ info.topActivityInfo.applicationInfo.scanPublicSourceDir = "";
+ info.topActivityInfo.applicationInfo.scanSourceDir = "";
+ info.topActivityInfo.applicationInfo.sourceDir = "";
+ info.topActivityInfo.applicationInfo.taskAffinity = "";
+ info.topActivityInfo.applicationInfo.name = "";
+ info.topActivityInfo.applicationInfo.packageName = "";
+ }
+
+ if (task.effectiveUid != baseActivityUid) {
+ info.baseActivity = new ComponentName("", "");
+ }
+ }
+
@Nullable PictureInPictureParams getPictureInPictureParams() {
return getPictureInPictureParams(getTopMostTask());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 16afef57d31e..9363420a0f8f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -109,6 +109,7 @@ import static org.mockito.Mockito.times;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
@@ -552,13 +553,23 @@ public class AlarmManagerServiceTest {
private PendingIntent getNewMockPendingIntent() {
- return getNewMockPendingIntent(TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ return getNewMockPendingIntent(false);
+ }
+
+ private PendingIntent getNewMockPendingIntent(boolean isActivity) {
+ return getNewMockPendingIntent(TEST_CALLING_UID, TEST_CALLING_PACKAGE, isActivity);
}
private PendingIntent getNewMockPendingIntent(int creatorUid, String creatorPackage) {
+ return getNewMockPendingIntent(creatorUid, creatorPackage, false);
+ }
+
+ private PendingIntent getNewMockPendingIntent(int creatorUid, String creatorPackage,
+ boolean isActivity) {
final PendingIntent mockPi = mock(PendingIntent.class, Answers.RETURNS_DEEP_STUBS);
when(mockPi.getCreatorUid()).thenReturn(creatorUid);
when(mockPi.getCreatorPackage()).thenReturn(creatorPackage);
+ when(mockPi.isActivity()).thenReturn(isActivity);
return mockPi;
}
@@ -2801,21 +2812,53 @@ public class AlarmManagerServiceTest {
anyString()));
}
- @Test
- public void idleOptionsSentOnExpiration() throws Exception {
+ private void optionsSentOnExpiration(boolean isActivity, Bundle idleOptions)
+ throws Exception {
final long triggerTime = mNowElapsedTest + 5000;
- final PendingIntent alarmPi = getNewMockPendingIntent();
- final Bundle idleOptions = new Bundle();
- idleOptions.putChar("TEST_CHAR_KEY", 'x');
- idleOptions.putInt("TEST_INT_KEY", 53);
+ final PendingIntent alarmPi = getNewMockPendingIntent(isActivity);
setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, 0, alarmPi, 0, 0, TEST_CALLING_UID,
idleOptions);
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
- any(), any(Handler.class), isNull(), eq(idleOptions));
+ any(), any(Handler.class), isNull(), bundleCaptor.capture());
+ if (idleOptions != null) {
+ assertEquals(idleOptions, bundleCaptor.getValue());
+ } else {
+ assertFalse("BAL flag needs to be false in alarm manager",
+ bundleCaptor.getValue().getBoolean(
+ ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
+ true));
+ }
+ }
+
+ @Test
+ public void activityIdleOptionsSentOnExpiration() throws Exception {
+ final Bundle idleOptions = new Bundle();
+ idleOptions.putChar("TEST_CHAR_KEY", 'x');
+ idleOptions.putInt("TEST_INT_KEY", 53);
+ optionsSentOnExpiration(true, idleOptions);
+ }
+
+ @Test
+ public void broadcastIdleOptionsSentOnExpiration() throws Exception {
+ final Bundle idleOptions = new Bundle();
+ idleOptions.putChar("TEST_CHAR_KEY", 'x');
+ idleOptions.putInt("TEST_INT_KEY", 53);
+ optionsSentOnExpiration(false, idleOptions);
+ }
+
+ @Test
+ public void emptyActivityOptionsSentOnExpiration() throws Exception {
+ optionsSentOnExpiration(true, null);
+ }
+
+ @Test
+ public void emptyBroadcastOptionsSentOnExpiration() throws Exception {
+ optionsSentOnExpiration(false, null);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index cc1483594a41..6deb7af4fca8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -42,6 +42,7 @@ import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IS
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.UID_FIELD_NUMBER;
import static com.android.server.notification.PreferencesHelper.DEFAULT_BUBBLE_PREFERENCE;
import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
+import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT;
import static com.android.server.notification.PreferencesHelper.UNKNOWN_UID;
import static com.google.common.truth.Truth.assertThat;
@@ -3174,6 +3175,52 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testTooManyGroups() {
+ for (int i = 0; i < NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT; i++) {
+ NotificationChannelGroup group = new NotificationChannelGroup(String.valueOf(i),
+ String.valueOf(i));
+ mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+ }
+ try {
+ NotificationChannelGroup group = new NotificationChannelGroup(
+ String.valueOf(NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT),
+ String.valueOf(NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT));
+ mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+ fail("Allowed to create too many notification channel groups");
+ } catch (IllegalStateException e) {
+ // great
+ }
+ }
+
+ @Test
+ public void testTooManyGroups_xml() throws Exception {
+ String extraGroup = "EXTRA";
+ String extraGroup1 = "EXTRA1";
+
+ // create first... many... directly so we don't need a big xml blob in this test
+ for (int i = 0; i < NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT; i++) {
+ NotificationChannelGroup group = new NotificationChannelGroup(String.valueOf(i),
+ String.valueOf(i));
+ mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+ }
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ + "<channelGroup id=\"" + extraGroup + "\" name=\"hi\"/>"
+ + "<channelGroup id=\"" + extraGroup1 + "\" name=\"hi2\"/>"
+ + "</package>"
+ + "</ranking>";
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertNull(mHelper.getNotificationChannelGroup(extraGroup, PKG_O, UID_O));
+ assertNull(mHelper.getNotificationChannelGroup(extraGroup1, PKG_O, UID_O));
+ }
+
+ @Test
public void testRestoreMultiUser() throws Exception {
String pkg = "restore_pkg";
String channelId = "channelId";
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 5af68021b201..d60e535cb04a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -30,6 +30,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.os.Process.NOBODY_UID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -1145,21 +1146,36 @@ public class RecentTasksTest extends WindowTestsBase {
@Test
public void testCreateRecentTaskInfo_detachedTask() {
- final Task task = createTaskBuilder(".Task").setCreateActivity(true).build();
+ final Task task = createTaskBuilder(".Task").build();
+ final ComponentName componentName = new ComponentName("com.foo", ".BarActivity");
+ new ActivityBuilder(mSupervisor.mService)
+ .setTask(task)
+ .setUid(NOBODY_UID)
+ .setComponent(componentName)
+ .build();
final TaskDisplayArea tda = task.getDisplayArea();
assertTrue(task.isAttached());
assertTrue(task.supportsMultiWindow());
- RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true);
+ RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
assertTrue(info.supportsSplitScreenMultiWindow);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ false /* getTasksAllowed */);
+
+ assertFalse(info.topActivity.equals(componentName));
+ assertFalse(info.topActivityInfo.packageName.equals(componentName.getPackageName()));
+ assertFalse(info.baseActivity.equals(componentName));
+
// The task can be put in split screen even if it is not attached now.
task.removeImmediately();
- info = mRecentTasks.createRecentTaskInfo(task, true);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
assertTrue(info.supportsSplitScreenMultiWindow);
@@ -1169,7 +1185,8 @@ public class RecentTasksTest extends WindowTestsBase {
doReturn(false).when(tda).supportsNonResizableMultiWindow();
doReturn(false).when(task).isResizeable();
- info = mRecentTasks.createRecentTaskInfo(task, true);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertFalse(info.supportsMultiWindow);
assertFalse(info.supportsSplitScreenMultiWindow);
@@ -1178,7 +1195,8 @@ public class RecentTasksTest extends WindowTestsBase {
// the device supports it.
doReturn(true).when(tda).supportsNonResizableMultiWindow();
- info = mRecentTasks.createRecentTaskInfo(task, true);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
assertTrue(info.supportsSplitScreenMultiWindow);