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

Commit f438b5fc authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Use setIncludedPackages() API to send a single broadcast.

Instead of broadcasting PACKAGE_CHANGED to each target package
individually, send one consolidated package_changed broadcast.

Bug: 433394697
Test: atest services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
Test: atest tests/tests/content/src/android/content/pm/cts/PackageChangedBroadcastTest.java
Flag: android.content.pm.consolidate_package_changed_broadcasts
Change-Id: I4af9c5a4ffaf63c2da56fdd8069541e02bb073e7
parent d4fca214
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -340,3 +340,12 @@ flag {
    is_fixed_read_only: true
}

flag {
    name: "consolidate_package_changed_broadcasts"
    namespace: "package_manager_service"
    description: "Flag guarding the changes to consolidate package_changed broadcasts into a single one, instead of sending them individually"
    bug: "433394697"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
 No newline at end of file
+29 −21
Original line number Diff line number Diff line
@@ -399,41 +399,49 @@ public final class BroadcastHelper {
            // 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.

            final ArrayList<String> targetPackages = new ArrayList<>();
            // First, send the PACKAGE_CHANGED broadcast to the system.
            if (!TextUtils.equals(packageName, "android")) {
                tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
                        "android" /* targetPackageName */, "notExported" /* targetComponent */,
                        notExportedComponentNames.size(), callingPackageNameForTrace);
                sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                        notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
                        broadcastAllowList, "android" /* targetPackageName */,
                        null /* requiredPermissions */, null /* bOptions */);
                targetPackages.add("android");
            }

            // Second, send the PACKAGE_CHANGED broadcast to the application itself.
            tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
                    packageName /* targetPackageName */, "notExported" /* targetComponent */,
                    notExportedComponentNames.size(), callingPackageNameForTrace);
            sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                    notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
                    broadcastAllowList, packageName /* targetPackageName */,
                    null /* requiredPermissions */, null /* bOptions */);
            targetPackages.add(packageName);

            // 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;
                if (!TextUtils.equals(packageName, sharedPackage)) {
                    targetPackages.add(sharedPackage);
                }
                tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace, packageName,
                        sharedPackage /* targetPackageName */, "notExported" /* targetComponent */,
            }

            if (android.content.pm.Flags.consolidatePackageChangedBroadcasts()) {
                final Bundle bOptions = BroadcastOptions.makeBasic()
                        .setIncludedPackages(targetPackages.toArray(
                                new String[targetPackages.size()]))
                        .toBundle();
                tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace,
                        packageName, "<explicit-pkg-list>" /* targetPackageName */,
                        "notExported" /* targetComponent */,
                        notExportedComponentNames.size(), callingPackageNameForTrace);
                sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                        notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
                        broadcastAllowList, sharedPackage /* targetPackageName */,
                        broadcastAllowList, null /* targetPackageName */,
                        null /* requiredPermissions */, bOptions);
            } else {
                for (int i = 0; i < targetPackages.size(); ++i) {
                    final String targetPackageName = targetPackages.get(i);
                    tracePackageChangedBroadcastEvent(true /* applyFlag */, reasonForTrace,
                            packageName, targetPackageName,
                            "notExported" /* targetComponent */,
                            notExportedComponentNames.size(), callingPackageNameForTrace);
                    sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
                            notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
                            broadcastAllowList, targetPackageName,
                            null /* requiredPermissions */, null /* bOptions */);
                }

            }
        }

        if (!exportedComponentNames.isEmpty()) {
+62 −0
Original line number Diff line number Diff line
@@ -32,14 +32,20 @@ import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.BroadcastOptions;
import android.content.Context;
import android.content.Intent;
import android.content.pm.Flags;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.AppModeNonSdkSandbox;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -49,6 +55,7 @@ import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.PackageStateInternal;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -67,6 +74,9 @@ public class BroadcastHelperTest {
    private static final String PACKAGE_CHANGED_TEST_MAIN_ACTIVITY =
            PACKAGE_CHANGED_TEST_PACKAGE_NAME + ".MainActivity";

    @Rule
    public SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    @Mock
    ActivityManagerInternal mMockActivityManagerInternal;
    @Mock
@@ -112,12 +122,34 @@ public class BroadcastHelperTest {
        mBroadcastHelper = new BroadcastHelper(mMockPackageManagerServiceInjector);
    }

    @EnableFlags(Flags.FLAG_CONSOLIDATE_PACKAGE_CHANGED_BROADCASTS)
    @Test
    public void changeNonExportedComponent_sendPackageChangedBroadcastToSystemAndApplicationItself()
            throws Exception {
        changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
                new String[0] /* sharedPackages */);

        ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
        ArgumentCaptor<Bundle> captorOptions = ArgumentCaptor.forClass(Bundle.class);
        verify(mMockActivityManagerInternal, times(1)).broadcastIntentWithCallback(
                captorIntent.capture(), eq(null), eq(null), anyInt(), eq(null), eq(null),
                captorOptions.capture());
        Intent actualIntent = captorIntent.getValue();
        assertThat(actualIntent).isNotNull();
        assertThat(actualIntent.getPackage()).isNull();

        Bundle actualOptions = captorOptions.getValue();
        assertThat(actualOptions).isNotNull();
        verifyIncludedPackages(actualOptions, "android", PACKAGE_CHANGED_TEST_PACKAGE_NAME);
    }

    @DisableFlags(Flags.FLAG_CONSOLIDATE_PACKAGE_CHANGED_BROADCASTS)
    @Test
    public void changeNonExportedComponent_sendPackageChangedBroadcastToSystemAndApplicationItself_flagDisabled()
            throws Exception {
        changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
                new String[0] /* sharedPackages */);

        ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
        verify(mMockActivityManagerInternal, times(2)).broadcastIntentWithCallback(
                captorIntent.capture(), eq(null), eq(null), anyInt(), eq(null), eq(null), eq(null));
@@ -132,12 +164,35 @@ public class BroadcastHelperTest {
        assertThat(intent2.getPackage()).isEqualTo(PACKAGE_CHANGED_TEST_PACKAGE_NAME);
    }

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

        ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
        ArgumentCaptor<Bundle> captorOptions = ArgumentCaptor.forClass(Bundle.class);
        verify(mMockActivityManagerInternal, times(1)).broadcastIntentWithCallback(
                captorIntent.capture(), eq(null), eq(null), anyInt(), eq(null), eq(null),
                captorOptions.capture());
        Intent actualIntent = captorIntent.getValue();
        assertThat(actualIntent).isNotNull();
        assertThat(actualIntent.getPackage()).isNull();

        Bundle actualOptions = captorOptions.getValue();
        assertThat(actualOptions).isNotNull();
        verifyIncludedPackages(actualOptions, "android", PACKAGE_CHANGED_TEST_PACKAGE_NAME,
                "shared.package");
    }

    @DisableFlags(Flags.FLAG_CONSOLIDATE_PACKAGE_CHANGED_BROADCASTS)
    @Test
    public void changeNonExportedComponent_sendPackageChangedBroadcastToSharedUserIdApplications_flagDisabled()
            throws Exception {
        changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
                new String[]{"shared.package"} /* sharedPackages */);

        ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
        verify(mMockActivityManagerInternal, times(3)).broadcastIntentWithCallback(
                captorIntent.capture(), eq(null), eq(null), anyInt(), eq(null), eq(null), eq(null));
@@ -202,4 +257,11 @@ public class BroadcastHelperTest {
                UserHandle.USER_SYSTEM, "test" /* reason */, PackageMetrics.STRING_TEST,
                Binder.getCallingUid());
    }

    private void verifyIncludedPackages(Bundle actualOptions,
            String... expectedIncludedPackages) {
        BroadcastOptions actualBroadcastOptions = new BroadcastOptions(actualOptions);
        assertThat(actualBroadcastOptions.getIncludedPackages())
                .isEqualTo(expectedIncludedPackages);
    }
}