Force restrict all archived packages' icons' size.

To make sure they will fit into parcel.

Bug: 297916136
Test: adb shell device_config put package_manager_service android.content.pm.archiving true && atest PackageManagerTest
Test: adb shell device_config put package_manager_service android.content.pm.archiving false && atest PackageManagerTest
Test: atest PackageArchiverTest
Change-Id: Iea96fe012631e5f331f9e04f3b560ff7d6db3e40
diff --git a/core/java/android/content/pm/ArchivedActivity.java b/core/java/android/content/pm/ArchivedActivity.java
index 5139e2d..9e49c9e 100644
--- a/core/java/android/content/pm/ArchivedActivity.java
+++ b/core/java/android/content/pm/ArchivedActivity.java
@@ -79,14 +79,14 @@
      * @hide
      */
     public static Bitmap drawableToBitmap(Drawable drawable) {
-        return drawableToBitmap(drawable, /* maxIconSize= */ Integer.MAX_VALUE);
+        return drawableToBitmap(drawable, /* iconSize= */ 0);
     }
 
     /**
-     * Same as above, but.
+     * Same as above, but scale the resulting image to fit iconSize.
      * @hide
      */
-    public static Bitmap drawableToBitmap(Drawable drawable, int maxIconSize) {
+    public static Bitmap drawableToBitmap(Drawable drawable, int iconSize) {
         if (drawable instanceof BitmapDrawable) {
             return ((BitmapDrawable) drawable).getBitmap();
 
@@ -106,8 +106,8 @@
         Canvas canvas = new Canvas(bitmap);
         drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
         drawable.draw(canvas);
-        if (bitmap.getWidth() > maxIconSize || bitmap.getHeight() > maxIconSize) {
-            var scaledBitmap = Bitmap.createScaledBitmap(bitmap, maxIconSize, maxIconSize, true);
+        if (iconSize > 0 && bitmap.getWidth() > iconSize * 2 || bitmap.getHeight() > iconSize * 2) {
+            var scaledBitmap = Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, true);
             if (scaledBitmap != bitmap) {
                 bitmap.recycle();
             }
@@ -118,7 +118,6 @@
 
     /**
      * Compress bitmap to PNG format.
-     * The bitmap is going to be recycled.
      * @hide
      */
     public static byte[] bytesFromBitmap(Bitmap bitmap) {
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 4922851..42a97f7 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -29,6 +29,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.content.Context;
@@ -214,10 +215,13 @@
     ArchiveState createArchiveStateInternal(String packageName, int userId,
             List<LauncherActivityInfo> mainActivities, String installerPackage)
             throws IOException {
+        final int iconSize = mContext.getSystemService(
+                ActivityManager.class).getLauncherLargeIconSize();
+
         List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>(mainActivities.size());
         for (int i = 0, size = mainActivities.size(); i < size; i++) {
             LauncherActivityInfo mainActivity = mainActivities.get(i);
-            Path iconPath = storeIcon(packageName, mainActivity, userId, i);
+            Path iconPath = storeIcon(packageName, mainActivity, userId, i, iconSize);
             ArchiveActivityInfo activityInfo =
                     new ArchiveActivityInfo(
                             mainActivity.getLabel().toString(),
@@ -247,7 +251,7 @@
 
     @VisibleForTesting
     Path storeIcon(String packageName, LauncherActivityInfo mainActivity,
-            @UserIdInt int userId, int index) throws IOException {
+            @UserIdInt int userId, int index, int iconSize) throws IOException {
         int iconResourceId = mainActivity.getActivityInfo().getIconResource();
         if (iconResourceId == 0) {
             // The app doesn't define an icon. No need to store anything.
@@ -255,7 +259,7 @@
         }
         File iconsDir = createIconsDir(userId);
         File iconFile = new File(iconsDir, packageName + "-" + index + ".png");
-        Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0));
+        Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0), iconSize);
         try (FileOutputStream out = new FileOutputStream(iconFile)) {
             // Note: Quality is ignored for PNGs.
             if (!icon.compress(Bitmap.CompressFormat.PNG, /* quality= */ 100, out)) {
@@ -590,8 +594,8 @@
     /**
      * Creates serializable archived activities from launcher activities.
      */
-    static ArchivedActivityParcel[] createArchivedActivities(List<LauncherActivityInfo> infos)
-            throws IOException {
+    static ArchivedActivityParcel[] createArchivedActivities(List<LauncherActivityInfo> infos,
+            int iconSize) throws IOException {
         if (infos == null || infos.isEmpty()) {
             throw new IllegalArgumentException("No launcher activities");
         }
@@ -606,7 +610,7 @@
             archivedActivity.title = info.getLabel().toString();
             archivedActivity.originalComponentName = info.getComponentName();
             archivedActivity.iconBitmap = info.getActivityInfo().getIconResource() == 0 ? null :
-                    bytesFromBitmap(drawableToBitmap(info.getIcon(/* density= */ 0)));
+                    bytesFromBitmap(drawableToBitmap(info.getIcon(/* density= */ 0), iconSize));
             // TODO(b/298452477) Handle monochrome icons.
             archivedActivity.monochromeIconBitmap = null;
             activities.add(archivedActivity);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 61b6b24..52655c4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1486,11 +1486,14 @@
                 archPkg.archivedActivities = PackageArchiver.createArchivedActivities(
                         archiveState);
             } else {
+                final int iconSize = mContext.getSystemService(
+                        ActivityManager.class).getLauncherLargeIconSize();
+
                 var mainActivities =
                         mInstallerService.mPackageArchiver.getLauncherActivityInfos(packageName,
                                 userId);
                 archPkg.archivedActivities = PackageArchiver.createArchivedActivities(
-                        mainActivities);
+                        mainActivities, iconSize);
             }
         } catch (Exception e) {
             throw new IllegalArgumentException("Package does not have a main activity", e);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index 7a6ac4e..e7f1d16e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -100,6 +101,8 @@
     @Mock
     private LauncherApps mLauncherApps;
     @Mock
+    private ActivityManager mActivityManager;
+    @Mock
     private PackageManager mPackageManager;
     @Mock
     private PackageInstallerService mInstallerService;
@@ -159,6 +162,10 @@
         when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps);
         when(mLauncherApps.getActivityList(eq(PACKAGE), eq(UserHandle.CURRENT))).thenReturn(
                 mLauncherActivityInfos);
+
+        when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
+        when(mActivityManager.getLauncherLargeIconDensity()).thenReturn(100);
+
         doReturn(mComputer).when(mPackageManagerService).snapshotComputer();
         when(mComputer.getPackageUid(eq(CALLER_PACKAGE), eq(0L), eq(mUserId))).thenReturn(
                 Binder.getCallingUid());
@@ -172,7 +179,7 @@
 
         mArchiveManager = spy(new PackageArchiver(mContext, mPackageManagerService));
         doReturn(ICON_PATH).when(mArchiveManager).storeIcon(eq(PACKAGE),
-                any(LauncherActivityInfo.class), eq(mUserId), anyInt());
+                any(LauncherActivityInfo.class), eq(mUserId), anyInt(), anyInt());
         doReturn(mIcon).when(mArchiveManager).decodeIcon(
                 any(ArchiveState.ArchiveActivityInfo.class));
     }
@@ -277,7 +284,7 @@
     public void archiveApp_storeIconFails() throws IntentSender.SendIntentException, IOException {
         IOException e = new IOException("IO");
         doThrow(e).when(mArchiveManager).storeIcon(eq(PACKAGE),
-                any(LauncherActivityInfo.class), eq(mUserId), anyInt());
+                any(LauncherActivityInfo.class), eq(mUserId), anyInt(), anyInt());
 
         mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
         rule.mocks().getHandler().flush();