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();