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

Commit a33f399c authored by Willie Koomson's avatar Willie Koomson
Browse files

Allow Play Store to pin widgets for other apps

This change updates AppWidgetServiceImpl to allow apps with
INSTALL_PACKAGES permission to pin widgets for other apps.

Bug: 414841181
Test: AppWidgetServiceImplTest.testRequestPinAppWidget_otherProvider_installPackagesPermission
Flag: EXEMPT bugfix
Change-Id: I89b03bd9599f7b46b5c797d60ed0a85cf5375088
parent 6e36aa65
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -110,3 +110,13 @@ flag {
  description: "Remote document features in Q4 2025 release"
  bug: "412684851"
}

flag {
  name: "play_store_pin_widgets"
  namespace: "app_widgets"
  description: "Allow apps with INSTALL_PACKAGES to pin widgets for other packages"
  bug: "414841181"
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}
+10 −4
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.appwidget;

import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL;
import static android.appwidget.flags.Flags.playStorePinWidgets;
import static android.appwidget.flags.Flags.remoteAdapterConversion;
import static android.appwidget.flags.Flags.remoteViewsProto;
import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
@@ -2545,7 +2546,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            if (!mPackageManagerInternal.isSameApp(pkg, callingUid, userId)) {
                // If the calling process is requesting to pin appwidgets from another process,
                // check if the calling process has the necessary permission.
                if (!injectHasAccessWidgetsPermission(Binder.getCallingPid(), callingUid)) {
                if (!injectHasPinWidgetsPermission(Binder.getCallingPid(), callingUid)) {
                    return false;
                }
                id = new ProviderId(mPackageManagerInternal.getPackageUid(
@@ -2571,9 +2572,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    /**
     * Returns true if the caller has the proper permission to access app widgets.
     */
    private boolean injectHasAccessWidgetsPermission(int callingPid, int callingUid) {
        return mContext.checkPermission(Manifest.permission.CLEAR_APP_USER_DATA,
                callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
    private boolean injectHasPinWidgetsPermission(int callingPid, int callingUid) {
        boolean hasClearAppUserData = mContext.checkPermission(
                Manifest.permission.CLEAR_APP_USER_DATA, callingPid, callingUid)
                == PackageManager.PERMISSION_GRANTED;
        boolean hasInstallPackages = playStorePinWidgets() && mContext.checkPermission(
                Manifest.permission.INSTALL_PACKAGES, callingPid, callingUid)
                == PackageManager.PERMISSION_GRANTED;
        return hasClearAppUserData || hasInstallPackages;
    }

    /**
+34 −0
Original line number Diff line number Diff line
@@ -24,8 +24,10 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -36,9 +38,11 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManagerInternal;
import android.app.UiAutomation;
import android.app.admin.DevicePolicyManagerInternal;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetManagerInternal;
@@ -74,6 +78,7 @@ import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -116,6 +121,7 @@ public class AppWidgetServiceImplTest {
    private PackageManagerInternal mMockPackageManager;
    private AppOpsManagerInternal mMockAppOpsManagerInternal;
    private IAppWidgetHost mMockHost;
    private UiAutomation mUiAutomation;

    @Before
    public void setUp() throws Exception {
@@ -141,6 +147,13 @@ public class AppWidgetServiceImplTest {
                .thenReturn(false);
        mService.onStart();
        mService.systemServicesReady();

        mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
    }

    @After
    public void tearDown() {
        mUiAutomation.dropShellPermissionIdentity();
    }

    @Test
@@ -186,6 +199,27 @@ public class AppWidgetServiceImplTest {
        assertFalse(mManager.requestPinAppWidget(otherProvider, null, null));
    }

    @Test
    @EnableFlags(Flags.FLAG_PLAY_STORE_PIN_WIDGETS)
    public void testRequestPinAppWidget_otherProvider_installPackagesPermission() {
        mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES);
        ComponentName otherProvider = null;
        int uid = 0;
        for (AppWidgetProviderInfo provider : mManager.getInstalledProviders()) {
            if (!provider.provider.getPackageName().equals(mTestContext.getPackageName())) {
                otherProvider = provider.provider;
                uid = provider.providerInfo.applicationInfo.uid;
                break;
            }
        }
        assumeNotNull(otherProvider);
        when(mMockShortcutService.requestPinAppWidget(anyString(), any(AppWidgetProviderInfo.class),
                eq(null), eq(null), anyInt())).thenReturn(true);
        when(mMockPackageManager.getPackageUid(eq(otherProvider.getPackageName()), anyLong(),
                anyInt())).thenReturn(uid);
        assertTrue(mManager.requestPinAppWidget(otherProvider, null, null));
    }

    @Test
    public void testRequestPinAppWidget() {
        ComponentName provider = new ComponentName(mTestContext, TestAppWidgetProvider.class);