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

Commit 2c61aede authored by Alex Buynytskyy's avatar Alex Buynytskyy Committed by Automerger Merge Worker
Browse files

Merge "Improve performance of file copy in PackageInstaller." into udc-dev am: 168cbb27

parents 6449de95 168cbb27
Loading
Loading
Loading
Loading
+34 −0
Original line number Original line Diff line number Diff line
@@ -2072,6 +2072,25 @@ public class PackageInstaller {
        return new InstallInfo(result);
        return new InstallInfo(result);
    }
    }


    /**
     * Parse a single APK file passed as an FD to get install relevant information about
     * the package wrapped in {@link InstallInfo}.
     * @throws PackageParsingException if the package source file(s) provided is(are) not valid,
     * or the parser isn't able to parse the supplied source(s).
     * @hide
     */
    @NonNull
    public InstallInfo readInstallInfo(@NonNull ParcelFileDescriptor pfd,
            @Nullable String debugPathName, int flags) throws PackageParsingException {
        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
        final ParseResult<PackageLite> result = ApkLiteParseUtils.parseMonolithicPackageLite(input,
                pfd.getFileDescriptor(), debugPathName, flags);
        if (result.isError()) {
            throw new PackageParsingException(result.getErrorCode(), result.getErrorMessage());
        }
        return new InstallInfo(result);
    }

    // (b/239722738) This class serves as a bridge between the PackageLite class, which
    // (b/239722738) This class serves as a bridge between the PackageLite class, which
    // is a hidden class, and the consumers of this class. (e.g. InstallInstalling.java)
    // is a hidden class, and the consumers of this class. (e.g. InstallInstalling.java)
    // This is a part of an effort to remove dependency on hidden APIs and use SystemAPIs or
    // This is a part of an effort to remove dependency on hidden APIs and use SystemAPIs or
@@ -2125,6 +2144,21 @@ public class PackageInstaller {
        public long calculateInstalledSize(@NonNull SessionParams params) throws IOException {
        public long calculateInstalledSize(@NonNull SessionParams params) throws IOException {
            return InstallLocationUtils.calculateInstalledSize(mPkg, params.abiOverride);
            return InstallLocationUtils.calculateInstalledSize(mPkg, params.abiOverride);
        }
        }

        /**
         * @param params {@link SessionParams} of the installation
         * @param pfd of an APK opened for read
         * @return Total disk space occupied by an application after installation.
         * Includes the size of the raw APKs, possibly unpacked resources, raw dex metadata files,
         * and all relevant native code.
         * @throws IOException when size of native binaries cannot be calculated.
         * @hide
         */
        public long calculateInstalledSize(@NonNull SessionParams params,
                @NonNull ParcelFileDescriptor pfd) throws IOException {
            return InstallLocationUtils.calculateInstalledSize(mPkg, params.abiOverride,
                    pfd.getFileDescriptor());
        }
    }
    }


    /**
    /**
+28 −1
Original line number Original line Diff line number Diff line
@@ -109,7 +109,7 @@ public class ApkLiteParseUtils {
    }
    }


    /**
    /**
     * Parse lightweight details about a single APK files.
     * Parse lightweight details about a single APK file.
     */
     */
    public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
    public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
            File packageFile, int flags) {
            File packageFile, int flags) {
@@ -134,6 +134,33 @@ public class ApkLiteParseUtils {
        }
        }
    }
    }


    /**
     * Parse lightweight details about a single APK file passed as an FD.
     */
    public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
            FileDescriptor packageFd, String debugPathName, int flags) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
        try {
            final ParseResult<ApkLite> result = parseApkLite(input, packageFd, debugPathName,
                    flags);
            if (result.isError()) {
                return input.error(result);
            }

            final ApkLite baseApk = result.getResult();
            final String packagePath = debugPathName;
            return input.success(
                    new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */,
                            null /* isFeatureSplits */, null /* usesSplitNames */,
                            null /* configForSplit */, null /* splitApkPaths */,
                            null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(),
                            null /* requiredSplitTypes */, null, /* splitTypes */
                            baseApk.isAllowUpdateOwnership()));
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

    /**
    /**
     * Parse lightweight details about a directory of APKs.
     * Parse lightweight details about a directory of APKs.
     *
     *
+9 −4
Original line number Original line Diff line number Diff line
@@ -16,14 +16,14 @@


package com.android.packageinstaller;
package com.android.packageinstaller;


import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;

import android.app.Activity;
import android.app.Activity;
import android.content.Intent;
import android.content.Intent;
import android.os.Bundle;
import android.os.Bundle;


import androidx.annotation.Nullable;
import androidx.annotation.Nullable;


import java.io.File;

/**
/**
 * Trampoline activity. Calls PackageInstallerActivity and deletes staged install file onResult.
 * Trampoline activity. Calls PackageInstallerActivity and deletes staged install file onResult.
 */
 */
@@ -52,8 +52,13 @@ public class DeleteStagedFileOnResult extends Activity {
        super.onDestroy();
        super.onDestroy();


        if (isFinishing()) {
        if (isFinishing()) {
            File sourceFile = new File(getIntent().getData().getPath());
            // While we expect PIA/InstallStaging to abandon/commit the session, still there
            new Thread(sourceFile::delete).start();
            // might be cases when the session becomes orphan.
            int sessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);
            try {
                getPackageManager().getPackageInstaller().abandonSession(sessionId);
            } catch (SecurityException ignored) {
            }
        }
        }
    }
    }
}
}
+14 −93
Original line number Original line Diff line number Diff line
@@ -16,17 +16,17 @@


