Merge "Add ResetPasswordWithToken migration code." into main
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 7117f25..ac9263c 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -63,4 +63,14 @@
namespace: "app_widgets"
description: "Remote document support features in Q2 2025 release"
bug: "339721781"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "security_policy_interact_across_users"
+ namespace: "app_widgets"
+ description: "Allow packages with interact_across_users permission to manage app widgets on behalf of other users."
+ bug: "357621815"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 157af7d..9726ecf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1013,6 +1013,7 @@
android:excludeFromRecents="true"
android:autoRemoveFromRecents="true"
android:launchMode="singleTop"
+ android:showForAllUsers="true"
android:exported="false">
</activity>
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b53bf98..b2c679f 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -18,6 +18,7 @@
import static android.appwidget.flags.Flags.remoteAdapterConversion;
import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
+import static android.appwidget.flags.Flags.securityPolicyInteractAcrossUsers;
import static android.appwidget.flags.Flags.supportResumeRestoreAfterReboot;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -32,6 +33,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.PermissionName;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -1463,13 +1465,15 @@
mSecurityPolicy.enforceCallFromPackage(callingPackage);
// Check that if a cross-profile binding is attempted, it is allowed.
- if (!mSecurityPolicy.isEnabledGroupProfile(providerProfileId)) {
+ // Cross-profile binding is also allowed if the caller has interact across users permission.
+ if (!mSecurityPolicy.isEnabledGroupProfile(providerProfileId)
+ && !mSecurityPolicy.hasCallerInteractAcrossUsersPermission()) {
return false;
}
- // If the provider is not under the calling user, make sure this
- // provider is allowlisted for access from the parent.
- if (!mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
+ // If the provider is not under the calling user, make sure this provider is allowlisted for
+ // access from the parent, or that the caller has permission to interact across users.
+ if (!mSecurityPolicy.canAccessProvider(
providerComponent.getPackageName(), providerProfileId)) {
return false;
}
@@ -2190,8 +2194,10 @@
Slog.i(TAG, "getInstalledProvidersForProfiles() " + userId);
}
- // Ensure the profile is in the group and enabled.
- if (!mSecurityPolicy.isEnabledGroupProfile(profileId)) {
+ // Ensure the profile is in the group and enabled, or that the caller has permission to
+ // interact across users.
+ if (!mSecurityPolicy.isEnabledGroupProfile(profileId)
+ && !mSecurityPolicy.hasCallerInteractAcrossUsersPermission()) {
return null;
}
@@ -2226,7 +2232,7 @@
// Add providers only for the requested profile that are allowlisted.
final int providerProfileId = info.getProfile().getIdentifier();
if (providerProfileId == profileId
- && mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
+ && mSecurityPolicy.canAccessProvider(
providerPackageName, providerProfileId)
&& !mPackageManagerInternal.filterAppAccess(providerPackageName, callingUid,
profileId)) {
@@ -4620,7 +4626,7 @@
final int callingUid = Binder.getCallingUid();
final String providerPackageName = componentName.getPackageName();
final boolean providerIsInCallerProfile =
- mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
+ mSecurityPolicy.canAccessProvider(
providerPackageName, providerProfileId);
final boolean shouldFilterAppAccess = mPackageManagerInternal.filterAppAccess(
providerPackageName, callingUid, providerProfileId);
@@ -4948,8 +4954,7 @@
final int userId = UserHandle.getUserId(uid);
if ((widget.host.getUserId() == userId || (widget.provider != null
&& widget.provider.getUserId() == userId))
- && mContext.checkCallingPermission(android.Manifest.permission.BIND_APPWIDGET)
- == PackageManager.PERMISSION_GRANTED) {
+ && callerHasPermission(android.Manifest.permission.BIND_APPWIDGET)) {
// Apps that run in the same user as either the host or the provider and
// have the bind widget permission have access to the widget.
return true;
@@ -4968,12 +4973,20 @@
return getProfileParent(profileId) == parentId;
}
- public boolean isProviderInCallerOrInProfileAndWhitelListed(String packageName,
- int profileId) {
+ /**
+ * The provider is accessible by the caller if any of the following is true:
+ * - The provider belongs to the caller
+ * - The provider belongs to a profile of the caller and is allowlisted
+ * - The caller has permission to interact across users
+ */
+ public boolean canAccessProvider(String packageName, int profileId) {
final int callerId = UserHandle.getCallingUserId();
if (profileId == callerId) {
return true;
}
+ if (hasCallerInteractAcrossUsersPermission()) {
+ return true;
+ }
final int parentId = getProfileParent(profileId);
if (parentId != callerId) {
return false;
@@ -5041,6 +5054,20 @@
}
return true;
}
+
+ /** Returns true if the caller has permission to interact across users. */
+ public boolean hasCallerInteractAcrossUsersPermission() {
+ if (!securityPolicyInteractAcrossUsers()) {
+ return false;
+ }
+
+ return callerHasPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ || callerHasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ }
+
+ private boolean callerHasPermission(@NonNull @PermissionName String permission) {
+ return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
+ }
}
static final class Provider {