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

Commit 1f571c64 authored by Richard Uhler's avatar Richard Uhler Committed by Narayan Kamath
Browse files

Clean up how APK backups are made.

In preparation for supporting backup of APEX files.

Get the path to the apk using PackageInfo rather than through
PackageManagerInternal APIs. This new approach will work for both
APKs and APEX files.

Copy the apk manually rather than using PackageManagerServiceUtils,
which will work for both APKs and APEX files.

Bug: 112431924
Test: atest RollbackTest
Change-Id: Ifa9d42da9f0bf048f62dae891d59c3ac54fae438
parent b39aaa49
Loading
Loading
Loading
Loading
+20 −20
Original line number Diff line number Diff line
@@ -47,7 +47,6 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.pm.Installer;
import com.android.server.pm.PackageManagerServiceUtils;

import java.io.File;
import java.io.IOException;
@@ -329,15 +328,18 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
                int sessionId = packageInstaller.createSession(params);
                PackageInstaller.Session session = packageInstaller.openSession(sessionId);

                // TODO: Will it always be called "base.apk"? What about splits?
                // What about apex?
                File packageDir = new File(data.backupDir, info.getPackageName());
                File baseApk = new File(packageDir, "base.apk");
                try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
                File packageCode = RollbackStore.getPackageCode(data, info.getPackageName());
                if (packageCode == null) {
                    sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
                            "Backup copy of package code inaccessible");
                    return;
                }

                try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCode,
                        ParcelFileDescriptor.MODE_READ_ONLY)) {
                    final long token = Binder.clearCallingIdentity();
                    try {
                        session.write("base.apk", 0, baseApk.length(), fd);
                        session.write(packageCode.getName(), 0, packageCode.length(), fd);
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
@@ -735,17 +737,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
        VersionedPackage newVersion = new VersionedPackage(packageName, newPackage.versionCode);

        // Get information about the currently installed package.
        PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
        PackageParser.Package installedPackage = pm.getPackage(packageName);
        if (installedPackage == null) {
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pkgInfo = null;
        try {
            pkgInfo = pm.getPackageInfo(packageName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            // TODO: Support rolling back fresh package installs rather than
            // fail here. Test this case.
            Log.e(TAG, packageName + " is not installed");
            return false;
        }
        VersionedPackage installedVersion = new VersionedPackage(packageName,
                installedPackage.getLongVersionCode());

        VersionedPackage installedVersion = new VersionedPackage(packageName,
                pkgInfo.getLongVersionCode());

        final IntArray pendingBackups = mUserdataHelper.snapshotAppData(packageName,
                installedUsers);
@@ -769,16 +773,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
            return false;
        }

        File packageDir = mRollbackStore.packageCodePathForAvailableRollback(data, packageName);
        packageDir.mkdirs();

        // TODO: Copy by hard link instead to save on cpu and storage space?
        int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, packageDir);
        if (status != PackageManager.INSTALL_SUCCEEDED) {
            Log.e(TAG, "Unable to copy package for rollback for " + packageName);
        try {
            RollbackStore.backupPackageCode(data, packageName, pkgInfo.applicationInfo.sourceDir);
        } catch (IOException e) {
            Log.e(TAG, "Unable to copy package for rollback for " + packageName, e);
            return false;
        }

        return true;
    }

+24 −4
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
@@ -201,11 +202,30 @@ class RollbackStore {
    }

    /**
     * Returns the directory where the code for a package should be stored for
     * given rollback <code>data</code> and <code>packageName</code>.
     * Creates a backup copy of the apk or apex for a package.
     */
    File packageCodePathForAvailableRollback(RollbackData data, String packageName) {
        return new File(data.backupDir, packageName);
    static void backupPackageCode(RollbackData data, String packageName, String codePath)
            throws IOException {
        File sourceFile = new File(codePath);
        File targetDir = new File(data.backupDir, packageName);
        targetDir.mkdirs();
        File targetFile = new File(targetDir, sourceFile.getName());

        // TODO: Copy by hard link instead to save on cpu and storage space?
        Files.copy(sourceFile.toPath(), targetFile.toPath());
    }

    /**
     * Returns the apk or apex file backed up for the given package.
     * Returns null if none found.
     */
    static File getPackageCode(RollbackData data, String packageName) {
        File targetDir = new File(data.backupDir, packageName);
        File[] files = targetDir.listFiles();
        if (files == null || files.length != 1) {
            return null;
        }
        return files[0];
    }

    /**