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

Commit 12142e2e authored by Alex Buynytskyy's avatar Alex Buynytskyy
Browse files

Add archived activity info for archive package install.

Bug: 297916136
Test: atest PackageManagerTest
Change-Id: Ibb66fa241f733fc65c55ea19d759ea46f2af0a83
parent d0e3d74f
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.content.pm;

import android.graphics.Bitmap;

/** @hide */
parcelable ArchivedActivityParcel {
    String title;
    // PNG compressed bitmaps.
    byte[] iconBitmap;
    byte[] monochromeIconBitmap;
}
+2 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.content.pm;

import android.content.pm.ArchivedActivityParcel;
import android.content.pm.SigningDetails;

/**
@@ -29,9 +30,8 @@ parcelable ArchivedPackageParcel {
    int versionCode;
    int versionCodeMajor;
    int targetSdkVersion;
    String backupAllowed;
    String defaultToDeviceProtectedStorage;
    String requestLegacyExternalStorage;
    String userDataFragile;
    String clearUserDataOnFailedRestoreAllowed;
    ArchivedActivityParcel[] archivedActivities;
}
+1 −1
Original line number Diff line number Diff line
@@ -832,5 +832,5 @@ interface IPackageManager {

    void unregisterPackageMonitorCallback(IRemoteCallback callback);

    ArchivedPackageParcel getArchivedPackage(in String apkPath);
    ArchivedPackageParcel getArchivedPackage(in String packageName, int userId);
}
+2 −3
Original line number Diff line number Diff line
@@ -258,15 +258,14 @@ public class ApkLite {
        mHasDeviceAdminReceiver = false;
        mIsSdkLibrary = false;
        // @see ParsingPackageUtils#parseBaseAppBasicFlags
        mBackupAllowed = XmlUtils.convertValueToBoolean(archivedPackage.backupAllowed, true);
        mBackupAllowed = true;
        mDefaultToDeviceProtectedStorage = XmlUtils.convertValueToBoolean(
                archivedPackage.defaultToDeviceProtectedStorage, false);
        mRequestLegacyExternalStorage = XmlUtils.convertValueToBoolean(
                archivedPackage.requestLegacyExternalStorage,
                mTargetSdkVersion < Build.VERSION_CODES.Q);
        mUserDataFragile = XmlUtils.convertValueToBoolean(archivedPackage.userDataFragile, false);
        mClearUserDataOnFailedRestoreAllowed = XmlUtils.convertValueToBoolean(
                archivedPackage.clearUserDataOnFailedRestoreAllowed, true);
        mClearUserDataOnFailedRestoreAllowed = true;
    }

    /**
+100 −11
Original line number Diff line number Diff line
@@ -31,12 +31,14 @@ import android.app.BroadcastOptions;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ArchivedActivityParcel;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -56,6 +58,7 @@ import com.android.server.pm.pkg.ArchiveState.ArchiveActivityInfo;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserStateInternal;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -158,7 +161,8 @@ public class PackageArchiver {
        String responsibleInstallerPackage = getResponsibleInstallerPackage(ps);
        verifyInstaller(responsibleInstallerPackage);

        List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps, userId);
        List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps.getPackageName(),
                userId);
        final CompletableFuture<ArchiveState> archiveState = new CompletableFuture<>();
        mPm.mHandler.post(() -> {
            try {
@@ -172,11 +176,11 @@ public class PackageArchiver {
        return archiveState;
    }

    private ArchiveState createArchiveStateInternal(String packageName, int userId,
    static ArchiveState createArchiveStateInternal(String packageName, int userId,
            List<LauncherActivityInfo> mainActivities, String installerPackage)
            throws IOException {
        List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>();
        for (int i = 0; i < mainActivities.size(); i++) {
        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);
            ArchiveActivityInfo activityInfo = new ArchiveActivityInfo(
@@ -189,7 +193,7 @@ public class PackageArchiver {

    // TODO(b/298452477) Handle monochrome icons.
    @VisibleForTesting
    Path storeIcon(String packageName, LauncherActivityInfo mainActivity,
    static Path storeIcon(String packageName, LauncherActivityInfo mainActivity,
            @UserIdInt int userId)
            throws IOException {
        int iconResourceId = mainActivity.getActivityInfo().getIconResource();
@@ -310,16 +314,16 @@ public class PackageArchiver {
                /* initialExtras= */ null);
    }

    private List<LauncherActivityInfo> getLauncherActivityInfos(PackageStateInternal ps,
    List<LauncherActivityInfo> getLauncherActivityInfos(String packageName,
            int userId) throws PackageManager.NameNotFoundException {
        List<LauncherActivityInfo> mainActivities =
                Binder.withCleanCallingIdentity(() -> getLauncherApps().getActivityList(
                        ps.getPackageName(),
                        packageName,
                        new UserHandle(userId)));
        if (mainActivities.isEmpty()) {
            throw new PackageManager.NameNotFoundException(
                    TextUtils.formatSimple("The app %s does not have a main activity.",
                            ps.getPackageName()));
                            packageName));
        }

        return mainActivities;
