Merge cherrypicks of ['googleplex-android-review.googlesource.com/29753252', 'googleplex-android-review.googlesource.com/30882111', 'googleplex-android-review.googlesource.com/31089761', 'googleplex-android-review.googlesource.com/31434165', 'googleplex-android-review.googlesource.com/31583040', 'googleplex-android-review.googlesource.com/31221230', 'googleplex-android-review.googlesource.com/31616632', 'googleplex-android-review.googlesource.com/32385125', 'googleplex-android-review.googlesource.com/32343661', 'googleplex-android-review.googlesource.com/32343678', 'googleplex-android-review.googlesource.com/32343679'] into 25Q1-release.
Change-Id: I663ac7408fd715e1e7906a9511c84e41949dcbb8
diff --git a/core/java/android/app/BackgroundStartPrivileges.java b/core/java/android/app/BackgroundStartPrivileges.java
index 20278ea..adea0a8 100644
--- a/core/java/android/app/BackgroundStartPrivileges.java
+++ b/core/java/android/app/BackgroundStartPrivileges.java
@@ -23,12 +23,13 @@
import com.android.internal.util.Preconditions;
import java.util.List;
+import java.util.Objects;
/**
* Privileges granted to a Process that allows it to execute starts from the background.
* @hide
*/
-public class BackgroundStartPrivileges {
+public final class BackgroundStartPrivileges {
/** No privileges. */
public static final BackgroundStartPrivileges NONE = new BackgroundStartPrivileges(
false, false, null);
@@ -190,4 +191,22 @@
+ ", originatingToken=" + mOriginatingToken
+ ']';
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ BackgroundStartPrivileges that = (BackgroundStartPrivileges) o;
+ return mAllowsBackgroundActivityStarts == that.mAllowsBackgroundActivityStarts
+ && mAllowsBackgroundForegroundServiceStarts
+ == that.mAllowsBackgroundForegroundServiceStarts
+ && Objects.equals(mOriginatingToken, that.mOriginatingToken);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAllowsBackgroundActivityStarts,
+ mAllowsBackgroundForegroundServiceStarts,
+ mOriginatingToken);
+ }
}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index d5ba32c..3621981 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -75,4 +75,16 @@
bug: "362575865"
}
+flag {
+ name: "bal_strict_mode_grace_period"
+ namespace: "responsible_apis"
+ description: "Strict mode violation triggered by grace period usage"
+ bug: "384807495"
+}
+flag {
+ name: "bal_clear_allowlist_duration"
+ namespace: "responsible_apis"
+ description: "Clear the allowlist duration when clearAllowBgActivityStarts is called"
+ bug: "322159724"
+}
diff --git a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
index cf6266c..931d646 100644
--- a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
+++ b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
@@ -119,4 +119,15 @@
Arrays.asList(BSP_ALLOW_A, BSP_ALLOW_A, BSP_ALLOW_A, BSP_ALLOW_A)))
.isEqualTo(BSP_ALLOW_A);
}
+
+ @Test
+ public void backgroundStartPrivilege_equals_works() {
+ assertThat(NONE).isEqualTo(NONE);
+ assertThat(ALLOW_BAL).isEqualTo(ALLOW_BAL);
+ assertThat(ALLOW_FGS).isEqualTo(ALLOW_FGS);
+ assertThat(BSP_ALLOW_A).isEqualTo(BSP_ALLOW_A);
+ assertThat(NONE).isNotEqualTo(ALLOW_BAL);
+ assertThat(ALLOW_FGS).isNotEqualTo(ALLOW_BAL);
+ assertThat(BSP_ALLOW_A).isNotEqualTo(BSP_ALLOW_B);
+ }
}
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 626fa70..7e68239 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.NETWORK_STACK;
import static com.android.net.module.util.PermissionUtils.enforceAnyPermissionOf;
+import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1020,6 +1021,8 @@
@Override
@Nullable
public byte[] getFromVpnProfileStore(@NonNull String name) {
+ // TODO(b/307903113): Replace NETWORK_STACK permission and adopt proper permission
+ enforceNetworkStackPermission(mContext);
return mVpnProfileStore.get(name);
}
@@ -1037,6 +1040,8 @@
*/
@Override
public boolean putIntoVpnProfileStore(@NonNull String name, @NonNull byte[] blob) {
+ // TODO(b/307903113): Replace NETWORK_STACK permission and adopt proper permission
+ enforceNetworkStackPermission(mContext);
return mVpnProfileStore.put(name, blob);
}
@@ -1052,6 +1057,8 @@
*/
@Override
public boolean removeFromVpnProfileStore(@NonNull String name) {
+ // TODO(b/307903113): Replace NETWORK_STACK permission and adopt proper permission
+ enforceNetworkStackPermission(mContext);
return mVpnProfileStore.remove(name);
}
@@ -1069,6 +1076,8 @@
@Override
@NonNull
public String[] listFromVpnProfileStore(@NonNull String prefix) {
+ // TODO(b/307903113): Replace NETWORK_STACK permission and adopt proper permission
+ enforceNetworkStackPermission(mContext);
return mVpnProfileStore.list(prefix);
}
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index 354f281..aa06b7e 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -316,8 +316,7 @@
return null;
}
if (callerApp.info.uid != SYSTEM_UID
- && !callerApp.getPkgList().containsKey(callerPackage)
- && !"android".equals(callerPackage)) {
+ && !callerApp.getPkgList().containsKey(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 3817ba1..ac30be9 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -24,6 +24,8 @@
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static android.os.Process.ROOT_UID;
import static android.os.Process.SYSTEM_UID;
@@ -305,6 +307,10 @@
this.stringName = null;
}
+ @VisibleForTesting TempAllowListDuration getAllowlistDurationLocked(IBinder allowlistToken) {
+ return mAllowlistDuration.get(allowlistToken);
+ }
+
void setAllowBgActivityStarts(IBinder token, int flags) {
if (token == null) return;
if ((flags & FLAG_ACTIVITY_SENDER) != 0) {
@@ -323,6 +329,13 @@
mAllowBgActivityStartsForActivitySender.remove(token);
mAllowBgActivityStartsForBroadcastSender.remove(token);
mAllowBgActivityStartsForServiceSender.remove(token);
+ if (mAllowlistDuration != null) {
+ TempAllowListDuration duration = mAllowlistDuration.get(token);
+ if (duration != null
+ && duration.type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+ duration.type = TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
+ }
+ }
}
public void registerCancelListenerLocked(IResultReceiver receiver) {
@@ -703,7 +716,7 @@
return res;
}
- private BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
+ @VisibleForTesting BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
IBinder allowlistToken) {
return mAllowBgActivityStartsForActivitySender.contains(allowlistToken)
? BackgroundStartPrivileges.allowBackgroundActivityStarts(allowlistToken)
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 82a51c3..ef72481 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -180,6 +180,7 @@
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.pm.PackageList;
import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.ProtectedPackages;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
@@ -236,6 +237,13 @@
*/
private static final int CURRENT_VERSION = 1;
+ /**
+ * The upper limit of total number of attributed op entries that can be returned in a binder
+ * transaction to avoid TransactionTooLargeException
+ */
+ private static final int NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD = 2000;
+
+
private SensorPrivacyManager mSensorPrivacyManager;
// Write at most every 30 minutes.
@@ -301,6 +309,8 @@
private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ private ProtectedPackages mProtectedPackages;
+
/**
* Registered callbacks, called from {@link #collectAsyncNotedOp}.
*
@@ -1699,6 +1709,8 @@
Manifest.permission.GET_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid())
== PackageManager.PERMISSION_GRANTED;
+ int totalAttributedOpEntryCount = 0;
+
if (ops == null) {
resOps = new ArrayList<>();
for (int j = 0; j < pkgOps.size(); j++) {
@@ -1706,7 +1718,12 @@
if (opRestrictsRead(curOp.op) && !shouldReturnRestrictedAppOps) {
continue;
}
- resOps.add(getOpEntryForResult(curOp, persistentDeviceId));
+ if (totalAttributedOpEntryCount > NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD) {
+ break;
+ }
+ OpEntry opEntry = getOpEntryForResult(curOp, persistentDeviceId);
+ resOps.add(opEntry);
+ totalAttributedOpEntryCount += opEntry.getAttributedOpEntries().size();
}
} else {
for (int j = 0; j < ops.length; j++) {
@@ -1718,10 +1735,21 @@
if (opRestrictsRead(curOp.op) && !shouldReturnRestrictedAppOps) {
continue;
}
- resOps.add(getOpEntryForResult(curOp, persistentDeviceId));
+ if (totalAttributedOpEntryCount > NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD) {
+ break;
+ }
+ OpEntry opEntry = getOpEntryForResult(curOp, persistentDeviceId);
+ resOps.add(opEntry);
+ totalAttributedOpEntryCount += opEntry.getAttributedOpEntries().size();
}
}
}
+
+ if (totalAttributedOpEntryCount > NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD) {
+ Slog.w(TAG, "The number of attributed op entries has exceeded the threshold. This "
+ + "could be due to DoS attack from malicious apps. The result is throttled.");
+ }
+
return resOps;
}
@@ -2074,6 +2102,12 @@
enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingOp(code);
+
+ if (isDeviceProvisioningPackage(uid, null)) {
+ Slog.w(TAG, "Cannot set uid mode for device provisioning app by Shell");
+ return;
+ }
+
code = AppOpsManager.opToSwitch(code);
if (permissionPolicyCallback == null) {
@@ -2384,6 +2418,11 @@
return;
}
+ if (isDeviceProvisioningPackage(uid, packageName)) {
+ Slog.w(TAG, "Cannot set op mode for device provisioning app by Shell");
+ return;
+ }
+
code = AppOpsManager.opToSwitch(code);
PackageVerificationResult pvr;
@@ -2414,6 +2453,36 @@
notifyStorageManagerOpModeChangedSync(code, uid, packageName, mode, previousMode);
}
+ // Device provisioning package is restricted from setting app op mode through shell command
+ private boolean isDeviceProvisioningPackage(int uid,
+ @Nullable String packageName) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) == Process.SHELL_UID) {
+ ProtectedPackages protectedPackages = getProtectedPackages();
+
+ if (packageName != null && protectedPackages.isDeviceProvisioningPackage(packageName)) {
+ return true;
+ }
+
+ String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packageNames != null) {
+ for (String pkg : packageNames) {
+ if (protectedPackages.isDeviceProvisioningPackage(pkg)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ // Race condition is allowed here for better performance
+ private ProtectedPackages getProtectedPackages() {
+ if (mProtectedPackages == null) {
+ mProtectedPackages = new ProtectedPackages(mContext);
+ }
+ return mProtectedPackages;
+ }
+
private void notifyOpChanged(ArraySet<OnOpModeChangedListener> callbacks, int code,
int uid, String packageName, String persistentDeviceId) {
for (int i = 0; i < callbacks.size(); i++) {
diff --git a/services/core/java/com/android/server/pm/ProtectedPackages.java b/services/core/java/com/android/server/pm/ProtectedPackages.java
index 524252c..e715881 100644
--- a/services/core/java/com/android/server/pm/ProtectedPackages.java
+++ b/services/core/java/com/android/server/pm/ProtectedPackages.java
@@ -20,6 +20,7 @@
import android.annotation.UserIdInt;
import android.content.Context;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -27,6 +28,7 @@
import com.android.internal.annotations.GuardedBy;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -164,4 +166,13 @@
return hasDeviceOwnerOrProfileOwner(userId, packageName)
|| isProtectedPackage(userId, packageName);
}
+
+ /**
+ * Returns {@code true} if a given package is the device provisioning package. Otherwise,
+ * returns {@code false}.
+ */
+ public synchronized boolean isDeviceProvisioningPackage(String packageName) {
+ return !TextUtils.isEmpty(mDeviceProvisioningPackage) && Objects.equals(
+ mDeviceProvisioningPackage, packageName);
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 1a9d211..7b6ebbf 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -25,6 +25,9 @@
import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION;
import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES;
import static android.content.Context.KEYGUARD_SERVICE;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_HOME;
+import static android.content.Intent.CATEGORY_SECONDARY_HOME;
import static android.content.Intent.EXTRA_INTENT;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_TASK_ID;
@@ -40,6 +43,7 @@
import android.app.KeyguardManager;
import android.app.TaskInfo;
import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
@@ -119,6 +123,11 @@
*/
TaskDisplayArea mPresumableLaunchDisplayArea;
+ /**
+ * Whether the component is specified originally in the given Intent.
+ */
+ boolean mComponentSpecified;
+
ActivityStartInterceptor(
ActivityTaskManagerService service, ActivityTaskSupervisor supervisor) {
this(service, supervisor, service.mContext);
@@ -185,6 +194,14 @@
return TaskFragment.fromTaskFragmentToken(taskFragToken, mService);
}
+ // TODO: consolidate this method with the one below since this is used for test only.
+ boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
+ Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid,
+ ActivityOptions activityOptions, TaskDisplayArea presumableLaunchDisplayArea) {
+ return intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment, callingPid,
+ callingUid, activityOptions, presumableLaunchDisplayArea, false);
+ }
+
/**
* Intercept the launch intent based on various signals. If an interception happened the
* internal variables get assigned and need to be read explicitly by the caller.
@@ -193,7 +210,8 @@
*/
boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid,
- ActivityOptions activityOptions, TaskDisplayArea presumableLaunchDisplayArea) {
+ ActivityOptions activityOptions, TaskDisplayArea presumableLaunchDisplayArea,
+ boolean componentSpecified) {
mUserManager = UserManager.get(mServiceContext);
mIntent = intent;
@@ -206,6 +224,7 @@
mInTaskFragment = inTaskFragment;
mActivityOptions = activityOptions;
mPresumableLaunchDisplayArea = presumableLaunchDisplayArea;
+ mComponentSpecified = componentSpecified;
if (interceptQuietProfileIfNeeded()) {
// If work profile is turned off, skip the work challenge since the profile can only
@@ -230,7 +249,8 @@
}
if (interceptHomeIfNeeded()) {
// Replace primary home intents directed at displays that do not support primary home
- // but support secondary home with the relevant secondary home activity.
+ // but support secondary home with the relevant secondary home activity. Or the home
+ // intent is not in the correct format.
return true;
}
@@ -479,9 +499,72 @@
if (mPresumableLaunchDisplayArea == null || mService.mRootWindowContainer == null) {
return false;
}
- if (!ActivityRecord.isHomeIntent(mIntent)) {
+
+ boolean intercepted = false;
+ if (!ACTION_MAIN.equals(mIntent.getAction()) || (!mIntent.hasCategory(CATEGORY_HOME)
+ && !mIntent.hasCategory(CATEGORY_SECONDARY_HOME))) {
+ // not a home intent
return false;
}
+
+ if (mComponentSpecified) {
+ final ComponentName homeComponent = mIntent.getComponent();
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfo = mService.mRootWindowContainer.resolveHomeActivity(
+ mUserId, homeIntent);
+ if (!aInfo.getComponentName().equals(homeComponent)) {
+ // Do nothing if the intent is not for the default home component.
+ return false;
+ }
+ }
+
+ if (!ActivityRecord.isHomeIntent(mIntent) || mComponentSpecified) {
+ // This is not a standard home intent, make it so if possible.
+ normalizeHomeIntent();
+ intercepted = true;
+ }
+
+ intercepted |= replaceToSecondaryHomeIntentIfNeeded();
+ if (intercepted) {
+ mCallingPid = mRealCallingPid;
+ mCallingUid = mRealCallingUid;
+ mResolvedType = null;
+
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, /* flags= */ 0,
+ mRealCallingUid, mRealCallingPid);
+ mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, /*profilerInfo=*/
+ null);
+ }
+ return intercepted;
+ }
+
+ private void normalizeHomeIntent() {
+ Slog.w(TAG, "The home Intent is not correctly formatted");
+ if (mIntent.getCategories().size() > 1) {
+ Slog.d(TAG, "Purge home intent categories");
+ boolean isSecondaryHome = false;
+ final Object[] categories = mIntent.getCategories().toArray();
+ for (int i = categories.length - 1; i >= 0; i--) {
+ final String category = (String) categories[i];
+ if (CATEGORY_SECONDARY_HOME.equals(category)) {
+ isSecondaryHome = true;
+ }
+ mIntent.removeCategory(category);
+ }
+ mIntent.addCategory(isSecondaryHome ? CATEGORY_SECONDARY_HOME : CATEGORY_HOME);
+ }
+ if (mIntent.getType() != null || mIntent.getData() != null) {
+ Slog.d(TAG, "Purge home intent data/type");
+ mIntent.setType(null);
+ }
+ if (mComponentSpecified) {
+ Slog.d(TAG, "Purge home intent component, " + mIntent.getComponent());
+ mIntent.setComponent(null);
+ }
+ mIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ private boolean replaceToSecondaryHomeIntentIfNeeded() {
if (!mIntent.hasCategory(Intent.CATEGORY_HOME)) {
// Already a secondary home intent, leave it alone.
return false;
@@ -506,13 +589,6 @@
// and should not be moved to the caller's task. Also, activities cannot change their type,
// e.g. a standard activity cannot become a home activity.
mIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
- mCallingPid = mRealCallingPid;
- mCallingUid = mRealCallingUid;
- mResolvedType = null;
-
- mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, /* flags= */ 0,
- mRealCallingUid, mRealCallingPid);
- mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, /*profilerInfo=*/ null);
return true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 2781592..c352575 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1340,7 +1340,8 @@
callingPackage,
callingFeatureId);
if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
- callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) {
+ callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea,
+ request.componentSpecified)) {
// activity start was intercepted, e.g. because the target user is currently in quiet
// mode (turn off work) or the target application is suspended
intent = mInterceptor.mIntent;
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 0604953..559b063 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -653,6 +653,10 @@
if (!isSystemCaller) {
task.mLockTaskUid = callingUid;
if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
+ if (mLockTaskModeTasks.contains(task)) {
+ ProtoLog.w(WM_DEBUG_LOCKTASK, "Already locked.");
+ return;
+ }
// startLockTask() called by app, but app is not part of lock task allowlist. Show
// app pinning request. We will come back here with isSystemCaller true.
ProtoLog.w(WM_DEBUG_LOCKTASK, "Mode default, asking user");
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index db08be3..866798d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7206,12 +7206,12 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
@NeverCompile // Avoid size overhead of debugging code.
private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
boolean dumpAll = false;
int opti = 0;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
index 89b48ba..ab3784b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static android.os.Process.INVALID_UID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -27,9 +30,11 @@
import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_FORCE_STOPPED;
import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_SUPERSEDED;
import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_USER_STOPPED;
+import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.cancelReasonToString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -39,9 +44,11 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
+import android.app.BackgroundStartPrivileges;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.IPackageManager;
+import android.os.Binder;
import android.os.Looper;
import android.os.UserHandle;
@@ -179,6 +186,39 @@
}
}
+ @Test
+ public void testClearAllowBgActivityStartsClearsToken() {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ Binder token = new Binder();
+ pir.setAllowBgActivityStarts(token, FLAG_ACTIVITY_SENDER);
+ assertEquals(BackgroundStartPrivileges.allowBackgroundActivityStarts(token),
+ pir.getBackgroundStartPrivilegesForActivitySender(token));
+ pir.clearAllowBgActivityStarts(token);
+ assertEquals(BackgroundStartPrivileges.NONE,
+ pir.getBackgroundStartPrivilegesForActivitySender(token));
+ }
+
+ @Test
+ public void testClearAllowBgActivityStartsClearsDuration() {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ Binder token = new Binder();
+ pir.setAllowlistDurationLocked(token, 1000,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, REASON_NOTIFICATION_SERVICE,
+ "NotificationManagerService");
+ PendingIntentRecord.TempAllowListDuration allowlistDurationLocked =
+ pir.getAllowlistDurationLocked(token);
+ assertEquals(1000, allowlistDurationLocked.duration);
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ allowlistDurationLocked.type);
+ pir.clearAllowBgActivityStarts(token);
+ PendingIntentRecord.TempAllowListDuration allowlistDurationLockedAfterClear =
+ pir.getAllowlistDurationLocked(token);
+ assertNotNull(allowlistDurationLockedAfterClear);
+ assertEquals(1000, allowlistDurationLockedAfterClear.duration);
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+ allowlistDurationLocked.type);
+ }
+
private void assertCancelReason(int expectedReason, int actualReason) {
final String errMsg = "Expected: " + cancelReasonToString(expectedReason)
+ "; Actual: " + cancelReasonToString(actualReason);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 670f9f6..6c4f638 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -54,6 +54,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.Pair;
import android.util.SparseArray;
@@ -133,6 +134,8 @@
private SparseArray<ActivityInterceptorCallback> mActivityInterceptorCallbacks =
new SparseArray<>();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
@@ -237,6 +240,19 @@
}
@Test
+ public void testInterceptIncorrectHomeIntent() {
+ // Create a non-standard home intent
+ final Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ // Ensure the intent is intercepted and normalized to standard home intent.
+ assertTrue(mInterceptor.intercept(homeIntent, null, mAInfo, null, null, null, 0, 0, null,
+ mTaskDisplayArea, false));
+ assertTrue(ActivityRecord.isHomeIntent(homeIntent));
+ }
+
+ @Test
public void testInterceptLockTaskModeViolationPackage() {
when(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index bef4531..5122aee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -239,6 +239,11 @@
verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
// THEN screen pinning toast should be shown
verify(mStatusBarService).showPinningEnterExitToast(eq(true /* entering */));
+
+ // WHEN the app calls startLockTaskMode while the Task is already locked
+ mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+ // THEN a pinning request should NOT be shown
+ verify(mStatusBarManagerInternal, never()).showScreenPinningRequest(anyInt(), anyInt());
}
@Test