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

Commit 6bc43529 authored by Jakob Schneider's avatar Jakob Schneider Committed by Android (Google) Code Review
Browse files

Merge "Add support for fetching the icon of an archived app." into main

parents 13b69bdc 736daa47
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -3355,8 +3355,12 @@ public class ApplicationPackageManager extends PackageManager {
        }
        Drawable dr = null;
        if (itemInfo.packageName != null) {
            if (itemInfo.isArchived) {
                dr = getArchivedAppIcon(itemInfo.packageName);
            } else {
                dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
            }
        }
        if (dr == null && itemInfo != appInfo && appInfo != null) {
            dr = loadUnbadgedItemIcon(appInfo, appInfo);
        }
@@ -3964,4 +3968,14 @@ public class ApplicationPackageManager extends PackageManager {
            throw e.rethrowFromSystemServer();
        }
    }

    @Nullable
    private Drawable getArchivedAppIcon(String packageName) {
        try {
            return new BitmapDrawable(null,
                    mPM.getArchivedAppIcon(packageName, new UserHandle(getUserId())));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ package android.content.pm;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ArchivedPackageParcel;
@@ -59,7 +60,7 @@ import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.content.IntentSender;
import android.os.UserHandle;

import java.util.Map;

@@ -833,4 +834,6 @@ interface IPackageManager {
    void unregisterPackageMonitorCallback(IRemoteCallback callback);

    ArchivedPackageParcel getArchivedPackage(in String packageName, int userId);

    Bitmap getArchivedAppIcon(String packageName, in UserHandle user);
}
+40 −0
Original line number Diff line number Diff line
@@ -307,6 +307,46 @@ public class PackageArchiver {
        mPm.mHandler.post(() -> unarchiveInternal(packageName, userHandle, installerPackage));
    }

    /**
     * Returns the icon of an archived app. This is the icon of the main activity of the app.
     *
     * <p> The icon is returned without any treatment/overlay. In the rare case the app had multiple
     * launcher activities, only one of the icons is returned arbitrarily.
     */
    public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user) {
        Objects.requireNonNull(packageName);
        Objects.requireNonNull(user);

        Computer snapshot = mPm.snapshotComputer();
        int callingUid = Binder.getCallingUid();
        int userId = user.getIdentifier();
        PackageStateInternal ps;
        try {
            ps = getPackageState(packageName, snapshot, callingUid, userId);
            snapshot.enforceCrossUserPermission(callingUid, userId, true, false,
                    "getArchivedAppIcon");
            verifyArchived(ps, userId);
        } catch (PackageManager.NameNotFoundException e) {
            throw new ParcelableException(e);
        }

        List<ArchiveActivityInfo> activityInfos = ps.getUserStateOrDefault(
                userId).getArchiveState().getActivityInfos();
        if (activityInfos.size() == 0) {
            return null;
        }

        // TODO(b/298452477) Handle monochrome icons.
        // In the rare case the archived app defined more than two launcher activities, we choose
        // the first one arbitrarily.
        return decodeIcon(activityInfos.get(0));
    }

    @VisibleForTesting
    Bitmap decodeIcon(ArchiveActivityInfo archiveActivityInfo) {
        return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString());
    }

    private void verifyArchived(PackageStateInternal ps, int userId)
            throws PackageManager.NameNotFoundException {
        PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
+5 −0
Original line number Diff line number Diff line
@@ -6389,6 +6389,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService
            return getArchivedPackageInternal(packageName, userId);
        }

        @Override
        public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user) {
            return mInstallerService.mPackageArchiver.getArchivedAppIcon(packageName, user);
        }

        /**
         * Wait for the handler to finish handling all pending messages.
         * @param timeoutMillis Maximum time in milliseconds to wait.
+34 −1
Original line number Diff line number Diff line
@@ -159,11 +159,12 @@ public class PackageArchiverTest {
        when(mContext.getPackageManager()).thenReturn(mPackageManager);
        when(mPackageManager.getResourcesForApplication(eq(PACKAGE))).thenReturn(
                mock(Resources.class));
        when(mIcon.compress(eq(Bitmap.CompressFormat.PNG), eq(100), any())).thenReturn(true);

        mArchiveManager = spy(new PackageArchiver(mContext, pm));
        doReturn(ICON_PATH).when(mArchiveManager).storeIcon(eq(PACKAGE),
                any(LauncherActivityInfo.class), eq(mUserId), anyInt());
        doReturn(mIcon).when(mArchiveManager).decodeIcon(
                any(ArchiveState.ArchiveActivityInfo.class));
    }

    @Test
@@ -374,6 +375,38 @@ public class PackageArchiverTest {
        assertThat(intent.getPackage()).isEqualTo(INSTALLER_PACKAGE);
    }

    @Test
    public void getArchivedAppIcon_packageNotInstalled() {
        when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn(
                null);

        Exception e = assertThrows(
                ParcelableException.class,
                () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT));
        assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
        assertThat(e.getCause()).hasMessageThat().isEqualTo(
                String.format("Package %s not found.", PACKAGE));
    }

    @Test
    public void getArchivedAppIcon_notArchived() {
        Exception e = assertThrows(
                ParcelableException.class,
                () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT));
        assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
        assertThat(e.getCause()).hasMessageThat().isEqualTo(
                String.format("Package %s is not currently archived.", PACKAGE));
    }

    @Test
    public void getArchivedAppIcon_success() {
        mUserState.setArchiveState(createArchiveState()).setInstalled(false);

        assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isEqualTo(
                mIcon);
    }


    private static ArchiveState createArchiveState() {
        List<ArchiveState.ArchiveActivityInfo> activityInfos = new ArrayList<>();
        for (LauncherActivityInfo mainActivity : createLauncherActivities()) {