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

Commit 94902c46 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "compressed images on system partition" into oc-mr1-dev

parents 5c74928b 7c8addfe
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -251,6 +251,9 @@ public class PackageInfo implements Parcelable {
     */
    public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;

    /** @hide */
    public boolean isStub;

    /** @hide */
    public boolean coreApp;

@@ -324,6 +327,7 @@ public class PackageInfo implements Parcelable {
        dest.writeTypedArray(reqFeatures, parcelableFlags);
        dest.writeTypedArray(featureGroups, parcelableFlags);
        dest.writeInt(installLocation);
        dest.writeInt(isStub ? 1 : 0);
        dest.writeInt(coreApp ? 1 : 0);
        dest.writeInt(requiredForAllUsers ? 1 : 0);
        dest.writeString(restrictedAccountType);
@@ -375,6 +379,7 @@ public class PackageInfo implements Parcelable {
        reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
        featureGroups = source.createTypedArray(FeatureGroupInfo.CREATOR);
        installLocation = source.readInt();
        isStub = source.readInt() != 0;
        coreApp = source.readInt() != 0;
        requiredForAllUsers = source.readInt() != 0;
        restrictedAccountType = source.readString();
+4 −3
Original line number Diff line number Diff line
@@ -665,6 +665,7 @@ public class PackageParser {
        pi.sharedUserLabel = p.mSharedUserLabel;
        pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
        pi.installLocation = p.installLocation;
        pi.isStub = p.isStub;
        pi.coreApp = p.coreApp;
        if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0
                || (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
@@ -5879,10 +5880,10 @@ public class PackageParser {

        public byte[] restrictUpdateHash;

        /**
         * Set if the app or any of its components are visible to Instant Apps.
         */
        /** Set if the app or any of its components are visible to instant applications. */
        public boolean visibleToInstantApps;
        /** Whether or not the package is a stub and must be replaced by the full version. */
        public boolean isStub;

        public Package(String packageName) {
            this.packageName = packageName;
+212 −16
Original line number Diff line number Diff line
@@ -292,6 +292,7 @@ import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
import libcore.io.Streams;
import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
@@ -309,6 +310,8 @@ import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -340,6 +343,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.GZIPInputStream;
/**
 * Keep track of all those APKs everywhere.
@@ -393,6 +397,7 @@ public class PackageManagerService extends IPackageManager.Stub
    private static final boolean DEBUG_FILTERS = false;
    private static final boolean DEBUG_PERMISSIONS = false;
    private static final boolean DEBUG_SHARED_LIBRARIES = false;
    private static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
    // Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService
    // and PackageDexOptimizer. All these classes have their own flag to allow switching a single
@@ -447,6 +452,8 @@ public class PackageManagerService extends IPackageManager.Stub
    static final int FLAGS_REMOVE_CHATTY = 1<<31;
    private static final String STATIC_SHARED_LIB_DELIMITER = "_";
    /** Extension of the compressed packages */
    private final static String COMPRESSED_EXTENSION = ".gz";
    private static final int[] EMPTY_INT_ARRAY = new int[0];
@@ -2634,9 +2641,21 @@ public class PackageManagerService extends IPackageManager.Stub
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
            // Prune any system packages that no longer exist.
            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
            // Stub packages must either be replaced with full versions in the /data
            // partition or be disabled.
            final List<String> stubSystemApps = new ArrayList<>();
            if (!mOnlyCore) {
                Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
                // do this first before mucking with mPackages for the "expecting better" case
                final Iterator<PackageParser.Package> pkgIterator = mPackages.values().iterator();
                while (pkgIterator.hasNext()) {
                    final PackageParser.Package pkg = pkgIterator.next();
                    if (pkg.isStub) {
                        stubSystemApps.add(pkg.packageName);
                    }
                }
                final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
                while (psit.hasNext()) {
                    PackageSetting ps = psit.next();
@@ -2724,36 +2743,37 @@ public class PackageManagerService extends IPackageManager.Stub
                        | PackageParser.PARSE_FORWARD_LOCK,
                        scanFlags | SCAN_REQUIRE_KNOWN, 0);
                /**
                 * Remove disable package settings for any updated system
                 * apps that were removed via an OTA. If they're not a
                 * previously-updated app, remove them completely.
                 * Otherwise, just revoke their system-level permissions.
                 */
                // Remove disable package settings for updated system apps that were
                // removed via an OTA. If the update is no longer present, remove the
                // app completely. Otherwise, revoke their system privileges.
                for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
                    PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
                    mSettings.removeDisabledSystemPackageLPw(deletedAppName);
                    String msg;
                    final String msg;
                    if (deletedPkg == null) {
                        // should have found an update, but, we didn't; remove everything
                        msg = "Updated system package " + deletedAppName
                                + " no longer exists; it's data will be wiped";
                                + " no longer exists; removing its data";
                        // Actual deletion of code and data will be handled by later
                        // reconciliation step
                    } else {
                        msg = "Updated system app + " + deletedAppName
                                + " no longer present; removing system privileges for "
                                + deletedAppName;
                        // found an update; revoke system privileges
                        msg = "Updated system package + " + deletedAppName
                                + " no longer exists; revoking system privileges";
                        deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
                        // Don't do anything if a stub is removed from the system image. If
                        // we were to remove the uncompressed version from the /data partition,
                        // this is where it'd be done.
                        PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
                        final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
                        deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
                        deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
                    }
                    logCriticalInfo(Log.WARN, msg);
                }
                /**
                /*
                 * Make sure all system apps that we expected to appear on
                 * the userdata partition actually showed up. If they never
                 * appeared, crawl back and revive the system version.
@@ -2795,6 +2815,11 @@ public class PackageManagerService extends IPackageManager.Stub
                        }
                    }
                }
                // Uncompress and install any stubbed system applications.
                // This must be done last to ensure all stubs are replaced or disabled.
                decompressSystemApplications(stubSystemApps, scanFlags);
                final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
                final int dataPackagesCount = mPackages.size() - systemPackagesCount;
                Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
@@ -3062,6 +3087,174 @@ public class PackageManagerService extends IPackageManager.Stub
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
    /**
     * Uncompress and install stub applications.
     * <p>In order to save space on the system partition, some applications are shipped in a
     * compressed form. In addition the compressed bits for the full application, the
     * system image contains a tiny stub comprised of only the Android manifest.
     * <p>During the first boot, attempt to uncompress and install the full application. If
     * the application can't be installed for any reason, disable the stub and prevent
     * uncompressing the full application during future boots.
     * <p>In order to forcefully attempt an installation of a full application, go to app
     * settings and enable the application.
     */
    private void decompressSystemApplications(@NonNull List<String> stubSystemApps, int scanFlags) {
        for (int i = stubSystemApps.size() - 1; i >= 0; --i) {
            final String pkgName = stubSystemApps.get(i);
            // skip if the system package is already disabled
            if (mSettings.isDisabledSystemPackageLPr(pkgName)) {
                stubSystemApps.remove(i);
                continue;
            }
            // skip if the package isn't installed (?!); this should never happen
            final PackageParser.Package pkg = mPackages.get(pkgName);
            if (pkg == null) {
                stubSystemApps.remove(i);
                continue;
            }
            // skip if the package has been disabled by the user
            final PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
                if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
                    stubSystemApps.remove(i);
                    continue;
                }
            }
            if (DEBUG_COMPRESSION) {
                Slog.i(TAG, "Uncompressing system stub; pkg: " + pkgName);
            }
            // uncompress the binary to its eventual destination on /data
            final File scanFile = decompressPackage(pkg);
            if (scanFile == null) {
                continue;
            }
            // install the package to replace the stub on /system
            try {
                mSettings.disableSystemPackageLPw(pkgName, true /*replaced*/);
                removePackageLI(pkg, true /*chatty*/);
                scanPackageTracedLI(scanFile, 0 /*reparseFlags*/, scanFlags, 0, null);
                ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
                        UserHandle.USER_SYSTEM, "android");
                stubSystemApps.remove(i);
                continue;
            } catch (PackageManagerException e) {
                Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
            }
            // any failed attempt to install the package will be cleaned up later
        }
        // disable any stub still left; these failed to install the full application
        for (int i = stubSystemApps.size() - 1; i >= 0; --i) {
            final String pkgName = stubSystemApps.get(i);
            final PackageSetting ps = mSettings.mPackages.get(pkgName);
            ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                    UserHandle.USER_SYSTEM, "android");
            logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
        }
    }
    private int decompressFile(File srcFile, File dstFile) throws ErrnoException {
        if (DEBUG_COMPRESSION) {
            Slog.i(TAG, "Decompress file"
                    + "; src: " + srcFile.getAbsolutePath()
                    + ", dst: " + dstFile.getAbsolutePath());
        }
        try (
                InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
                OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
        ) {
            Streams.copy(fileIn, fileOut);
            Os.chmod(dstFile.getAbsolutePath(), 0644);
            return PackageManager.INSTALL_SUCCEEDED;
        } catch (IOException e) {
            logCriticalInfo(Log.ERROR, "Failed to decompress file"
                    + "; src: " + srcFile.getAbsolutePath()
                    + ", dst: " + dstFile.getAbsolutePath());
        }
        return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
    }
    private File[] getCompressedFiles(String codePath) {
        return new File(codePath).listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.toLowerCase().endsWith(COMPRESSED_EXTENSION);
            }
        });
    }
    private boolean compressedFileExists(String codePath) {
        final File[] compressedFiles = getCompressedFiles(codePath);
        return compressedFiles != null && compressedFiles.length > 0;
    }
    /**
     * Decompresses the given package on the system image onto
     * the /data partition.
     * @return The directory the package was decompressed into. Otherwise, {@code null}.
     */
    private File decompressPackage(PackageParser.Package pkg) {
        final File[] compressedFiles = getCompressedFiles(pkg.codePath);
        if (compressedFiles == null || compressedFiles.length == 0) {
            if (DEBUG_COMPRESSION) {
                Slog.i(TAG, "No files to decompress");
            }
            return null;
        }
        final File dstCodePath =
                getNextCodePath(Environment.getDataAppDirectory(null), pkg.packageName);
        int ret = PackageManager.INSTALL_SUCCEEDED;
        try {
            Os.mkdir(dstCodePath.getAbsolutePath(), 0755);
            Os.chmod(dstCodePath.getAbsolutePath(), 0755);
            for (File srcFile : compressedFiles) {
                final String srcFileName = srcFile.getName();
                final String dstFileName = srcFileName.substring(
                        0, srcFileName.length() - COMPRESSED_EXTENSION.length());
                final File dstFile = new File(dstCodePath, dstFileName);
                ret = decompressFile(srcFile, dstFile);
                if (ret != PackageManager.INSTALL_SUCCEEDED) {
                    logCriticalInfo(Log.ERROR, "Failed to decompress"
                            + "; pkg: " + pkg.packageName
                            + ", file: " + dstFileName);
                    break;
                }
            }
        } catch (ErrnoException e) {
            logCriticalInfo(Log.ERROR, "Failed to decompress"
                    + "; pkg: " + pkg.packageName
                    + ", err: " + e.errno);
        }
        if (ret == PackageManager.INSTALL_SUCCEEDED) {
            final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
            NativeLibraryHelper.Handle handle = null;
            try {
                handle = NativeLibraryHelper.Handle.create(dstCodePath);
                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                        null /*abiOverride*/);
            } catch (IOException e) {
                logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
                        + "; pkg: " + pkg.packageName);
                ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
            } finally {
                IoUtils.closeQuietly(handle);
            }
        }
        if (ret != PackageManager.INSTALL_SUCCEEDED) {
            if (dstCodePath == null || !dstCodePath.exists()) {
                return null;
            }
            removeCodePathLI(dstCodePath);
            return null;
        }
        return dstCodePath;
    }
    private void updateInstantAppInstallerLocked(String modifiedPackage) {
        // we're only interested in updating the installer appliction when 1) it's not
        // already set or 2) the modified package is the installer
@@ -10707,6 +10900,9 @@ public class PackageManagerService extends IPackageManager.Stub
                    r.info.encryptionAware = r.info.directBootAware = true;
                }
            }
            if (compressedFileExists(pkg.baseCodePath)) {
                pkg.isStub = true;
            }
        } else {
            // Only allow system apps to be flagged as core apps.
            pkg.coreApp = false;