package com.android.packageinstaller;
package com.android.packageinstaller;


import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;

import android.app.PendingIntent;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.InstallInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Process;
import android.util.Log;
import android.util.Log;
import android.view.View;
import android.view.View;
import android.widget.Button;
import android.widget.Button;
@@ -34,10 +34,7 @@ import android.widget.Button;
import androidx.annotation.Nullable;
import androidx.annotation.Nullable;


import java.io.File;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


/**
/**
 * Send package to the package manager and handle results from package manager. Once the
 * Send package to the package manager and handle results from package manager. Once the
@@ -77,7 +74,7 @@ public class InstallInstalling extends AlertActivity {
                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        mPackageURI = getIntent().getData();
        mPackageURI = getIntent().getData();


        if ("package".equals(mPackageURI.getScheme())) {
        if (PackageInstallerActivity.SCHEME_PACKAGE.equals(mPackageURI.getScheme())) {
            try {
            try {
                getPackageManager().installExistingPackage(appInfo.packageName);
                getPackageManager().installExistingPackage(appInfo.packageName);
                launchSuccess();
                launchSuccess();
@@ -86,6 +83,8 @@ public class InstallInstalling extends AlertActivity {
                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
            }
        } else {
        } else {
            // ContentResolver.SCHEME_FILE
            // STAGED_SESSION_ID extra contains an ID of a previously staged install session.
            final File sourceFile = new File(mPackageURI.getPath());
            final File sourceFile = new File(mPackageURI.getPath());
            PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
            PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);


@@ -122,41 +121,6 @@ public class InstallInstalling extends AlertActivity {
                    // Does not happen
                    // Does not happen
                }
                }
            } else {
            } else {
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                final Uri referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
                params.setPackageSource(
                        referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
                                : PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE);
                params.setInstallAsInstantApp(false);
                params.setReferrerUri(referrerUri);
                params.setOriginatingUri(getIntent()
                        .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
                params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                        Process.INVALID_UID));
                params.setInstallerPackageName(getIntent().getStringExtra(
                        Intent.EXTRA_INSTALLER_PACKAGE_NAME));
                params.setInstallReason(PackageManager.INSTALL_REASON_USER);

                File file = new File(mPackageURI.getPath());
                try {
                    final InstallInfo result = getPackageManager().getPackageInstaller()
                            .readInstallInfo(file, 0);
                    params.setAppPackageName(result.getPackageName());
                    params.setInstallLocation(result.getInstallLocation());
                    try {
                        params.setSize(result.calculateInstalledSize(params));
                    } catch (IOException e) {
                        e.printStackTrace();
                        params.setSize(file.length());
                    }
                } catch (PackageInstaller.PackageParsingException e) {

                    Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.", e);
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                }
                try {
                try {
                    mInstallId = InstallEventReceiver
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
@@ -166,9 +130,14 @@ public class InstallInstalling extends AlertActivity {
                            PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                            PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
                }


                try {
                mSessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                // Try to open session previously staged in InstallStaging.
                } catch (IOException e) {
                try (PackageInstaller.Session ignored =
                             getPackageManager().getPackageInstaller().openSession(
                        mSessionId)) {
                    Log.d(LOG_TAG, "Staged session is valid, proceeding with the install");
                } catch (IOException | SecurityException e) {
                    Log.e(LOG_TAG, "Invalid session id passed", e);
                    launchFailure(PackageInstaller.STATUS_FAILURE,
                    launchFailure(PackageInstaller.STATUS_FAILURE,
                            PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                            PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
                }
@@ -293,57 +262,9 @@ public class InstallInstalling extends AlertActivity {


        @Override
        @Override
        protected PackageInstaller.Session doInBackground(Void... params) {
        protected PackageInstaller.Session doInBackground(Void... params) {
            PackageInstaller.Session session;
            try {
            try {
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
                return getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
            } catch (IOException e) {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
                return null;
            }

            session.setStagingProgress(0);

            try {
                File file = new File(mPackageURI.getPath());

                try (InputStream in = new FileInputStream(file)) {
                    long sizeBytes = file.length();
                    long totalRead = 0;
                    try (OutputStream out = session
                            .openWrite("PackageInstaller", 0, sizeBytes)) {
                        byte[] buffer = new byte[1024 * 1024];
                        while (true) {
                            int numRead = in.read(buffer);

                            if (numRead == -1) {
                                session.fsync(out);
                                break;
                            }

                            if (isCancelled()) {
                                session.close();
                                break;
                            }

                            out.write(buffer, 0, numRead);
                            if (sizeBytes > 0) {
                                totalRead += numRead;
                                float fraction = ((float) totalRead / (float) sizeBytes);
                                session.setStagingProgress(fraction);
                            }
                        }
                    }
                }

                return session;
            } catch (IOException | SecurityException e) {
                Log.e(LOG_TAG, "Could not write package", e);

                session.close();

                return null;
                return null;
            } finally {
            } finally {
                synchronized (this) {
                synchronized (this) {
+193 −51

File changed.

Preview size limit exceeded, changes collapsed.

Loading