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

Commit 4e903d6c authored by lpeter's avatar lpeter
Browse files

Apps that share the same UID should also receive the PACKAGE_CHANGED broadcast

Limit sending of the PACKAGE_CHANGED broadcast to only the system
the application itself and applications with the same user ID
when the component is not exported.

Flag: android.content.pm.reduce_broadcasts_for_component_state_changes

Bug: 376430671
Test: atest PackageChangedBroadcastTest
Change-Id: Ib658fe98941189979b6097d5a14f77a4c0312c70
parent b87170e4
Loading
Loading
Loading
Loading
+27 −8
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.provider.DeviceConfig;
import android.stats.storage.StorageEnums;
import android.text.TextUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -355,7 +356,8 @@ public final class BroadcastHelper {
            @Nullable int[] userIds,
            @Nullable int[] instantUserIds,
            @Nullable SparseArray<int[]> broadcastAllowList,
            @NonNull AndroidPackage pkg) {
            @NonNull AndroidPackage pkg,
            @NonNull String[] sharedUidPackages) {
        final boolean isForWholeApp = componentNames.contains(packageName);
        if (isForWholeApp || !android.content.pm.Flags.reduceBroadcastsForComponentStateChanges()) {
            sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
@@ -374,20 +376,36 @@ public final class BroadcastHelper {
        exportedComponentNames.removeAll(notExportedComponentNames);

        if (!notExportedComponentNames.isEmpty()) {
            // Limit sending of the PACKAGE_CHANGED broadcast to only the system and the
            // application itself when the component is not exported.
            // Limit sending of the PACKAGE_CHANGED broadcast to only the system, the application
            // itself and applications with the same UID when the component is not exported.

            // First, send the PACKAGE_CHANGED broadcast to the system.
            if (!TextUtils.equals(packageName, "android")) {
                sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                        notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
                        broadcastAllowList, "android" /* targetPackageName */,
                    new String[]{PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});
                        new String[]{
                                PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});
            }

            // Second, send the PACKAGE_CHANGED broadcast to the application itself.
            sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                    notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
                    broadcastAllowList, packageName /* targetPackageName */,
                    null /* requiredPermissions */);

            // Third, send the PACKAGE_CHANGED broadcast to the applications with the same UID.
            for (int i = 0; i < sharedUidPackages.length; i++) {
                final String sharedPackage = sharedUidPackages[i];
                if (TextUtils.equals(packageName, sharedPackage)) {
                    continue;
                }
                sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                        notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
                        broadcastAllowList, sharedPackage /* targetPackageName */,
                        null /* requiredPermissions */);
            }

        }

        if (!exportedComponentNames.isEmpty()) {
@@ -936,7 +954,8 @@ public final class BroadcastHelper {
                isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
        mHandler.post(() -> sendPackageChangedBroadcastInternal(
                packageName, dontKillApp, componentNames, packageUid, reason, userIds,
                instantUserIds, broadcastAllowList, setting.getPkg()));
                instantUserIds, broadcastAllowList, setting.getPkg(),
                snapshot.getSharedUserPackagesForPackage(packageName, userId)));
        mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
                packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler);
    }
+47 −4
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -60,6 +61,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
import java.util.List;

@AppModeFull
@AppModeNonSdkSandbox
@@ -124,7 +126,8 @@ public class BroadcastHelperTest {
    @Test
    public void changeNonExportedComponent_sendPackageChangedBroadcastToSystem_withPermission()
            throws Exception {
        changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */);
        changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
                new String[0] /* sharedPackages */);

        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
        verify(mMockActivityManagerInternal).broadcastIntentWithCallback(
@@ -140,7 +143,8 @@ public class BroadcastHelperTest {
    @Test
    public void changeNonExportedComponent_sendPackageChangedBroadcastToApplicationItself()
            throws Exception {
        changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */);
        changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
                new String[0] /* sharedPackages */);

        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
        verify(mMockActivityManagerInternal).broadcastIntentWithCallback(captor.capture(), eq(null),
@@ -150,9 +154,45 @@ public class BroadcastHelperTest {
        assertThat(intent.getPackage()).isEqualTo(PACKAGE_CHANGED_TEST_PACKAGE_NAME);
    }

    @RequiresFlagsEnabled(FLAG_REDUCE_BROADCASTS_FOR_COMPONENT_STATE_CHANGES)
    @Test
    public void changeNonExportedComponent_sendPackageChangedBroadcastToSharedUserIdApplications()
            throws Exception {
        changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
                new String[]{"shared.package"} /* sharedPackages */);

        ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
        ArgumentCaptor<String[]> captorRequiredPermissions = ArgumentCaptor.forClass(
                String[].class);
        verify(mMockActivityManagerInternal, times(3)).broadcastIntentWithCallback(
                captorIntent.capture(), eq(null), captorRequiredPermissions.capture(), anyInt(),
                eq(null), eq(null), eq(null));
        List<Intent> intents = captorIntent.getAllValues();
        List<String[]> requiredPermissions = captorRequiredPermissions.getAllValues();
        assertNotNull(intents);
        assertThat(intents.size()).isEqualTo(3);

        final Intent intent1 = intents.get(0);
        final String[] requiredPermission1 = requiredPermissions.get(0);
        assertThat(intent1.getPackage()).isEqualTo("android");
        assertThat(requiredPermission1).isEqualTo(
                new String[]{PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});

        final Intent intent2 = intents.get(1);
        final String[] requiredPermission2 = requiredPermissions.get(1);
        assertThat(intent2.getPackage()).isEqualTo(PACKAGE_CHANGED_TEST_PACKAGE_NAME);
        assertThat(requiredPermission2).isNull();

        final Intent intent3 = intents.get(2);
        final String[] requiredPermission3 = requiredPermissions.get(2);
        assertThat(intent3.getPackage()).isEqualTo("shared.package");
        assertThat(requiredPermission3).isNull();
    }

    @Test
    public void changeExportedComponent_sendPackageChangedBroadcastToAll() throws Exception {
        changeComponentAndSendPackageChangedBroadcast(true /* changeExportedComponent */);
        changeComponentAndSendPackageChangedBroadcast(true /* changeExportedComponent */,
                new String[0] /* sharedPackages */);

        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
        verify(mMockActivityManagerInternal).broadcastIntentWithCallback(captor.capture(), eq(null),
@@ -162,11 +202,14 @@ public class BroadcastHelperTest {
        assertNull(intent.getPackage());
    }

    private void changeComponentAndSendPackageChangedBroadcast(boolean changeExportedComponent) {
    private void changeComponentAndSendPackageChangedBroadcast(boolean changeExportedComponent,
            String[] sharedPackages) {
        when(mMockSnapshot.getPackageStateInternal(eq(PACKAGE_CHANGED_TEST_PACKAGE_NAME),
                anyInt())).thenReturn(mMockPackageStateInternal);
        when(mMockSnapshot.isInstantAppInternal(any(), anyInt(), anyInt())).thenReturn(false);
        when(mMockSnapshot.getVisibilityAllowLists(any(), any())).thenReturn(null);
        when(mMockSnapshot.getSharedUserPackagesForPackage(eq(PACKAGE_CHANGED_TEST_PACKAGE_NAME),
                anyInt())).thenReturn(sharedPackages);
        when(mMockPackageStateInternal.getPkg()).thenReturn(mMockAndroidPackageInternal);

        when(mMockParsedActivity.getClassName()).thenReturn(