Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b5b78b71 authored by Darrell Shi's avatar Darrell Shi
Browse files

Allow interact_across_users in app widget service

This change updates security policies in the app widget service to allow
packages that have interact_across_users permission to manage widgets on
behalf of other users.

Bug: 357621815
Test: verified able to host widgets from system user on hsum
Flag: android.appwidget.flags.security_policy_interact_across_users
Change-Id: I26b7274090096e54d6b6dea0a072e69bc914c609
parent ba72d5ed
Loading
Loading
Loading
Loading
+11 −1
Original line number Original line Diff line number Diff line
@@ -64,3 +64,13 @@ flag {
  description: "Remote document support features in Q2 2025 release"
  description: "Remote document support features in Q2 2025 release"
  bug: "339721781"
  bug: "339721781"
}
}

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
    }
}
+39 −12
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.appwidget;


import static android.appwidget.flags.Flags.remoteAdapterConversion;
import static android.appwidget.flags.Flags.remoteAdapterConversion;
import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
import static android.appwidget.flags.Flags.securityPolicyInteractAcrossUsers;
import static android.appwidget.flags.Flags.supportResumeRestoreAfterReboot;
import static android.appwidget.flags.Flags.supportResumeRestoreAfterReboot;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -32,6 +33,7 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.Manifest;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.PermissionName;
import android.annotation.RequiresPermission;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManager;
@@ -1463,13 +1465,15 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        mSecurityPolicy.enforceCallFromPackage(callingPackage);
        mSecurityPolicy.enforceCallFromPackage(callingPackage);


        // Check that if a cross-profile binding is attempted, it is allowed.
        // 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;
            return false;
        }
        }


        // If the provider is not under the calling user, make sure this
        // If the provider is not under the calling user, make sure this provider is allowlisted for
        // provider is allowlisted for access from the parent.
        // access from the parent, or that the caller has permission to interact across users.
        if (!mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
        if (!mSecurityPolicy.canAccessProvider(
                providerComponent.getPackageName(), providerProfileId)) {
                providerComponent.getPackageName(), providerProfileId)) {
            return false;
            return false;
        }
        }
@@ -2190,8 +2194,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            Slog.i(TAG, "getInstalledProvidersForProfiles() " + userId);
            Slog.i(TAG, "getInstalledProvidersForProfiles() " + userId);
        }
        }


        // Ensure the profile is in the group and enabled.
        // Ensure the profile is in the group and enabled, or that the caller has permission to
        if (!mSecurityPolicy.isEnabledGroupProfile(profileId)) {
        // interact across users.
        if (!mSecurityPolicy.isEnabledGroupProfile(profileId)
                && !mSecurityPolicy.hasCallerInteractAcrossUsersPermission()) {
            return null;
            return null;
        }
        }


@@ -2226,7 +2232,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                // Add providers only for the requested profile that are allowlisted.
                // Add providers only for the requested profile that are allowlisted.
                final int providerProfileId = info.getProfile().getIdentifier();
                final int providerProfileId = info.getProfile().getIdentifier();
                if (providerProfileId == profileId
                if (providerProfileId == profileId
                        && mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
                        && mSecurityPolicy.canAccessProvider(
                        providerPackageName, providerProfileId)
                        providerPackageName, providerProfileId)
                        && !mPackageManagerInternal.filterAppAccess(providerPackageName, callingUid,
                        && !mPackageManagerInternal.filterAppAccess(providerPackageName, callingUid,
                        profileId)) {
                        profileId)) {
@@ -4620,7 +4626,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                final int callingUid = Binder.getCallingUid();
                final int callingUid = Binder.getCallingUid();
                final String providerPackageName = componentName.getPackageName();
                final String providerPackageName = componentName.getPackageName();
                final boolean providerIsInCallerProfile =
                final boolean providerIsInCallerProfile =
                        mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
                        mSecurityPolicy.canAccessProvider(
                                providerPackageName, providerProfileId);
                                providerPackageName, providerProfileId);
                final boolean shouldFilterAppAccess = mPackageManagerInternal.filterAppAccess(
                final boolean shouldFilterAppAccess = mPackageManagerInternal.filterAppAccess(
                        providerPackageName, callingUid, providerProfileId);
                        providerPackageName, callingUid, providerProfileId);
@@ -4948,8 +4954,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            final int userId = UserHandle.getUserId(uid);
            final int userId = UserHandle.getUserId(uid);
            if ((widget.host.getUserId() == userId || (widget.provider != null
            if ((widget.host.getUserId() == userId || (widget.provider != null
                    && widget.provider.getUserId() == userId))
                    && widget.provider.getUserId() == userId))
                && mContext.checkCallingPermission(android.Manifest.permission.BIND_APPWIDGET)
                    && callerHasPermission(android.Manifest.permission.BIND_APPWIDGET)) {
                    == PackageManager.PERMISSION_GRANTED) {
                // Apps that run in the same user as either the host or the provider and
                // 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.
                // have the bind widget permission have access to the widget.
                return true;
                return true;
@@ -4968,12 +4973,20 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            return getProfileParent(profileId) == parentId;
            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();
            final int callerId = UserHandle.getCallingUserId();
            if (profileId == callerId) {
            if (profileId == callerId) {
                return true;
                return true;
            }
            }
            if (hasCallerInteractAcrossUsersPermission()) {
                return true;
            }
            final int parentId = getProfileParent(profileId);
            final int parentId = getProfileParent(profileId);
            if (parentId != callerId) {
            if (parentId != callerId) {
                return false;
                return false;
@@ -5041,6 +5054,20 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            }
            }
            return true;
            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 {
    static final class Provider {