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

Commit e23ae20e authored by Winson's avatar Winson
Browse files

Important migration for new ParsingPackage/ParsedPackage split

Part of the Parsing/ParsedPackage split into core/server.

This splits all the "important" changes, or those which change
significant code/logic and that requires a closer look during
review.

Bug: 135203078

Test: enumerated in first commit of change ID
		Ib4fe51d729a56bfb0ea1316e577358ba0dfceccf

Change-Id: Ie0e4394de2b3063121d850060fcd58622511c59d
parent 01e38f40
Loading
Loading
Loading
Loading
+5 −205
Original line number Diff line number Diff line
@@ -79,14 +79,10 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.system.StructStat;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -118,7 +114,6 @@ import org.xmlpull.v1.XmlPullParserException;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -316,8 +311,8 @@ public class PackageParser {

    public int mParseError = PackageManager.INSTALL_SUCCEEDED;

    public ThreadLocal<ApkParseUtils.ParseResult> mSharedResult
            = ThreadLocal.withInitial(ApkParseUtils.ParseResult::new);
    public ThreadLocal<ParsingPackageUtils.ParseResult> mSharedResult
            = ThreadLocal.withInitial(ParsingPackageUtils.ParseResult::new);

    public static boolean sCompatibilityModeEnabled = true;
    public static boolean sUseRoundIcon = false;
@@ -1054,7 +1049,7 @@ public class PackageParser {
     * and unique split names.
     * <p>
     * Note that this <em>does not</em> perform signature verification; that
     * must be done separately in {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}.
     * must be done separately in {@link #collectCertificates(Package, boolean)}.
     *
     * If {@code useCaches} is true, the package parser might return a cached
     * result from a previous parse of the same {@code packageFile} with the same
@@ -1081,201 +1076,6 @@ public class PackageParser {
        return parsePackage(packageFile, flags, false /* useCaches */);
    }

    /**
     * Updated method which returns {@link ParsedPackage}, the current representation of a
     * package parsed from disk.
     *
     * @see #parsePackage(File, int, boolean)
     */
    @AnyThread
    public ParsedPackage parseParsedPackage(File packageFile, int flags, boolean useCaches)
            throws PackageParserException {
        ParsedPackage parsed = useCaches ? getCachedResult(packageFile, flags) : null;
        if (parsed != null) {
            return parsed;
        }

        long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
        ApkParseUtils.ParseInput parseInput = mSharedResult.get().reset();
        parsed = ApkParseUtils.parsePackage(
                parseInput,
                mSeparateProcesses,
                mCallback,
                mMetrics,
                mOnlyCoreApps,
                packageFile,
                flags
        )
                .hideAsParsed();

        long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
        cacheResult(packageFile, flags, parsed);
        if (LOG_PARSE_TIMINGS) {
            parseTime = cacheTime - parseTime;
            cacheTime = SystemClock.uptimeMillis() - cacheTime;
            if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
                Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
                        + "ms, update_cache=" + cacheTime + " ms");
            }
        }

        return parsed;
    }

    /**
     * Returns the cache key for a specified {@code packageFile} and {@code flags}.
     */
    private String getCacheKey(File packageFile, int flags) {
        StringBuilder sb = new StringBuilder(packageFile.getName());
        sb.append('-');
        sb.append(flags);

        return sb.toString();
    }

    @VisibleForTesting
    protected ParsedPackage fromCacheEntry(byte[] bytes) {
        return fromCacheEntryStatic(bytes);
    }

    /** static version of {@link #fromCacheEntry} for unit tests. */
    @VisibleForTesting
    public static ParsedPackage fromCacheEntryStatic(byte[] bytes) {
        final Parcel p = Parcel.obtain();
        p.unmarshall(bytes, 0, bytes.length);
        p.setDataPosition(0);

        final ReadHelper helper = new ReadHelper(p);
        helper.startAndInstall();

        // TODO(b/135203078): Hide PackageImpl constructor?
        ParsedPackage pkg = new PackageImpl(p);

        p.recycle();

        sCachedPackageReadCount.incrementAndGet();

        return pkg;
    }

    @VisibleForTesting
    protected byte[] toCacheEntry(ParsedPackage pkg) {
        return toCacheEntryStatic(pkg);

    }

    /** static version of {@link #toCacheEntry} for unit tests. */
    @VisibleForTesting
    public static byte[] toCacheEntryStatic(ParsedPackage pkg) {
        final Parcel p = Parcel.obtain();
        final WriteHelper helper = new WriteHelper(p);

        pkg.writeToParcel(p, 0 /* flags */);

        helper.finishAndUninstall();

        byte[] serialized = p.marshall();
        p.recycle();

        return serialized;
    }

    /**
     * Given a {@code packageFile} and a {@code cacheFile} returns whether the
     * cache file is up to date based on the mod-time of both files.
     */
    private static boolean isCacheUpToDate(File packageFile, File cacheFile) {
        try {
            // NOTE: We don't use the File.lastModified API because it has the very
            // non-ideal failure mode of returning 0 with no excepions thrown.
            // The nio2 Files API is a little better but is considerably more expensive.
            final StructStat pkg = android.system.Os.stat(packageFile.getAbsolutePath());
            final StructStat cache = android.system.Os.stat(cacheFile.getAbsolutePath());
            return pkg.st_mtime < cache.st_mtime;
        } catch (ErrnoException ee) {
            // The most common reason why stat fails is that a given cache file doesn't
            // exist. We ignore that here. It's easy to reason that it's safe to say the
            // cache isn't up to date if we see any sort of exception here.
            //
            // (1) Exception while stating the package file : This should never happen,
            // and if it does, we do a full package parse (which is likely to throw the
            // same exception).
            // (2) Exception while stating the cache file : If the file doesn't exist, the
            // cache is obviously out of date. If the file *does* exist, we can't read it.
            // We will attempt to delete and recreate it after parsing the package.
            if (ee.errno != OsConstants.ENOENT) {
                Slog.w("Error while stating package cache : ", ee);
            }

            return false;
        }
    }

    /**
     * Returns the cached parse result for {@code packageFile} for parse flags {@code flags},
     * or {@code null} if no cached result exists.
     */
    public ParsedPackage getCachedResult(File packageFile, int flags) {
        if (mCacheDir == null) {
            return null;
        }

        final String cacheKey = getCacheKey(packageFile, flags);
        final File cacheFile = new File(mCacheDir, cacheKey);

        try {
            // If the cache is not up to date, return null.
            if (!isCacheUpToDate(packageFile, cacheFile)) {
                return null;
            }

            final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
            return fromCacheEntry(bytes);
        } catch (Throwable e) {
            Slog.w(TAG, "Error reading package cache: ", e);

            // If something went wrong while reading the cache entry, delete the cache file
            // so that we regenerate it the next time.
            cacheFile.delete();
            return null;
        }
    }

    /**
     * Caches the parse result for {@code packageFile} with flags {@code flags}.
     */
    public void cacheResult(File packageFile, int flags, ParsedPackage parsed) {
        if (mCacheDir == null) {
            return;
        }

        try {
            final String cacheKey = getCacheKey(packageFile, flags);
            final File cacheFile = new File(mCacheDir, cacheKey);

            if (cacheFile.exists()) {
                if (!cacheFile.delete()) {
                    Slog.e(TAG, "Unable to delete cache file: " + cacheFile);
                }
            }

            final byte[] cacheEntry = toCacheEntry(parsed);

            if (cacheEntry == null) {
                return;
            }

            try (FileOutputStream fos = new FileOutputStream(cacheFile)) {
                fos.write(cacheEntry);
            } catch (IOException ioe) {
                Slog.w(TAG, "Error writing cache entry.", ioe);
                cacheFile.delete();
            }
        } catch (Throwable e) {
            Slog.w(TAG, "Error saving package cache.", e);
        }
    }

    /**
     * Parse all APKs contained in the given directory, treating them as a
     * single package. This also performs sanity checking, such as requiring
@@ -1284,7 +1084,7 @@ public class PackageParser {
     * <p>
     * Note that this <em>does not</em> perform signature verification; that
     * must be done separately in
     * {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}.
     * {@link #collectCertificates(Package, boolean)} .
     */
    private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
        final PackageLite lite = parseClusterPackageLite(packageDir, 0);
@@ -1346,7 +1146,7 @@ public class PackageParser {
     * <p>
     * Note that this <em>does not</em> perform signature verification; that
     * must be done separately in
     * {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}.
     * {@link #collectCertificates(Package, boolean)}.
     */
    @UnsupportedAppUsage
    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
+2 −2
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ import java.util.List;
/** @hide */
public class ApkLiteParseUtils {

    private static final String TAG = ApkParseUtils.TAG;
    private static final String TAG = ParsingPackageUtils.TAG;

    // TODO(b/135203078): Consolidate constants
    private static final int DEFAULT_MIN_SDK_VERSION = 1;
@@ -235,7 +235,7 @@ public class ApkLiteParseUtils {
                final boolean skipVerify = (flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
                try {
                    signingDetails = ApkParseUtils.collectCertificates(apkFile.getAbsolutePath(),
                    signingDetails = ParsingPackageUtils.collectCertificates(apkFile.getAbsolutePath(),
                            skipVerify, false, PackageParser.SigningDetails.UNKNOWN,
                            DEFAULT_TARGET_SDK_VERSION);
                } finally {
+24 −27
Original line number Diff line number Diff line
@@ -82,7 +82,7 @@ import java.util.Objects;
 */
public class ComponentParseUtils {

    private static final String TAG = ApkParseUtils.TAG;
    private static final String TAG = ParsingUtils.TAG;

    // TODO(b/135203078): None of this class's subclasses do anything. Remove in favor of base?
    public static class ParsedIntentInfo extends IntentFilter {
@@ -315,7 +315,7 @@ public class ComponentParseUtils {
        // TODO(b/135203078): Make subclass that contains these fields only for the necessary
        //  subtypes
        protected boolean enabled = true;
        protected boolean directBootAware;
        public boolean directBootAware;
        public int flags;

        private String packageName;
@@ -1460,7 +1460,7 @@ public class ComponentParseUtils {
                outError[0] = tag + " does not specify android:name";
                return null;
            } else {
                String className = ApkParseUtils.buildClassName(packageName, name);
                String className = ParsingUtils.buildClassName(packageName, name);
                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
                    outError[0] = tag + " invalid android:name";
                    return null;
@@ -1537,7 +1537,7 @@ public class ComponentParseUtils {
                    R.styleable.AndroidManifestActivity_parentActivityName,
                    Configuration.NATIVE_CONFIG_VERSION);
            if (parentName != null) {
                String parentClassName = ApkParseUtils.buildClassName(packageName, parentName);
                String parentClassName = ParsingUtils.buildClassName(packageName, parentName);
                if (parentClassName == null) {
                    Log.e(TAG,
                            "Activity " + result.className
@@ -1856,9 +1856,8 @@ public class ComponentParseUtils {
                    parsingPackage.addPreferredActivityFilter(intentInfo);
                }
            } else if (parser.getName().equals("meta-data")) {
                if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
                        result.metaData,
                        outError)) == null) {
                if ((result.metaData = ParsingPackageUtils.parseMetaData(parsingPackage, res,
                        parser, result.metaData, outError)) == null) {
                    return null;
                }
            } else if (!receiver && parser.getName().equals("layout")) {
@@ -1969,7 +1968,7 @@ public class ComponentParseUtils {
                outError[0] = "<service> does not specify android:name";
                return null;
            } else {
                String className = ApkParseUtils.buildClassName(packageName, name);
                String className = ParsingUtils.buildClassName(packageName, name);
                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
                    outError[0] = "<service> invalid android:name";
                    return null;
@@ -2099,7 +2098,7 @@ public class ComponentParseUtils {
            }
        }

        if (parsingPackage.cantSaveState()) {
        if (parsingPackage.isCantSaveState()) {
            // A heavy-weight application can not have services in its main process
            // We can do direct compare because we intern all strings.
            if (Objects.equals(result.getProcessName(), parsingPackage.getPackageName())) {
@@ -2132,9 +2131,8 @@ public class ComponentParseUtils {
                result.order = Math.max(intent.getOrder(), result.order);
                result.intents.add(intent);
            } else if (parser.getName().equals("meta-data")) {
                if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
                        result.metaData,
                        outError)) == null) {
                if ((result.metaData = ParsingPackageUtils.parseMetaData(parsingPackage, res,
                        parser, result.metaData, outError)) == null) {
                    return null;
                }
            } else {
@@ -2182,7 +2180,7 @@ public class ComponentParseUtils {
                outError[0] = "<provider> does not specify android:name";
                return null;
            } else {
                String className = ApkParseUtils.buildClassName(packageName, name);
                String className = ParsingUtils.buildClassName(packageName, name);
                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
                    outError[0] = "<provider> invalid android:name";
                    return null;
@@ -2413,7 +2411,7 @@ public class ComponentParseUtils {
                outInfo.intents.add(intent);

            } else if (parser.getName().equals("meta-data")) {
                Bundle metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
                Bundle metaData = ParsingPackageUtils.parseMetaData(parsingPackage, res, parser,
                        outInfo.metaData, outError);
                if (metaData == null) {
                    return false;
@@ -2607,7 +2605,7 @@ public class ComponentParseUtils {
        }

        String packageName = parsingPackage.getPackageName();
        targetActivity = ApkParseUtils.buildClassName(packageName, targetActivity);
        targetActivity = ParsingUtils.buildClassName(packageName, targetActivity);
        if (targetActivity == null) {
            outError[0] = "Empty class name in package " + packageName;
            sa.recycle();
@@ -2664,7 +2662,7 @@ public class ComponentParseUtils {
        result.requestedVrComponent = target.requestedVrComponent;
        result.directBootAware = target.directBootAware;

        result.setProcessName(parsingPackage.getAppInfoProcessName(), target.getProcessName());
        result.setProcessName(parsingPackage.getProcessName(), target.getProcessName());

        // Not all attributes from the target ParsedActivity are copied to the alias.
        // Careful when adding an attribute and determine whether or not it should be copied.
@@ -2686,7 +2684,7 @@ public class ComponentParseUtils {
            outError[0] = "<activity-alias> does not specify android:name";
            return null;
        } else {
            String className = ApkParseUtils.buildClassName(packageName, name);
            String className = ParsingUtils.buildClassName(packageName, name);
            if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
                outError[0] = "<activity-alias> invalid android:name";
                return null;
@@ -2751,7 +2749,7 @@ public class ComponentParseUtils {
                R.styleable.AndroidManifestActivityAlias_parentActivityName,
                Configuration.NATIVE_CONFIG_VERSION);
        if (parentName != null) {
            String parentClassName = ApkParseUtils.buildClassName(result.getPackageName(),
            String parentClassName = ParsingUtils.buildClassName(result.getPackageName(),
                    parentName);
            if (parentClassName == null) {
                Log.e(TAG, "Activity alias " + result.className +
@@ -2811,9 +2809,8 @@ public class ComponentParseUtils {
                    result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
                }
            } else if (tagName.equals("meta-data")) {
                if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
                        result.metaData,
                        outError)) == null) {
                if ((result.metaData = ParsingPackageUtils.parseMetaData(parsingPackage, res,
                        parser, result.metaData, outError)) == null) {
                    return null;
                }
            } else {
@@ -2935,7 +2932,7 @@ public class ComponentParseUtils {
                outError[0] = "<permission> does not specify android:name";
                return null;
            } else {
                String className = ApkParseUtils.buildClassName(packageName, name);
                String className = ParsingUtils.buildClassName(packageName, name);
                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
                    outError[0] = "<permission> invalid android:name";
                    return null;
@@ -3073,7 +3070,7 @@ public class ComponentParseUtils {
                outError[0] = "<permission-tree> does not specify android:name";
                return null;
            } else {
                String className = ApkParseUtils.buildClassName(packageName, name);
                String className = ParsingUtils.buildClassName(packageName, name);
                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
                    outError[0] = "<permission-tree> invalid android:name";
                    return null;
@@ -3163,7 +3160,7 @@ public class ComponentParseUtils {
                outError[0] = "<permission> does not specify android:name";
                return null;
            } else {
                String className = ApkParseUtils.buildClassName(packageName, name);
                String className = ParsingUtils.buildClassName(packageName, name);
                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
                    outError[0] = "<permission> invalid android:name";
                    return null;
@@ -3262,7 +3259,7 @@ public class ComponentParseUtils {
                outError[0] = "<instrumentation> does not specify android:name";
                return null;
            } else {
                String className = ApkParseUtils.buildClassName(packageName, name);
                String className = ParsingUtils.buildClassName(packageName, name);
                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
                    outError[0] = "<instrumentation> invalid android:name";
                    return null;
@@ -3764,8 +3761,8 @@ public class ComponentParseUtils {
            }

            if (parser.getName().equals("meta-data")) {
                if ((outInfo.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
                        outInfo.metaData, outError)) == null) {
                if ((outInfo.metaData = ParsingPackageUtils.parseMetaData(parsingPackage, res,
                        parser, outInfo.metaData, outError)) == null) {
                    return false;
                }
            } else {
+691 −0

File changed and moved.

Preview size limit exceeded, changes collapsed.

+2 −2
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@ import com.android.internal.util.Parcelling.BuiltIn.ForBoolean;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArray;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringList;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringMap;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringValueMap;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet;

import java.security.PublicKey;
@@ -159,7 +159,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
    private int overlayPriority;
    private boolean overlayIsStatic;
    @NonNull
    @DataClass.ParcelWith(ForInternedStringMap.class)
    @DataClass.ParcelWith(ForInternedStringValueMap.class)
    private Map<String, String> overlayables = emptyMap();

    @Nullable
Loading