Loading core/java/android/content/pm/flags.aconfig +9 −0 Original line number Diff line number Diff line Loading @@ -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 services/core/java/com/android/server/pm/BroadcastHelper.java +29 −21 Original line number Diff line number Diff line Loading @@ -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()) { Loading services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java +62 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -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)); Loading @@ -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)); Loading Loading @@ -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); } } Loading
core/java/android/content/pm/flags.aconfig +9 −0 Original line number Diff line number Diff line Loading @@ -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
services/core/java/com/android/server/pm/BroadcastHelper.java +29 −21 Original line number Diff line number Diff line Loading @@ -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()) { Loading
services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java +62 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -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)); Loading @@ -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)); Loading Loading @@ -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); } }