@@ -423,7 +427,7 @@ public class PackageArchiver {
        }
    }

    private File createIconsDir(@UserIdInt int userId) throws IOException {
    private static File createIconsDir(@UserIdInt int userId) throws IOException {
        File iconsDir = getIconsDir(userId);
        if (!iconsDir.isDirectory()) {
            iconsDir.delete();
@@ -436,7 +440,7 @@ public class PackageArchiver {
        return iconsDir;
    }

    private File getIconsDir(int userId) {
    private static File getIconsDir(int userId) {
        return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR);
    }

@@ -462,4 +466,89 @@ public class PackageArchiver {
        drawable.draw(canvas);
        return bitmap;
    }

    private static byte[] bytesFromBitmapFile(Path path) throws IOException {
        if (path == null) {
            return null;
        }
        // Technically we could just read the bytes, but we want to be sure we store the
        // right format.
        return bytesFromBitmap(BitmapFactory.decodeFile(path.toString()));
    }

    private static byte[] bytesFromBitmap(Bitmap bitmap) throws IOException {
        if (bitmap == null) {
            return null;
        }

        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(
                bitmap.getByteCount())) {
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
            return baos.toByteArray();
        }
    }

    /**
     * Creates serializable archived activities from existing ArchiveState.
     */
    static ArchivedActivityParcel[] createArchivedActivities(ArchiveState archiveState)
            throws IOException {
        var infos = archiveState.getActivityInfos();
        if (infos == null || infos.isEmpty()) {
            throw new IllegalArgumentException("No activities in archive state");
        }

        List<ArchivedActivityParcel> activities = new ArrayList<>(infos.size());
        for (int i = 0, size = infos.size(); i < size; ++i) {
            var info = infos.get(i);
            if (info == null) {
                continue;
            }
            var archivedActivity = new ArchivedActivityParcel();
            archivedActivity.title = info.getTitle();
            archivedActivity.iconBitmap = bytesFromBitmapFile(info.getIconBitmap());
            archivedActivity.monochromeIconBitmap = bytesFromBitmapFile(
                    info.getMonochromeIconBitmap());
            activities.add(archivedActivity);
        }

        if (activities.isEmpty()) {
            throw new IllegalArgumentException(
                    "Failed to extract title and icon of main activities");
        }

        return activities.toArray(new ArchivedActivityParcel[activities.size()]);
    }

    /**
     * Creates serializable archived activities from launcher activities.
     */
    static ArchivedActivityParcel[] createArchivedActivities(List<LauncherActivityInfo> infos)
            throws IOException {
        if (infos == null || infos.isEmpty()) {
            throw new IllegalArgumentException("No launcher activities");
        }

        List<ArchivedActivityParcel> activities = new ArrayList<>(infos.size());
        for (int i = 0, size = infos.size(); i < size; ++i) {
            var info = infos.get(i);
            if (info == null) {
                continue;
            }
            var archivedActivity = new ArchivedActivityParcel();
            archivedActivity.title = info.getLabel().toString();
            archivedActivity.iconBitmap =
                    info.getActivityInfo().getIconResource() == 0 ? null : bytesFromBitmap(
                            drawableToBitmap(info.getIcon(/* density= */ 0)));
            // TODO(b/298452477) Handle monochrome icons.
            archivedActivity.monochromeIconBitmap = null;
            activities.add(archivedActivity);
        }

        if (activities.isEmpty()) {
            throw new IllegalArgumentException(
                    "Failed to extract title and icon of main activities");
        }

        return activities.toArray(new ArchivedActivityParcel[activities.size()]);
    }}
Loading