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

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

Merge "Reduce duplicate strings due to the package cache." into oc-mr1-dev

parents e2dc2d9e 4501c61d
Loading
Loading
Loading
Loading
+40 −11
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageParserCacheHelper.ReadHelper;
import android.content.pm.PackageParserCacheHelper.WriteHelper;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
@@ -121,6 +123,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.ZipEntry;

@@ -235,6 +238,11 @@ public class PackageParser {

    private static final boolean LOG_UNSAFE_BROADCASTS = false;

    /**
     * Total number of packages that were read from the cache.  We use it only for logging.
     */
    public static final AtomicInteger sCachedPackageReadCount = new AtomicInteger();

    // Set of broadcast actions that are safe for manifest receivers
    private static final Set<String> SAFE_BROADCASTS = new ArraySet<>();
    static {
@@ -1035,21 +1043,45 @@ public class PackageParser {
    }

    @VisibleForTesting
    protected Package fromCacheEntry(byte[] bytes) throws IOException {
        Parcel p = Parcel.obtain();
    protected Package fromCacheEntry(byte[] bytes) {
        return fromCacheEntryStatic(bytes);
    }

    /** static version of {@link #fromCacheEntry} for unit tests. */
    @VisibleForTesting
    public static Package 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();

        PackageParser.Package pkg = new PackageParser.Package(p);

        p.recycle();

        sCachedPackageReadCount.incrementAndGet();

        return pkg;
    }

    @VisibleForTesting
    protected byte[] toCacheEntry(Package pkg) throws IOException {
        Parcel p = Parcel.obtain();
    protected byte[] toCacheEntry(Package pkg) {
        return toCacheEntryStatic(pkg);

    }

    /** static version of {@link #toCacheEntry} for unit tests. */
    @VisibleForTesting
    public static byte[] toCacheEntryStatic(Package 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();

@@ -1146,13 +1178,7 @@ public class PackageParser {
            }
        }

        final byte[] cacheEntry;
        try {
            cacheEntry = toCacheEntry(parsed);
        } catch (IOException ioe) {
            Slog.e(TAG, "Unable to serialize parsed package for: " + packageFile);
            return;
        }
        final byte[] cacheEntry = toCacheEntry(parsed);

        if (cacheEntry == null) {
            return;
@@ -6421,8 +6447,11 @@ public class PackageParser {

            dest.writeStringList(requestedPermissions);
            dest.writeStringList(protectedBroadcasts);

            // TODO: This doesn't work: b/64295061
            dest.writeParcelable(parentPackage, flags);
            dest.writeParcelableList(childPackages, flags);

            dest.writeString(staticSharedLibName);
            dest.writeInt(staticSharedLibVersion);
            dest.writeStringList(libraryNames);
+157 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.os.Parcel;
import android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * Helper classes to read from and write to Parcel with pooled strings.
 *
 * @hide
 */
public class PackageParserCacheHelper {
    private PackageParserCacheHelper() {
    }

    private static final String TAG = "PackageParserCacheHelper";
    private static final boolean DEBUG = false;

    /**
     * Parcel read helper with a string pool.
     */
    public static class ReadHelper extends Parcel.ReadWriteHelper {
        private final ArrayList<String> mStrings = new ArrayList<>();

        private final Parcel mParcel;

        public ReadHelper(Parcel p) {
            mParcel = p;
        }

        /**
         * Prepare to read from a parcel, and install itself as a read-write helper.
         *
         * (We don't do it in the constructor to avoid calling methods before the constructor
         * finishes.)
         */
        public void startAndInstall() {
            mStrings.clear();

            final int poolPosition = mParcel.readInt();
            final int startPosition = mParcel.dataPosition();

            // The pool is at the end of the parcel.
            mParcel.setDataPosition(poolPosition);
            mParcel.readStringList(mStrings);

            // Then move back.
            mParcel.setDataPosition(startPosition);

            if (DEBUG) {
                Log.i(TAG, "Read " + mStrings.size() + " strings");
                for (int i = 0; i < mStrings.size(); i++) {
                    Log.i(TAG, "  " + i + ": \"" + mStrings.get(i) + "\"");
                }
            }

            mParcel.setReadWriteHelper(this);
        }

        /**
         * Read an string index from a parcel, and returns the corresponding string from the pool.
         */
        @Override
        public String readString(Parcel p) {
            return mStrings.get(p.readInt());
        }
    }

    /**
     * Parcel write helper with a string pool.
     */
    public static class WriteHelper extends Parcel.ReadWriteHelper {
        private final ArrayList<String> mStrings = new ArrayList<>();

        private final HashMap<String, Integer> mIndexes = new HashMap<>();

        private final Parcel mParcel;
        private final int mStartPos;

        /**
         * Constructor.  Prepare a parcel, and install it self as a read-write helper.
         */
        public WriteHelper(Parcel p) {
            mParcel = p;
            mStartPos = p.dataPosition();
            mParcel.writeInt(0); // We come back later here and write the pool position.

            mParcel.setReadWriteHelper(this);
        }

        /**
         * Instead of writing a string directly to a parcel, this method adds it to the pool,
         * and write the index in the pool to the parcel.
         */
        @Override
        public void writeString(Parcel p, String s) {
            final Integer cur = mIndexes.get(s);
            if (cur != null) {
                // String already in the pool. Just write the index.
                p.writeInt(cur); // Already in the pool.
                if (DEBUG) {
                    Log.i(TAG, "Duplicate '" + s + "' at " + cur);
                }
            } else {
                // Not in the pool. Add to the pool, and write the index.
                final int index = mStrings.size();
                mIndexes.put(s, index);
                mStrings.add(s);

                if (DEBUG) {
                    Log.i(TAG, "New '" + s + "' at " + index);
                }

                p.writeInt(index);
            }
        }

        /**
         * Closes a parcel by appending the string pool at the end and updating the pool offset,
         * which it assumes is at the first byte.  It also uninstalls itself as a read-write helper.
         */
        public void finishAndUninstall() {
            // Uninstall first, so that writeStringList() uses the native writeString.
            mParcel.setReadWriteHelper(null);

            final int poolPosition = mParcel.dataPosition();
            mParcel.writeStringList(mStrings);

            mParcel.setDataPosition(mStartPos);
            mParcel.writeInt(poolPosition);

            // Move back to the end.
            mParcel.setDataPosition(mParcel.dataSize());
            if (DEBUG) {
                Log.i(TAG, "Wrote " + mStrings.size() + " strings");
            }
        }
    }
}
+88 −46
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.os;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.Log;
@@ -23,6 +24,7 @@ import android.util.MathUtils;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;

import java.io.Serializable;
@@ -94,7 +96,8 @@ public class BaseBundle {
    private ClassLoader mClassLoader;

    /** {@hide} */
    int mFlags;
    @VisibleForTesting
    public int mFlags;

    /**
     * Constructs a new, empty Bundle that uses a specific ClassLoader for
@@ -218,22 +221,30 @@ public class BaseBundle {
     */
    /* package */ void unparcel() {
        synchronized (this) {
            final Parcel parcelledData = mParcelledData;
            if (parcelledData == null) {
                if (DEBUG) Log.d(TAG, "unparcel "
            final Parcel source = mParcelledData;
            if (source != null) {
                initializeFromParcelLocked(source, /*recycleParcel=*/ true);
            } else {
                if (DEBUG) {
                    Log.d(TAG, "unparcel "
                            + Integer.toHexString(System.identityHashCode(this))
                            + ": no parcelled data");
                return;
                }
            }
        }
    }

    private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel) {
        if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
            Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
                    + "clobber all data inside!", new Throwable());
        }

            if (isEmptyParcel()) {
                if (DEBUG) Log.d(TAG, "unparcel "
        if (isEmptyParcel(parcelledData)) {
            if (DEBUG) {
                Log.d(TAG, "unparcel "
                        + Integer.toHexString(System.identityHashCode(this)) + ": empty");
            }
            if (mMap == null) {
                mMap = new ArrayMap<>(1);
            } else {
@@ -243,21 +254,23 @@ public class BaseBundle {
            return;
        }

            int N = parcelledData.readInt();
            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
                    + ": reading " + N + " maps");
            if (N < 0) {
        final int count = parcelledData.readInt();
        if (DEBUG) {
            Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
                    + ": reading " + count + " maps");
        }
        if (count < 0) {
            return;
        }
        ArrayMap<String, Object> map = mMap;
        if (map == null) {
                map = new ArrayMap<>(N);
            map = new ArrayMap<>(count);
        } else {
            map.erase();
                map.ensureCapacity(N);
            map.ensureCapacity(count);
        }
        try {
                parcelledData.readArrayMapInternal(map, N, mClassLoader);
            parcelledData.readArrayMapInternal(map, count, mClassLoader);
        } catch (BadParcelableException e) {
            if (sShouldDefuse) {
                Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -267,10 +280,13 @@ public class BaseBundle {
            }
        } finally {
            mMap = map;
                parcelledData.recycle();
            if (recycleParcel) {
                recycleParcel(parcelledData);
            }
            mParcelledData = null;
        }
            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
        if (DEBUG) {
            Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
                    + " final map: " + mMap);
        }
    }
@@ -286,7 +302,20 @@ public class BaseBundle {
     * @hide
     */
    public boolean isEmptyParcel() {
        return mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL;
        return isEmptyParcel(mParcelledData);
    }

    /**
     * @hide
     */
    private static boolean isEmptyParcel(Parcel p) {
        return p == NoImagePreloadHolder.EMPTY_PARCEL;
    }

    private static void recycleParcel(Parcel p) {
        if (p != null && !isEmptyParcel(p)) {
            p.recycle();
        }
    }

    /** @hide */
@@ -1476,6 +1505,10 @@ public class BaseBundle {
     * @param parcel The parcel to copy this bundle to.
     */
    void writeToParcelInner(Parcel parcel, int flags) {
        // If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.
        if (parcel.hasReadWriteHelper()) {
            unparcel();
        }
        // Keep implementation in sync with writeToParcel() in
        // frameworks/native/libs/binder/PersistableBundle.cpp.
        final ArrayMap<String, Object> map;
@@ -1544,6 +1577,15 @@ public class BaseBundle {
                    + Integer.toHexString(magic));
        }

        if (parcel.hasReadWriteHelper()) {
            // If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
            // unparcel right away.
            synchronized (this) {
                initializeFromParcelLocked(parcel, /*recycleParcel=*/ false);
            }
            return;
        }

        // Advance within this Parcel
        int offset = parcel.dataPosition();
        parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
+43 −16
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import android.util.Size;
import android.util.SizeF;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@@ -32,9 +34,14 @@ import java.util.List;
 * @see PersistableBundle
 */
public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
    private static final int FLAG_HAS_FDS = 1 << 8;
    private static final int FLAG_HAS_FDS_KNOWN = 1 << 9;
    private static final int FLAG_ALLOW_FDS = 1 << 10;
    @VisibleForTesting
    static final int FLAG_HAS_FDS = 1 << 8;

    @VisibleForTesting
    static final int FLAG_HAS_FDS_KNOWN = 1 << 9;

    @VisibleForTesting
    static final int FLAG_ALLOW_FDS = 1 << 10;

    public static final Bundle EMPTY;

@@ -65,20 +72,42 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
     * will be unparcelled on first contact, using the assigned ClassLoader.
     *
     * @param parcelledData a Parcel containing a Bundle
     *
     * @hide
     */
    Bundle(Parcel parcelledData) {
    @VisibleForTesting
    public Bundle(Parcel parcelledData) {
        super(parcelledData);
        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
        if (mParcelledData.hasFileDescriptors()) {
            mFlags |= FLAG_HAS_FDS;
        }
        mFlags = FLAG_ALLOW_FDS;
        maybePrefillHasFds();
    }

    /* package */ Bundle(Parcel parcelledData, int length) {
    /**
     * Constructor from a parcel for when the length is known *and is not stored in the parcel.*
     * The other constructor that takes a parcel assumes the length is in the parcel.
     *
     * @hide
     */
    @VisibleForTesting
    public Bundle(Parcel parcelledData, int length) {
        super(parcelledData, length);
        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
        mFlags = FLAG_ALLOW_FDS;
        maybePrefillHasFds();
    }

    /**
     * If {@link #mParcelledData} is not null, copy the HAS FDS bit from it because it's fast.
     * Otherwise (if {@link #mParcelledData} is already null), leave {@link #FLAG_HAS_FDS_KNOWN}
     * unset, because scanning a map is slower.  We'll do it lazily in
     * {@link #hasFileDescriptors()}.
     */
    private void maybePrefillHasFds() {
        if (mParcelledData != null) {
            if (mParcelledData.hasFileDescriptors()) {
            mFlags |= FLAG_HAS_FDS;
                mFlags |= FLAG_HAS_FDS | FLAG_HAS_FDS_KNOWN;
            } else {
                mFlags |= FLAG_HAS_FDS_KNOWN;
            }
        }
    }

@@ -1213,10 +1242,8 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
     */
    public void readFromParcel(Parcel parcel) {
        super.readFromParcelInner(parcel);
        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
        if (mParcelledData.hasFileDescriptors()) {
            mFlags |= FLAG_HAS_FDS;
        }
        mFlags = FLAG_ALLOW_FDS;
        maybePrefillHasFds();
    }

    @Override
+73 −2
Original line number Diff line number Diff line
@@ -291,7 +291,7 @@ public final class Parcel {
    private static native void nativeWriteFloat(long nativePtr, float val);
    @FastNative
    private static native void nativeWriteDouble(long nativePtr, double val);
    private static native void nativeWriteString(long nativePtr, String val);
    static native void nativeWriteString(long nativePtr, String val);
    private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
    private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);

@@ -306,7 +306,7 @@ public final class Parcel {
    private static native float nativeReadFloat(long nativePtr);
    @CriticalNative
    private static native double nativeReadDouble(long nativePtr);
    private static native String nativeReadString(long nativePtr);
    static native String nativeReadString(long nativePtr);
    private static native IBinder nativeReadStrongBinder(long nativePtr);
    private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);

@@ -338,6 +338,33 @@ public final class Parcel {
        }
    };

    /**
     * @hide
     */
    public static class ReadWriteHelper {
        public static final ReadWriteHelper DEFAULT = new ReadWriteHelper();

        /**
         * Called when writing a string to a parcel. Subclasses wanting to write a string
         * must use {@link #writeStringNoHelper(String)} to avoid
         * infinity recursive calls.
         */
        public void writeString(Parcel p, String s) {
            nativeWriteString(p.mNativePtr, s);
        }

        /**
         * Called when reading a string to a parcel. Subclasses wanting to read a string
         * must use {@link #readStringNoHelper()} to avoid
         * infinity recursive calls.
         */
        public String readString(Parcel p) {
            return nativeReadString(p.mNativePtr);
        }
    }

    private ReadWriteHelper mReadWriteHelper = ReadWriteHelper.DEFAULT;

    /**
     * Retrieve a new Parcel object from the pool.
     */
@@ -352,6 +379,7 @@ public final class Parcel {
                    if (DEBUG_RECYCLE) {
                        p.mStack = new RuntimeException();
                    }
                    p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
                    return p;
                }
            }
@@ -385,6 +413,25 @@ public final class Parcel {
        }
    }

    /**
     * Set a {@link ReadWriteHelper}, which can be used to avoid having duplicate strings, for
     * example.
     *
     * @hide
     */
    public void setReadWriteHelper(ReadWriteHelper helper) {
        mReadWriteHelper = helper != null ? helper : ReadWriteHelper.DEFAULT;
    }

    /**
     * @return whether this parcel has a {@link ReadWriteHelper}.
     *
     * @hide
     */
    public boolean hasReadWriteHelper() {
        return (mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT);
    }

    /** @hide */
    public static native long getGlobalAllocSize();

@@ -625,6 +672,17 @@ public final class Parcel {
     * growing dataCapacity() if needed.
     */
    public final void writeString(String val) {
        mReadWriteHelper.writeString(this, val);
    }

    /**
     * Write a string without going though a {@link ReadWriteHelper}.  Subclasses of
     * {@link ReadWriteHelper} must use this method instead of {@link #writeString} to avoid
     * infinity recursive calls.
     *
     * @hide
     */
    public void writeStringNoHelper(String val) {
        nativeWriteString(mNativePtr, val);
    }

@@ -1997,6 +2055,17 @@ public final class Parcel {
     * Read a string value from the parcel at the current dataPosition().
     */
    public final String readString() {
        return mReadWriteHelper.readString(this);
    }

    /**
     * Read a string without going though a {@link ReadWriteHelper}.  Subclasses of
     * {@link ReadWriteHelper} must use this method instead of {@link #readString} to avoid
     * infinity recursive calls.
     *
     * @hide
     */
    public String readStringNoHelper() {
        return nativeReadString(mNativePtr);
    }

@@ -2997,6 +3066,7 @@ public final class Parcel {
        if (mOwnsNativeParcelObject) {
            updateNativeSize(nativeFreeBuffer(mNativePtr));
        }
        mReadWriteHelper = ReadWriteHelper.DEFAULT;
    }

    private void destroy() {
@@ -3007,6 +3077,7 @@ public final class Parcel {
            }
            mNativePtr = 0;
        }
        mReadWriteHelper = null;
    }

    @Override
Loading