Loading core/java/android/content/pm/PackageParser.java +40 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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(); Loading Loading @@ -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; Loading Loading @@ -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); Loading core/java/android/content/pm/PackageParserCacheHelper.java 0 → 100644 +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"); } } } } core/java/android/os/BaseBundle.java +88 −46 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; import android.util.Log; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 { Loading @@ -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); Loading @@ -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); } } Loading @@ -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 */ Loading Loading @@ -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; Loading Loading @@ -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)); Loading core/java/android/os/Bundle.java +43 −16 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; } } } Loading Loading @@ -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 Loading core/java/android/os/Parcel.java +73 −2 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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); Loading Loading @@ -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. */ Loading @@ -352,6 +379,7 @@ public final class Parcel { if (DEBUG_RECYCLE) { p.mStack = new RuntimeException(); } p.mReadWriteHelper = ReadWriteHelper.DEFAULT; return p; } } Loading Loading @@ -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(); Loading Loading @@ -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); } Loading Loading @@ -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); } Loading Loading @@ -2997,6 +3066,7 @@ public final class Parcel { if (mOwnsNativeParcelObject) { updateNativeSize(nativeFreeBuffer(mNativePtr)); } mReadWriteHelper = ReadWriteHelper.DEFAULT; } private void destroy() { Loading @@ -3007,6 +3077,7 @@ public final class Parcel { } mNativePtr = 0; } mReadWriteHelper = null; } @Override Loading Loading
core/java/android/content/pm/PackageParser.java +40 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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(); Loading Loading @@ -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; Loading Loading @@ -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); Loading
core/java/android/content/pm/PackageParserCacheHelper.java 0 → 100644 +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"); } } } }
core/java/android/os/BaseBundle.java +88 −46 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; import android.util.Log; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 { Loading @@ -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); Loading @@ -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); } } Loading @@ -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 */ Loading Loading @@ -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; Loading Loading @@ -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)); Loading
core/java/android/os/Bundle.java +43 −16 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; } } } Loading Loading @@ -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 Loading
core/java/android/os/Parcel.java +73 −2 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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); Loading Loading @@ -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. */ Loading @@ -352,6 +379,7 @@ public final class Parcel { if (DEBUG_RECYCLE) { p.mStack = new RuntimeException(); } p.mReadWriteHelper = ReadWriteHelper.DEFAULT; return p; } } Loading Loading @@ -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(); Loading Loading @@ -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); } Loading Loading @@ -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); } Loading Loading @@ -2997,6 +3066,7 @@ public final class Parcel { if (mOwnsNativeParcelObject) { updateNativeSize(nativeFreeBuffer(mNativePtr)); } mReadWriteHelper = ReadWriteHelper.DEFAULT; } private void destroy() { Loading @@ -3007,6 +3077,7 @@ public final class Parcel { } mNativePtr = 0; } mReadWriteHelper = null; } @Override Loading