Merge changes from topic 'TransferInstallSession-oc-mr1-dev' into oc-mr1-dev

* changes:
  Allow to read all params used to create session.
  Allow to transfer+seal a install session
diff --git a/api/current.txt b/api/current.txt
index 2097444..3c9cd8e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -37089,6 +37089,13 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR;
   }
 
+  public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator {
+    ctor public RegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -37129,13 +37136,6 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
-  public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator {
-    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
-  }
-
   public abstract interface Transformation {
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 6da5140..6c1983d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -17264,10 +17264,8 @@
     field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2
     field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
     field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
-    field public static final int IDENTIFIER_TYPE_VENDOR1_PRIMARY = 14; // 0xe
-    field public static final int IDENTIFIER_TYPE_VENDOR2_PRIMARY = 15; // 0xf
-    field public static final int IDENTIFIER_TYPE_VENDOR3_PRIMARY = 16; // 0x10
-    field public static final int IDENTIFIER_TYPE_VENDOR4_PRIMARY = 17; // 0x11
+    field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
+    field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
     field public static final int PROGRAM_TYPE_AM = 1; // 0x1
     field public static final int PROGRAM_TYPE_AM_HD = 3; // 0x3
     field public static final int PROGRAM_TYPE_DAB = 5; // 0x5
@@ -17275,10 +17273,8 @@
     field public static final int PROGRAM_TYPE_FM = 2; // 0x2
     field public static final int PROGRAM_TYPE_FM_HD = 4; // 0x4
     field public static final int PROGRAM_TYPE_SXM = 7; // 0x7
-    field public static final int PROGRAM_TYPE_VENDOR1 = 8; // 0x8
-    field public static final int PROGRAM_TYPE_VENDOR2 = 9; // 0x9
-    field public static final int PROGRAM_TYPE_VENDOR3 = 10; // 0xa
-    field public static final int PROGRAM_TYPE_VENDOR4 = 11; // 0xb
+    field public static final int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
+    field public static final int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
   }
 
   public static final class ProgramSelector.Identifier implements android.os.Parcelable {
@@ -40287,6 +40283,13 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR;
   }
 
+  public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator {
+    ctor public RegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -40327,13 +40330,6 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
-  public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator {
-    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
-  }
-
   public abstract interface Transformation {
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 7690a55..839964f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -37281,6 +37281,14 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR;
   }
 
+  public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator {
+    ctor public RegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
+    method public int describeContents();
+    method public boolean isValid(android.service.autofill.ValueFinder);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -37321,14 +37329,6 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
-  public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator {
-    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
-    method public int describeContents();
-    method public boolean isValid(android.service.autofill.ValueFinder);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
-  }
-
   public abstract interface Transformation {
   }
 
diff --git a/core/java/android/annotation/TargetApi.java b/core/java/android/annotation/TargetApi.java
index ea17890..975318e 100644
--- a/core/java/android/annotation/TargetApi.java
+++ b/core/java/android/annotation/TargetApi.java
@@ -16,6 +16,7 @@
 package android.annotation;
 
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.TYPE;
 
@@ -25,7 +26,7 @@
 
 /** Indicates that Lint should treat this type as targeting a given API level, no matter what the
     project target is. */
-@Target({TYPE, METHOD, CONSTRUCTOR})
+@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
 @Retention(RetentionPolicy.CLASS)
 public @interface TargetApi {
     /**
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index 371cd12..f99d1a8 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -19,6 +19,7 @@
 import android.os.FileUtils;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.system.ErrnoException;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -26,6 +27,8 @@
 import dalvik.system.BaseDexClassLoader;
 import dalvik.system.VMRuntime;
 
+import libcore.io.Libcore;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -151,22 +154,50 @@
             // The dex path is not a secondary dex file. Nothing to do.
             return;
         }
-        File secondaryProfile = getSecondaryProfileFile(dexPath);
+
+        File realDexPath;
         try {
-            // Create the profile if not already there.
-            // Returns true if the file was created, false if the file already exists.
-            // or throws exceptions in case of errors.
+            // Secondary dex profiles are stored in the oat directory, next to the real dex file
+            // and have the same name with 'cur.prof' appended. We use the realpath because that
+            // is what installd is using when processing the dex file.
+            // NOTE: Keep in sync with installd.
+            realDexPath = new File(Libcore.os.realpath(dexPath));
+        } catch (ErrnoException ex) {
+            Slog.e(TAG, "Failed to get the real path of secondary dex " + dexPath
+                    + ":" + ex.getMessage());
+            // Do not continue with registration if we could not retrieve the real path.
+            return;
+        }
+
+        // NOTE: Keep this in sync with installd expectations.
+        File secondaryProfileDir = new File(realDexPath.getParent(), "oat");
+        File secondaryProfile = new File(secondaryProfileDir, realDexPath.getName() + ".cur.prof");
+
+        // Create the profile if not already there.
+        // Returns true if the file was created, false if the file already exists.
+        // or throws exceptions in case of errors.
+        if (!secondaryProfileDir.exists()) {
+            if (!secondaryProfileDir.mkdir()) {
+                Slog.e(TAG, "Could not create the profile directory: " + secondaryProfile);
+                // Do not continue with registration if we could not create the oat dir.
+                return;
+            }
+        }
+
+        try {
             boolean created = secondaryProfile.createNewFile();
             if (DEBUG && created) {
                 Slog.i(TAG, "Created profile for secondary dex: " + secondaryProfile);
             }
         } catch (IOException ex) {
-            Slog.e(TAG, "Failed to create profile for secondary dex " + secondaryProfile +
-                    ":" + ex.getMessage());
-            // Don't move forward with the registration if we failed to create the profile.
+            Slog.e(TAG, "Failed to create profile for secondary dex " + dexPath
+                    + ":" + ex.getMessage());
+            // Do not continue with registration if we could not create the profile files.
             return;
         }
 
+        // If we got here, the dex paths is a secondary dex and we were able to create the profile.
+        // Register the path to the runtime.
         VMRuntime.registerAppInfo(secondaryProfile.getPath(), new String[] { dexPath });
     }
 
@@ -180,11 +211,4 @@
         }
         return false;
     }
-
-    // Secondary dex profiles are stored next to the dex file and have the same
-    // name with '.prof' appended.
-    // NOTE: Keep in sync with installd.
-    private File getSecondaryProfileFile(String dexPath) {
-        return new File(dexPath + ".prof");
-    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e8eca8c..4ae1aaf 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -50,6 +50,8 @@
 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.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 @@
 
     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 @@
     }
 
     @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 @@
             }
         }
 
-        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 @@
 
             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);
diff --git a/core/java/android/content/pm/PackageParserCacheHelper.java b/core/java/android/content/pm/PackageParserCacheHelper.java
new file mode 100644
index 0000000..44def33
--- /dev/null
+++ b/core/java/android/content/pm/PackageParserCacheHelper.java
@@ -0,0 +1,157 @@
+/*
+ * 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");
+            }
+        }
+    }
+}
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 94c25b3..2211cee 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -17,6 +17,7 @@
 package android.hardware.radio;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -73,10 +74,8 @@
     /** SiriusXM Satellite Radio. */
     public static final int PROGRAM_TYPE_SXM = 7;
     /** Vendor-specific, not synced across devices. */
-    public static final int PROGRAM_TYPE_VENDOR1 = 8;
-    public static final int PROGRAM_TYPE_VENDOR2 = 9;
-    public static final int PROGRAM_TYPE_VENDOR3 = 10;
-    public static final int PROGRAM_TYPE_VENDOR4 = 11;
+    public static final int PROGRAM_TYPE_VENDOR_START = 1000;
+    public static final int PROGRAM_TYPE_VENDOR_END = 1999;
     @IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
         PROGRAM_TYPE_AM,
         PROGRAM_TYPE_FM,
@@ -85,11 +84,8 @@
         PROGRAM_TYPE_DAB,
         PROGRAM_TYPE_DRMO,
         PROGRAM_TYPE_SXM,
-        PROGRAM_TYPE_VENDOR1,
-        PROGRAM_TYPE_VENDOR2,
-        PROGRAM_TYPE_VENDOR3,
-        PROGRAM_TYPE_VENDOR4,
     })
+    @IntRange(from = PROGRAM_TYPE_VENDOR_START, to = PROGRAM_TYPE_VENDOR_END)
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProgramType {}
 
@@ -145,12 +141,12 @@
      * Primary identifier for vendor-specific radio technology.
      * The value format is determined by a vendor.
      *
-     * It must not be used in any other programType than VENDORx.
+     * It must not be used in any other programType than corresponding VENDOR
+     * type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must
+     * not be used in any program type other than 1015).
      */
-    public static final int IDENTIFIER_TYPE_VENDOR1_PRIMARY = 14;
-    public static final int IDENTIFIER_TYPE_VENDOR2_PRIMARY = 15;
-    public static final int IDENTIFIER_TYPE_VENDOR3_PRIMARY = 16;
-    public static final int IDENTIFIER_TYPE_VENDOR4_PRIMARY = 17;
+    public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = PROGRAM_TYPE_VENDOR_START;
+    public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = PROGRAM_TYPE_VENDOR_END;
     @IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
         IDENTIFIER_TYPE_AMFM_FREQUENCY,
         IDENTIFIER_TYPE_RDS_PI,
@@ -165,11 +161,8 @@
         IDENTIFIER_TYPE_DRMO_MODULATION,
         IDENTIFIER_TYPE_SXM_SERVICE_ID,
         IDENTIFIER_TYPE_SXM_CHANNEL,
-        IDENTIFIER_TYPE_VENDOR1_PRIMARY,
-        IDENTIFIER_TYPE_VENDOR2_PRIMARY,
-        IDENTIFIER_TYPE_VENDOR3_PRIMARY,
-        IDENTIFIER_TYPE_VENDOR4_PRIMARY,
     })
+    @IntRange(from = IDENTIFIER_TYPE_VENDOR_PRIMARY_START, to = IDENTIFIER_TYPE_VENDOR_PRIMARY_END)
     @Retention(RetentionPolicy.SOURCE)
     public @interface IdentifierType {}
 
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index a5763ef..5312dca 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -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.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 @@
     private ClassLoader mClassLoader;
 
     /** {@hide} */
-    int mFlags;
+    @VisibleForTesting
+    public int mFlags;
 
     /**
      * Constructs a new, empty Bundle that uses a specific ClassLoader for
@@ -218,59 +221,72 @@
      */
     /* package */ void unparcel() {
         synchronized (this) {
-            final Parcel parcelledData = mParcelledData;
-            if (parcelledData == null) {
-                if (DEBUG) Log.d(TAG, "unparcel "
-                        + Integer.toHexString(System.identityHashCode(this))
-                        + ": no parcelled data");
-                return;
-            }
-
-            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 "
-                        + Integer.toHexString(System.identityHashCode(this)) + ": empty");
-                if (mMap == null) {
-                    mMap = new ArrayMap<>(1);
-                } else {
-                    mMap.erase();
-                }
-                mParcelledData = null;
-                return;
-            }
-
-            int N = parcelledData.readInt();
-            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
-                    + ": reading " + N + " maps");
-            if (N < 0) {
-                return;
-            }
-            ArrayMap<String, Object> map = mMap;
-            if (map == null) {
-                map = new ArrayMap<>(N);
+            final Parcel source = mParcelledData;
+            if (source != null) {
+                initializeFromParcelLocked(source, /*recycleParcel=*/ true);
             } else {
-                map.erase();
-                map.ensureCapacity(N);
-            }
-            try {
-                parcelledData.readArrayMapInternal(map, N, mClassLoader);
-            } catch (BadParcelableException e) {
-                if (sShouldDefuse) {
-                    Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
-                    map.erase();
-                } else {
-                    throw e;
+                if (DEBUG) {
+                    Log.d(TAG, "unparcel "
+                            + Integer.toHexString(System.identityHashCode(this))
+                            + ": no parcelled data");
                 }
-            } finally {
-                mMap = map;
-                parcelledData.recycle();
-                mParcelledData = null;
             }
-            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+        }
+    }
+
+    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(parcelledData)) {
+            if (DEBUG) {
+                Log.d(TAG, "unparcel "
+                        + Integer.toHexString(System.identityHashCode(this)) + ": empty");
+            }
+            if (mMap == null) {
+                mMap = new ArrayMap<>(1);
+            } else {
+                mMap.erase();
+            }
+            mParcelledData = null;
+            return;
+        }
+
+        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<>(count);
+        } else {
+            map.erase();
+            map.ensureCapacity(count);
+        }
+        try {
+            parcelledData.readArrayMapInternal(map, count, mClassLoader);
+        } catch (BadParcelableException e) {
+            if (sShouldDefuse) {
+                Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
+                map.erase();
+            } else {
+                throw e;
+            }
+        } finally {
+            mMap = map;
+            if (recycleParcel) {
+                recycleParcel(parcelledData);
+            }
+            mParcelledData = null;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
                     + " final map: " + mMap);
         }
     }
@@ -286,7 +302,20 @@
      * @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 @@
      * @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 @@
                     + 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));
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cc6b5e1..6d44330 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -206,8 +206,15 @@
      *   - CPU frequency time per uid
      * New in version 22:
      *   - BLE scan result background count, BLE unoptimized scan time
+     *   - Background partial wakelock time & count
+     * New in version 23:
+     *   - Logging smeared power model values
+     * New in version 24:
+     *   - Fixed bugs in background timers and BLE scan time
+     * New in version 25:
+     *   - Package wakeup alarms are now on screen-off timebase
      */
-    static final String CHECKIN_VERSION = "24";
+    static final String CHECKIN_VERSION = "25";
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
@@ -686,7 +693,7 @@
         public abstract long getSystemCpuTimeUs(int which);
 
         /**
-         * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed for a
+         * Returns the approximate cpu time (in microseconds) spent at a certain CPU speed for a
          * given CPU cluster.
          * @param cluster the index of the CPU cluster.
          * @param step the index of the CPU speed. This is not the actual speed of the CPU.
@@ -3544,7 +3551,12 @@
                     if (name.indexOf(',') >= 0) {
                         name = name.replace(',', '_');
                     }
-                    name = name.replaceAll("[\\n|\\r]+", "");
+                    if (name.indexOf('\n') >= 0) {
+                        name = name.replace('\n', '_');
+                    }
+                    if (name.indexOf('\r') >= 0) {
+                        name = name.replace('\r', '_');
+                    }
                     dumpLine(pw, uid, category, WAKELOCK_DATA, name, sb.toString());
                 }
             }
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 51f96ee..c58153a 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -22,6 +22,8 @@
 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 @@
  * @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 @@
      * 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;
-        if (mParcelledData.hasFileDescriptors()) {
-            mFlags |= FLAG_HAS_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 | FLAG_HAS_FDS_KNOWN;
+            } else {
+                mFlags |= FLAG_HAS_FDS_KNOWN;
+            }
         }
     }
 
@@ -1213,10 +1242,8 @@
      */
     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
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 2efb0f5..fae9d53 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -291,7 +291,7 @@
     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 @@
     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);
 
@@ -339,6 +339,33 @@
     };
 
     /**
+     * @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.
      */
     public static Parcel obtain() {
@@ -352,6 +379,7 @@
                     if (DEBUG_RECYCLE) {
                         p.mStack = new RuntimeException();
                     }
+                    p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
                     return p;
                 }
             }
@@ -385,6 +413,25 @@
         }
     }
 
+    /**
+     * 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 @@
      * 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 @@
      * 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 @@
         if (mOwnsNativeParcelObject) {
             updateNativeSize(nativeFreeBuffer(mNativePtr));
         }
+        mReadWriteHelper = ReadWriteHelper.DEFAULT;
     }
 
     private void destroy() {
@@ -3007,6 +3077,7 @@
             }
             mNativePtr = 0;
         }
+        mReadWriteHelper = null;
     }
 
     @Override
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index 0edb154..3da0b5e 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -32,7 +32,7 @@
  *
  * <p>This is useful when the autofill service needs to show a detailed view of what would be saved;
  * for example, when the screen contains a credit card, it could display a logo of the credit card
- * bank, the last for digits of the credit card number, and its expiration number.
+ * bank, the last four digits of the credit card number, and its expiration number.
  *
  * <p>A custom description is made of 2 parts:
  * <ul>
@@ -63,16 +63,16 @@
  * // Image child - different logo for each bank, based on credit card prefix
  * builder.addChild(R.id.templateccLogo,
  *   new ImageTransformation.Builder(ccNumberId)
- *     .addOption("^4815.*$", R.drawable.ic_credit_card_logo1)
- *     .addOption("^1623.*$", R.drawable.ic_credit_card_logo2)
- *     .addOption("^42.*$", R.drawable.ic_credit_card_logo3);
+ *     .addOption(Pattern.compile(""^4815.*$"), R.drawable.ic_credit_card_logo1)
+ *     .addOption(Pattern.compile(""^1623.*$"), R.drawable.ic_credit_card_logo2)
+ *     .addOption(Pattern.compile(""^42.*$"), R.drawable.ic_credit_card_logo3);
  * // Masked credit card number (as .....LAST_4_DIGITS)
  * builder.addChild(R.id.templateCcNumber, new CharSequenceTransformation.Builder()
- *     .addField(ccNumberId, "^.*(\\d\\d\\d\\d)$", "...$1")
+ *     .addField(ccNumberId, Pattern.compile(""^.*(\\d\\d\\d\\d)$"), "...$1")
  * // Expiration date as MM / YYYY:
  * builder.addChild(R.id.templateExpDate, new CharSequenceTransformation.Builder()
- *     .addField(ccExpMonthId, "^(\\d\\d)$", "Exp: $1")
- *     .addField(ccExpYearId, "^(\\d\\d)$", "/$1");
+ *     .addField(ccExpMonthId, Pattern.compile(""^(\\d\\d)$"), "Exp: $1")
+ *     .addField(ccExpYearId, Pattern.compile(""^(\\d\\d)$"), "/$1");
  * </pre>
  *
  * <p>See {@link ImageTransformation}, {@link CharSequenceTransformation} for more info about these
diff --git a/core/java/android/service/autofill/SimpleRegexValidator.java b/core/java/android/service/autofill/RegexValidator.java
similarity index 79%
rename from core/java/android/service/autofill/SimpleRegexValidator.java
rename to core/java/android/service/autofill/RegexValidator.java
index ef8c52c9..9dfe78d 100644
--- a/core/java/android/service/autofill/SimpleRegexValidator.java
+++ b/core/java/android/service/autofill/RegexValidator.java
@@ -34,9 +34,9 @@
  *
  * <p>See {@link SaveInfo.Builder#setValidator(Validator)} for examples.
  */
-public final class SimpleRegexValidator extends InternalValidator implements Validator, Parcelable {
+public final class RegexValidator extends InternalValidator implements Validator, Parcelable {
 
-    private static final String TAG = "SimpleRegexValidator";
+    private static final String TAG = "RegexValidator";
 
     private final AutofillId mId;
     private final Pattern mRegex;
@@ -49,7 +49,7 @@
      * matches the contents of the field identified by {@code id}, it returns {@code true};
      * otherwise, it returns {@code false}.
       */
-    public SimpleRegexValidator(@NonNull AutofillId id, @NonNull Pattern regex) {
+    public RegexValidator(@NonNull AutofillId id, @NonNull Pattern regex) {
         mId = Preconditions.checkNotNull(id);
         mRegex = Preconditions.checkNotNull(regex);
     }
@@ -76,7 +76,7 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        return "SimpleRegexValidator: [id=" + mId + ", regex=" + mRegex + "]";
+        return "RegexValidator: [id=" + mId + ", regex=" + mRegex + "]";
     }
 
     /////////////////////////////////////
@@ -93,17 +93,17 @@
         parcel.writeSerializable(mRegex);
     }
 
-    public static final Parcelable.Creator<SimpleRegexValidator> CREATOR =
-            new Parcelable.Creator<SimpleRegexValidator>() {
+    public static final Parcelable.Creator<RegexValidator> CREATOR =
+            new Parcelable.Creator<RegexValidator>() {
         @Override
-        public SimpleRegexValidator createFromParcel(Parcel parcel) {
-            return new SimpleRegexValidator(parcel.readParcelable(null),
+        public RegexValidator createFromParcel(Parcel parcel) {
+            return new RegexValidator(parcel.readParcelable(null),
                     (Pattern) parcel.readSerializable());
         }
 
         @Override
-        public SimpleRegexValidator[] newArray(int size) {
-            return new SimpleRegexValidator[size];
+        public RegexValidator[] newArray(int size) {
+            return new RegexValidator[size];
         }
     };
 }
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index f8a94d6..e0a0730 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -470,7 +470,7 @@
          * <p>Validator for a credit number that must have exactly 16 digits:
          *
          * <pre class="prettyprint">
-         * Validator validator = new SimpleRegexValidator(ccNumberId, "^\\d{16}$")
+         * Validator validator = new RegexValidator(ccNumberId, Pattern.compile(""^\\d{16}$"))
          * </pre>
          *
          * <p>Validator for a credit number that must pass a Luhn checksum and either have
@@ -483,8 +483,8 @@
          *   and(
          *     new LuhnChecksumValidator(ccNumberId),
          *     or(
-         *       new SimpleRegexValidator(ccNumberId, "^\\d{16}$"),
-         *       new SimpleRegexValidator(ccNumberId, "^108\\d{12}$")
+         *       new RegexValidator(ccNumberId, Pattern.compile(""^\\d{16}$")),
+         *       new RegexValidator(ccNumberId, Pattern.compile(""^108\\d{12}$"))
          *     )
          *   );
          * </pre>
@@ -496,7 +496,7 @@
          * Validator validator =
          *   and(
          *     new LuhnChecksumValidator(ccNumberId),
-         *     new SimpleRegexValidator(ccNumberId, "^(\\d{16}|108\\d{12})$")
+         *     new RegexValidator(ccNumberId, Pattern.compile(""^(\\d{16}|108\\d{12})$"))
          *   );
          * </pre>
          *
@@ -508,10 +508,10 @@
          *
          * Validator validator =
          *   and(
-         *     new SimpleRegexValidator(ccNumberId1, "^\\d{4}$"),
-         *     new SimpleRegexValidator(ccNumberId2, "^\\d{4}$"),
-         *     new SimpleRegexValidator(ccNumberId3, "^\\d{4}$"),
-         *     new SimpleRegexValidator(ccNumberId4, "^\\d{4}$")
+         *     new RegexValidator(ccNumberId1, Pattern.compile(""^\\d{4}$")),
+         *     new RegexValidator(ccNumberId2, Pattern.compile(""^\\d{4}$")),
+         *     new RegexValidator(ccNumberId3, Pattern.compile(""^\\d{4}$")),
+         *     new RegexValidator(ccNumberId4, Pattern.compile(""^\\d{4}$"))
          *   );
          * </pre>
          *
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 44c88e1..1a2968f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -65,7 +65,8 @@
     private static native void nativeSetSize(long nativeObject, int w, int h);
     private static native void nativeSetTransparentRegionHint(long nativeObject, Region region);
     private static native void nativeSetAlpha(long nativeObject, float alpha);
-    private static native void nativeSetMatrix(long nativeObject, float dsdx, float dtdx, float dsdy, float dtdy);
+    private static native void nativeSetMatrix(long nativeObject, float dsdx, float dtdx,
+            float dtdy, float dsdy);
     private static native void nativeSetFlags(long nativeObject, int flags, int mask);
     private static native void nativeSetWindowCrop(long nativeObject, int l, int t, int r, int b);
     private static native void nativeSetFinalCrop(long nativeObject, int l, int t, int r, int b);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e59cf84..e2bcea0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 162 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 163 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -5707,7 +5707,7 @@
 
         LongSamplingCounter mUserCpuTime;
         LongSamplingCounter mSystemCpuTime;
-        LongSamplingCounter[][] mCpuClusterSpeed;
+        LongSamplingCounter[][] mCpuClusterSpeedTimesUs;
 
         LongSamplingCounterArray mCpuFreqTimeMs;
         LongSamplingCounterArray mScreenOffCpuFreqTimeMs;
@@ -6556,12 +6556,12 @@
 
         @Override
         public long getTimeAtCpuSpeed(int cluster, int step, int which) {
-            if (mCpuClusterSpeed != null) {
-                if (cluster >= 0 && cluster < mCpuClusterSpeed.length) {
-                    final LongSamplingCounter[] cpuSpeeds = mCpuClusterSpeed[cluster];
-                    if (cpuSpeeds != null) {
-                        if (step >= 0 && step < cpuSpeeds.length) {
-                            final LongSamplingCounter c = cpuSpeeds[step];
+            if (mCpuClusterSpeedTimesUs != null) {
+                if (cluster >= 0 && cluster < mCpuClusterSpeedTimesUs.length) {
+                    final LongSamplingCounter[] cpuSpeedTimesUs = mCpuClusterSpeedTimesUs[cluster];
+                    if (cpuSpeedTimesUs != null) {
+                        if (step >= 0 && step < cpuSpeedTimesUs.length) {
+                            final LongSamplingCounter c = cpuSpeedTimesUs[step];
                             if (c != null) {
                                 return c.getCountLocked(which);
                             }
@@ -6712,8 +6712,8 @@
             mUserCpuTime.reset(false);
             mSystemCpuTime.reset(false);
 
-            if (mCpuClusterSpeed != null) {
-                for (LongSamplingCounter[] speeds : mCpuClusterSpeed) {
+            if (mCpuClusterSpeedTimesUs != null) {
+                for (LongSamplingCounter[] speeds : mCpuClusterSpeedTimesUs) {
                     if (speeds != null) {
                         for (LongSamplingCounter speed : speeds) {
                             if (speed != null) {
@@ -6902,8 +6902,8 @@
                 mUserCpuTime.detach();
                 mSystemCpuTime.detach();
 
-                if (mCpuClusterSpeed != null) {
-                    for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
+                if (mCpuClusterSpeedTimesUs != null) {
+                    for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) {
                         if (cpuSpeeds != null) {
                             for (LongSamplingCounter c : cpuSpeeds) {
                                 if (c != null) {
@@ -7156,10 +7156,10 @@
             mUserCpuTime.writeToParcel(out);
             mSystemCpuTime.writeToParcel(out);
 
-            if (mCpuClusterSpeed != null) {
+            if (mCpuClusterSpeedTimesUs != null) {
                 out.writeInt(1);
-                out.writeInt(mCpuClusterSpeed.length);
-                for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
+                out.writeInt(mCpuClusterSpeedTimesUs.length);
+                for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) {
                     if (cpuSpeeds != null) {
                         out.writeInt(1);
                         out.writeInt(cpuSpeeds.length);
@@ -7453,7 +7453,7 @@
                     throw new ParcelFormatException("Incompatible number of cpu clusters");
                 }
 
-                mCpuClusterSpeed = new LongSamplingCounter[numCpuClusters][];
+                mCpuClusterSpeedTimesUs = new LongSamplingCounter[numCpuClusters][];
                 for (int cluster = 0; cluster < numCpuClusters; cluster++) {
                     if (in.readInt() != 0) {
                         int numSpeeds = in.readInt();
@@ -7463,18 +7463,19 @@
                         }
 
                         final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
-                        mCpuClusterSpeed[cluster] = cpuSpeeds;
+                        mCpuClusterSpeedTimesUs[cluster] = cpuSpeeds;
                         for (int speed = 0; speed < numSpeeds; speed++) {
                             if (in.readInt() != 0) {
-                                cpuSpeeds[speed] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
+                                cpuSpeeds[speed] = new LongSamplingCounter(
+                                        mBsi.mOnBatteryTimeBase, in);
                             }
                         }
                     } else {
-                        mCpuClusterSpeed[cluster] = null;
+                        mCpuClusterSpeedTimesUs[cluster] = null;
                     }
                 }
             } else {
-                mCpuClusterSpeed = null;
+                mCpuClusterSpeedTimesUs = null;
             }
 
             mCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(in, mBsi.mOnBatteryTimeBase);
@@ -8043,6 +8044,7 @@
 
             /**
              * Number of times wakeup alarms have occurred for this app.
+             * On screen-off timebase starting in report v25.
              */
             ArrayMap<String, Counter> mWakeupAlarms = new ArrayMap<>();
 
@@ -8071,7 +8073,7 @@
                 mWakeupAlarms.clear();
                 for (int i=0; i<numWA; i++) {
                     String tag = in.readString();
-                    mWakeupAlarms.put(tag, new Counter(mBsi.mOnBatteryTimeBase, in));
+                    mWakeupAlarms.put(tag, new Counter(mBsi.mOnBatteryScreenOffTimeBase, in));
                 }
 
                 int numServs = in.readInt();
@@ -8110,7 +8112,7 @@
             public void noteWakeupAlarmLocked(String tag) {
                 Counter c = mWakeupAlarms.get(tag);
                 if (c == null) {
-                    c = new Counter(mBsi.mOnBatteryTimeBase);
+                    c = new Counter(mBsi.mOnBatteryScreenOffTimeBase);
                     mWakeupAlarms.put(tag, c);
                 }
                 c.stepAtomic();
@@ -10429,46 +10431,48 @@
             }
         }
 
-        long totalCpuClustersTime = 0;
+        long totalCpuClustersTimeMs = 0;
         // Read the time spent for each cluster at various cpu frequencies.
-        final long[][] clusterSpeeds = new long[mKernelCpuSpeedReaders.length][];
+        final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][];
         for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
-            clusterSpeeds[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
-            if (clusterSpeeds[cluster] != null) {
-                for (int speed = clusterSpeeds[cluster].length - 1; speed >= 0; --speed) {
-                    totalCpuClustersTime += clusterSpeeds[cluster][speed];
+            clusterSpeedTimesMs[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
+            if (clusterSpeedTimesMs[cluster] != null) {
+                for (int speed = clusterSpeedTimesMs[cluster].length - 1; speed >= 0; --speed) {
+                    totalCpuClustersTimeMs += clusterSpeedTimesMs[cluster][speed];
                 }
             }
         }
-        if (totalCpuClustersTime != 0) {
+        if (totalCpuClustersTimeMs != 0) {
             // We have cpu times per freq aggregated over all uids but we need the times per uid.
             // So, we distribute total time spent by an uid to different cpu freqs based on the
             // amount of time cpu was running at that freq.
             final int updatedUidsCount = updatedUids.size();
             for (int i = 0; i < updatedUidsCount; ++i) {
                 final Uid u = getUidStatsLocked(updatedUids.keyAt(i));
-                final long appCpuTimeMs = updatedUids.valueAt(i) / 1000;
+                final long appCpuTimeUs = updatedUids.valueAt(i);
                 // Add the cpu speeds to this UID.
                 final int numClusters = mPowerProfile.getNumCpuClusters();
-                if (u.mCpuClusterSpeed == null || u.mCpuClusterSpeed.length !=
+                if (u.mCpuClusterSpeedTimesUs == null || u.mCpuClusterSpeedTimesUs.length !=
                         numClusters) {
-                    u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
+                    u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
                 }
 
-                for (int cluster = 0; cluster < clusterSpeeds.length; cluster++) {
-                    final int speedsInCluster = clusterSpeeds[cluster].length;
-                    if (u.mCpuClusterSpeed[cluster] == null || speedsInCluster !=
-                            u.mCpuClusterSpeed[cluster].length) {
-                        u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[speedsInCluster];
+                for (int cluster = 0; cluster < clusterSpeedTimesMs.length; cluster++) {
+                    final int speedsInCluster = clusterSpeedTimesMs[cluster].length;
+                    if (u.mCpuClusterSpeedTimesUs[cluster] == null || speedsInCluster !=
+                            u.mCpuClusterSpeedTimesUs[cluster].length) {
+                        u.mCpuClusterSpeedTimesUs[cluster]
+                                = new LongSamplingCounter[speedsInCluster];
                     }
 
-                    final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeed[cluster];
+                    final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeedTimesUs[cluster];
                     for (int speed = 0; speed < speedsInCluster; speed++) {
                         if (cpuSpeeds[speed] == null) {
                             cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
                         }
-                        cpuSpeeds[speed].addCountLocked(appCpuTimeMs * clusterSpeeds[cluster][speed]
-                                / totalCpuClustersTime);
+                        cpuSpeeds[speed].addCountLocked(appCpuTimeUs
+                                * clusterSpeedTimesMs[cluster][speed]
+                                / totalCpuClustersTimeMs);
                     }
                 }
             }
@@ -11752,7 +11756,7 @@
                     throw new ParcelFormatException("Incompatible cpu cluster arrangement");
                 }
 
-                u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
+                u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
                 for (int cluster = 0; cluster < numClusters; cluster++) {
                     if (in.readInt() != 0) {
                         final int NSB = in.readInt();
@@ -11762,20 +11766,20 @@
                                     NSB);
                         }
 
-                        u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[NSB];
+                        u.mCpuClusterSpeedTimesUs[cluster] = new LongSamplingCounter[NSB];
                         for (int speed = 0; speed < NSB; speed++) {
                             if (in.readInt() != 0) {
-                                u.mCpuClusterSpeed[cluster][speed] = new LongSamplingCounter(
+                                u.mCpuClusterSpeedTimesUs[cluster][speed] = new LongSamplingCounter(
                                         mOnBatteryTimeBase);
-                                u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in);
+                                u.mCpuClusterSpeedTimesUs[cluster][speed].readSummaryFromParcelLocked(in);
                             }
                         }
                     } else {
-                        u.mCpuClusterSpeed[cluster] = null;
+                        u.mCpuClusterSpeedTimesUs[cluster] = null;
                     }
                 }
             } else {
-                u.mCpuClusterSpeed = null;
+                u.mCpuClusterSpeedTimesUs = null;
             }
 
             u.mCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
@@ -11867,7 +11871,7 @@
                 p.mWakeupAlarms.clear();
                 for (int iwa=0; iwa<NWA; iwa++) {
                     String tag = in.readString();
-                    Counter c = new Counter(mOnBatteryTimeBase);
+                    Counter c = new Counter(mOnBatteryScreenOffTimeBase);
                     c.readSummaryFromParcelLocked(in);
                     p.mWakeupAlarms.put(tag, c);
                 }
@@ -12183,10 +12187,10 @@
             u.mUserCpuTime.writeSummaryFromParcelLocked(out);
             u.mSystemCpuTime.writeSummaryFromParcelLocked(out);
 
-            if (u.mCpuClusterSpeed != null) {
+            if (u.mCpuClusterSpeedTimesUs != null) {
                 out.writeInt(1);
-                out.writeInt(u.mCpuClusterSpeed.length);
-                for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeed) {
+                out.writeInt(u.mCpuClusterSpeedTimesUs.length);
+                for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeedTimesUs) {
                     if (cpuSpeeds != null) {
                         out.writeInt(1);
                         out.writeInt(cpuSpeeds.length);
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 9d52e19..bb743c1 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -22,6 +22,7 @@
 public class CpuPowerCalculator extends PowerCalculator {
     private static final String TAG = "CpuPowerCalculator";
     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+    private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000;
     private final PowerProfile mProfile;
 
     public CpuPowerCalculator(PowerProfile profile) {
@@ -35,21 +36,22 @@
         app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
         final int numClusters = mProfile.getNumCpuClusters();
 
-        double cpuPowerMaMs = 0;
+        double cpuPowerMaUs = 0;
         for (int cluster = 0; cluster < numClusters; cluster++) {
             final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
             for (int speed = 0; speed < speedsForCluster; speed++) {
-                final double cpuSpeedStepPower = u.getTimeAtCpuSpeed(cluster, speed, statsType) *
+                final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
+                final double cpuSpeedStepPower = timeUs *
                         mProfile.getAveragePowerForCpu(cluster, speed);
                 if (DEBUG) {
                     Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
-                            + speed + " power="
-                            + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
+                            + speed + " timeUs=" + timeUs + " power="
+                            + BatteryStatsHelper.makemAh(cpuSpeedStepPower / MICROSEC_IN_HR));
                 }
-                cpuPowerMaMs += cpuSpeedStepPower;
+                cpuPowerMaUs += cpuSpeedStepPower;
             }
         }
-        app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);
+        app.cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
 
         if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
             Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
index 9c7debb..757a112 100644
--- a/core/java/com/android/internal/os/KernelCpuSpeedReader.java
+++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
@@ -39,8 +39,8 @@
     private static final String TAG = "KernelCpuSpeedReader";
 
     private final String mProcFile;
-    private final long[] mLastSpeedTimes;
-    private final long[] mDeltaSpeedTimes;
+    private final long[] mLastSpeedTimesMs;
+    private final long[] mDeltaSpeedTimesMs;
 
     // How long a CPU jiffy is in milliseconds.
     private final long mJiffyMillis;
@@ -51,8 +51,8 @@
     public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) {
         mProcFile = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
                 cpuNumber);
-        mLastSpeedTimes = new long[numSpeedSteps];
-        mDeltaSpeedTimes = new long[numSpeedSteps];
+        mLastSpeedTimesMs = new long[numSpeedSteps];
+        mDeltaSpeedTimesMs = new long[numSpeedSteps];
         long jiffyHz = Libcore.os.sysconf(OsConstants._SC_CLK_TCK);
         mJiffyMillis = 1000/jiffyHz;
     }
@@ -68,27 +68,27 @@
             TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
             String line;
             int speedIndex = 0;
-            while (speedIndex < mLastSpeedTimes.length && (line = reader.readLine()) != null) {
+            while (speedIndex < mLastSpeedTimesMs.length && (line = reader.readLine()) != null) {
                 splitter.setString(line);
-                Long.parseLong(splitter.next());
+                splitter.next();
 
                 long time = Long.parseLong(splitter.next()) * mJiffyMillis;
-                if (time < mLastSpeedTimes[speedIndex]) {
+                if (time < mLastSpeedTimesMs[speedIndex]) {
                     // The stats reset when the cpu hotplugged. That means that the time
                     // we read is offset from 0, so the time is the delta.
-                    mDeltaSpeedTimes[speedIndex] = time;
+                    mDeltaSpeedTimesMs[speedIndex] = time;
                 } else {
-                    mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex];
+                    mDeltaSpeedTimesMs[speedIndex] = time - mLastSpeedTimesMs[speedIndex];
                 }
-                mLastSpeedTimes[speedIndex] = time;
+                mLastSpeedTimesMs[speedIndex] = time;
                 speedIndex++;
             }
         } catch (IOException e) {
             Slog.e(TAG, "Failed to read cpu-freq: " + e.getMessage());
-            Arrays.fill(mDeltaSpeedTimes, 0);
+            Arrays.fill(mDeltaSpeedTimesMs, 0);
         } finally {
             StrictMode.setThreadPolicy(policy);
         }
-        return mDeltaSpeedTimes;
+        return mDeltaSpeedTimesMs;
     }
 }
diff --git a/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml b/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml
index 4b01736..2c430e0 100644
--- a/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml
+++ b/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml
@@ -40,15 +40,31 @@
 
     <application
         android:hasCode="true">
+        <meta-data android:name="key1" android:value="value1" />
+        <meta-data android:name="key2" android:value="this_is_app" />
+
         <activity
             android:name="com.android.frameworks.coretests.TestActivity">
+            <meta-data android:name="key1" android:value="value1" />
+            <meta-data android:name="key2" android:value="this_is_activity" />
         </activity>
         <provider
             android:name="com.android.frameworks.coretests.TestProvider"
-            android:authorities="com.android.frameworks.coretests.testprovider" />
+            android:authorities="com.android.frameworks.coretests.testprovider" >
+            <meta-data android:name="key1" android:value="value1" />
+            <meta-data android:name="key2" android:value="this_is_provider" />
+        </provider>
+
         <receiver
-            android:name="com.android.frameworks.coretests.TestReceiver" />
+            android:name="com.android.frameworks.coretests.TestReceiver" >
+            <meta-data android:name="key1" android:value="value1" />
+            <meta-data android:name="key2" android:value="this_is_receiver" />
+        </receiver>
+
         <service
-            android:name="com.android.frameworks.coretests.TestService" />
+            android:name="com.android.frameworks.coretests.TestService" >
+            <meta-data android:name="key1" android:value="value1" />
+            <meta-data android:name="key2" android:value="this_is_service" />
+        </service>
     </application>
 </manifest>
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
new file mode 100644
index 0000000..00be822
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import android.content.pm.PackageParserCacheHelper.ReadHelper;
+import android.content.pm.PackageParserCacheHelper.WriteHelper;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PackageParserCacheHelperTest {
+    @Test
+    public void testParcelUnparcel() throws Exception {
+        final Bundle source = new Bundle();
+        source.putInt("i1", 123);
+        source.putString("s1", "abcdef");
+        source.putString("s2", "xyz");
+        source.putString("s3", null);
+
+        final Bundle nest = new Bundle();
+        nest.putString("s1", "xyz");
+        source.putBundle("b1", nest);
+
+        final Parcel p = Parcel.obtain();
+        final WriteHelper writeHelper = new WriteHelper(p);
+
+        source.writeToParcel(p, 0);
+        writeHelper.finishAndUninstall();
+
+        p.setDataPosition(0);
+
+        final ReadHelper readHelper = new ReadHelper(p);
+        readHelper.startAndInstall();
+
+        final Bundle dest = new Bundle();
+        dest.readFromParcel(p);
+
+        dest.size(); // Unparcel so that toString() returns the content.
+
+        assertEquals(source.get("i1"), dest.get("i1"));
+        assertEquals(source.get("s1"), dest.get("s1"));
+        assertEquals(source.get("s2"), dest.get("s2"));
+        assertEquals(source.get("s3"), dest.get("s3"));
+        assertEquals(source.getBundle("b1").get("s1"), dest.getBundle("b1").get("s1"));
+        assertEquals(source.keySet().size(), dest.keySet().size());
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 73c153f..fda0f1e 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageParser.Package;
 import android.content.pm.PackageParser.Permission;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.FileUtils;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
@@ -39,6 +40,7 @@
 import java.io.File;
 import java.io.InputStream;
 import java.util.Arrays;
+import java.util.function.Function;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -271,22 +273,27 @@
         assertEquals(0x0083, finalConfigChanges); // Should be 10000011.
     }
 
+    Package parsePackage(String apkFileName, int apkResourceId) throws Exception {
+        return parsePackage(apkFileName, apkResourceId, p -> p);
+    }
+
     /**
      * Attempts to parse a package.
      *
      * APKs are put into coretests/apks/packageparser_*.
      *
-     * @param apkName temporary file name to store apk extracted from resources
+     * @param apkFileName temporary file name to store apk extracted from resources
      * @param apkResourceId identifier of the apk as a resource
      */
-    Package parsePackage(String apkFileName, int apkResourceId) throws Exception {
+    Package parsePackage(String apkFileName, int apkResourceId,
+            Function<Package, Package> converter) throws Exception {
         // Copy the resource to a file.
         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
         File outFile = new File(context.getFilesDir(), apkFileName);
         try {
             InputStream is = context.getResources().openRawResource(apkResourceId);
             assertTrue(FileUtils.copyToFile(is, outFile));
-            return new PackageParser().parsePackage(outFile, 0 /* flags */);
+            return converter.apply(new PackageParser().parsePackage(outFile, 0 /* flags */));
         } finally {
             outFile.delete();
         }
@@ -331,10 +338,39 @@
         assertEquals(protectionLevel, permission.info.protectionLevel);
     }
 
+    private void assertMetadata(Bundle b, String... keysAndValues) {
+        assertTrue("Odd number of elements in keysAndValues", (keysAndValues.length % 2) == 0);
+
+        assertNotNull(b);
+        assertEquals(keysAndValues.length / 2, b.size());
+
+        for (int i = 0; i < keysAndValues.length; i += 2) {
+            final String key = keysAndValues[i];
+            final String value = keysAndValues[i + 1];
+
+            assertEquals(value, b.getString(key));
+        }
+    }
+
+    // TODO Add a "_cached" test for testMultiPackageComponents() too, after fixing b/64295061.
+    // Package.writeToParcel can't handle circular package references.
+
     @Test
-    public void testPackageWithComponents() throws Exception {
+    public void testPackageWithComponents_no_cache() throws Exception {
+        checkPackageWithComponents(p -> p);
+    }
+
+    @Test
+    public void testPackageWithComponents_cached() throws Exception {
+        checkPackageWithComponents(p ->
+                PackageParser.fromCacheEntryStatic(PackageParser.toCacheEntryStatic(p)));
+    }
+
+    private void checkPackageWithComponents(
+            Function<Package, Package> converter) throws Exception {
         Package p = parsePackage(
-                "install_complete_package_info.apk", R.raw.install_complete_package_info);
+                "install_complete_package_info.apk", R.raw.install_complete_package_info,
+                converter);
         String packageName = "com.android.frameworks.coretests.install_complete_package_info";
 
         assertEquals(packageName, p.packageName);
@@ -344,6 +380,22 @@
                 packageName, PermissionInfo.PROTECTION_NORMAL, p.permissions.get(0));
 
         assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", p);
+
+        assertMetadata(p.mAppMetaData,
+                "key1", "value1",
+                "key2", "this_is_app");
+        assertMetadata(p.activities.get(0).metaData,
+                "key1", "value1",
+                "key2", "this_is_activity");
+        assertMetadata(p.services.get(0).metaData,
+                "key1", "value1",
+                "key2", "this_is_service");
+        assertMetadata(p.receivers.get(0).metaData,
+                "key1", "value1",
+                "key2", "this_is_receiver");
+        assertMetadata(p.providers.get(0).metaData,
+                "key1", "value1",
+                "key2", "this_is_provider");
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
new file mode 100644
index 0000000..9fcf96d
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for bundle that requires accessing hidden APS.  Tests that can be written only with
+ * public APIs should go in the CTS counterpart.
+ *
+ * Run with:
+ * bit FrameworksCoreTests:android.os.BundleTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BundleTest {
+    /**
+     * Create a test bundle, parcel it and return the parcel.
+     */
+    private Parcel createBundleParcel(boolean withFd) throws Exception {
+        final Bundle source = new Bundle();
+        source.putString("string", "abc");
+        source.putInt("int", 1);
+        if (withFd) {
+            ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+            pipe[1].close();
+            source.putParcelable("fd", pipe[0]);
+        }
+        final Parcel p = Parcel.obtain();
+        // Don't use p.writeParcelabe(), which would write the creator, which we don't need.
+        source.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        return p;
+    }
+
+    /**
+     * Verify a bundle generated by {@link #createBundleParcel(boolean)}.
+     */
+    private void checkBundle(Bundle b, boolean withFd) {
+        // First, do the checks without actually unparceling the bundle.
+        // (Note looking into the contents will unparcel a bundle, so we'll do it later.)
+        assertTrue("mParcelledData shouldn't be null here.", b.isParcelled());
+
+        // Make sure FLAG_HAS_FDS and FLAG_HAS_FDS_KNOWN are set/cleared properly.
+        if (withFd) {
+            // FLAG_HAS_FDS and FLAG_HAS_FDS_KNOWN should both be set.
+            assertEquals(Bundle.FLAG_HAS_FDS | Bundle.FLAG_HAS_FDS_KNOWN,
+                    b.mFlags & (Bundle.FLAG_HAS_FDS | Bundle.FLAG_HAS_FDS_KNOWN));
+        } else {
+            // FLAG_HAS_FDS_KNOWN should be set, bot not FLAG_HAS_FDS.
+            assertEquals(Bundle.FLAG_HAS_FDS_KNOWN,
+                    b.mFlags & (Bundle.FLAG_HAS_FDS | Bundle.FLAG_HAS_FDS_KNOWN));
+        }
+
+        // Then, check the contents.
+        assertEquals("abc", b.getString("string"));
+        assertEquals(1, b.getInt("int"));
+
+        // Make sure FLAG_HAS_FDS and FLAG_HAS_FDS_KNOWN are set/cleared properly.
+        if (withFd) {
+            assertEquals(ParcelFileDescriptor.class, b.getParcelable("fd").getClass());
+            assertEquals(3, b.keySet().size());
+        } else {
+            assertEquals(2, b.keySet().size());
+        }
+        assertFalse(b.isParcelled());
+    }
+
+    @Test
+    public void testCreateFromParcel() throws Exception {
+        boolean withFd;
+        Parcel p;
+        Bundle b;
+        int length;
+
+        withFd = false;
+
+        // new Bundle with p
+        p = createBundleParcel(withFd);
+        checkBundle(new Bundle(p), withFd);
+        p.recycle();
+
+        // new Bundle with p and length
+        p = createBundleParcel(withFd);
+        length = p.readInt();
+        checkBundle(new Bundle(p, length), withFd);
+        p.recycle();
+
+        // readFromParcel()
+        p = createBundleParcel(withFd);
+        b = new Bundle();
+        b.readFromParcel(p);
+        checkBundle(b, withFd);
+        p.recycle();
+
+        // Same test with FDs.
+        withFd = true;
+
+        // new Bundle with p
+        p = createBundleParcel(withFd);
+        checkBundle(new Bundle(p), withFd);
+        p.recycle();
+
+        // new Bundle with p and length
+        p = createBundleParcel(withFd);
+        length = p.readInt();
+        checkBundle(new Bundle(p, length), withFd);
+        p.recycle();
+
+        // readFromParcel()
+        p = createBundleParcel(withFd);
+        b = new Bundle();
+        b.readFromParcel(p);
+        checkBundle(b, withFd);
+        p.recycle();
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index 8fd4e31..0294095 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -488,7 +488,7 @@
         bi.noteStartSensorLocked(UID, SENSOR_ID);
 
         clocks.realtime += 111;
-        clocks.uptime += 1111;
+        clocks.uptime += 111;
 
         // Timebase and timer was on so times have increased.
         assertEquals(111_000, timer.getTotalTimeLocked(1000*clocks.realtime, which));
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 6664456..4ea4e38 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.IntDef;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
@@ -27,6 +28,8 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 import java.util.Map;
 
@@ -249,7 +252,7 @@
      * @return A Bitmap containing a representative video frame, which
      *         can be null, if such a frame cannot be retrieved.
      */
-    public Bitmap getFrameAtTime(long timeUs, int option) {
+    public Bitmap getFrameAtTime(long timeUs, @Option int option) {
         if (option < OPTION_PREVIOUS_SYNC ||
             option > OPTION_CLOSEST) {
             throw new IllegalArgumentException("Unsupported option: " + option);
@@ -286,27 +289,27 @@
      * {@link #OPTION_CLOSEST} often has larger performance overhead compared
      * to the other options if there is no sync frame located at timeUs.
      *
-     * @param dst_width expected output bitmap width
-     * @param dst_height expected output bitmap height
-     * @return A Bitmap of size not larger than dst_width by dst_height containing a
+     * @param dstWidth expected output bitmap width
+     * @param dstHeight expected output bitmap height
+     * @return A Bitmap of size not larger than dstWidth by dstHeight containing a
      *         scaled video frame, which can be null, if such a frame cannot be retrieved.
      * @throws IllegalArgumentException if passed in invalid option or width by height
      *         is less than or equal to 0.
      */
     public Bitmap getScaledFrameAtTime(
-            long timeUs, int option, int dst_width, int dst_height) {
+            long timeUs, @Option int option, int dstWidth, int dstHeight) {
         if (option < OPTION_PREVIOUS_SYNC ||
             option > OPTION_CLOSEST) {
             throw new IllegalArgumentException("Unsupported option: " + option);
         }
-        if (dst_width <= 0) {
-            throw new IllegalArgumentException("Invalid width: " + dst_width);
+        if (dstWidth <= 0) {
+            throw new IllegalArgumentException("Invalid width: " + dstWidth);
         }
-        if (dst_height <= 0) {
-            throw new IllegalArgumentException("Invalid height: " + dst_height);
+        if (dstHeight <= 0) {
+            throw new IllegalArgumentException("Invalid height: " + dstHeight);
         }
 
-        return _getFrameAtTime(timeUs, option, dst_width, dst_height);
+        return _getFrameAtTime(timeUs, option, dstWidth, dstHeight);
     }
 
     /**
@@ -427,6 +430,16 @@
      */
     public static final int OPTION_CLOSEST          = 0x03;
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "OPTION_" }, value = {
+            OPTION_PREVIOUS_SYNC,
+            OPTION_NEXT_SYNC,
+            OPTION_CLOSEST_SYNC,
+            OPTION_CLOSEST,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Option {}
+
     /*
      * Do not change these metadata key values without updating their
      * counterparts in include/media/mediametadataretriever.h!
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
index 1240e05..cc7798e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -31,4 +31,5 @@
     void sendRecentsDrawnEvent();
     void sendDockingTopTaskEvent(int dragMode, in Rect initialRect);
     void sendLaunchRecentsEvent();
+    void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index a5c994e..d10e080 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -54,9 +54,11 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
 import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
 import com.android.systemui.recents.events.component.ShowUserToastEvent;
 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -612,6 +614,14 @@
                 }
             });
         }
+
+        // This will catch the cases when a user launches from recents to another app
+        // (and vice versa) that is not in the recents stack (such as home or bugreport) and it
+        // would not reset the wait for transition flag. This will catch it and make sure that the
+        // flag is reset.
+        if (!event.visible) {
+            mImpl.setWaitingForTransitionStart(false);
+        }
     }
 
     /**
@@ -684,6 +694,11 @@
         }
     }
 
+    public final void onBusEvent(LaunchTaskFailedEvent event) {
+        // Reset the transition when tasks fail to launch
+        mImpl.setWaitingForTransitionStart(false);
+    }
+
     public final void onBusEvent(ConfigurationChangedEvent event) {
         // Update the configuration for the Recents component when the activity configuration
         // changes as well
@@ -711,6 +726,25 @@
         }
     }
 
+    public final void onBusEvent(SetWaitingForTransitionStartEvent event) {
+        int processUser = sSystemServicesProxy.getProcessUser();
+        if (sSystemServicesProxy.isSystemUser(processUser)) {
+            mImpl.setWaitingForTransitionStart(event.waitingForTransitionStart);
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mUserToSystemCallbacks.setWaitingForTransitionStartEvent(
+                                event.waitingForTransitionStart);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+    }
+
     /**
      * Attempts to register with the system user.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 7cd93bb..86e93fd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -24,12 +24,11 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityOptions;
+import android.app.ActivityOptions.OnAnimationStartedListener;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -208,6 +207,20 @@
     protected static RecentsTaskLoadPlan sInstanceLoadPlan;
     // Stores the last pinned task time
     protected static long sLastPipTime = -1;
+    // Stores whether we are waiting for a transition to/from recents to start. During this time,
+    // we disallow the user from manually toggling recents until the transition has started.
+    private static boolean mWaitingForTransitionStart = false;
+    // Stores whether or not the user toggled while we were waiting for a transition to/from
+    // recents. In this case, we defer the toggle state until then and apply it immediately after.
+    private static boolean mToggleFollowingTransitionStart = true;
+
+    private ActivityOptions.OnAnimationStartedListener mResetToggleFlagListener =
+            new OnAnimationStartedListener() {
+                @Override
+                public void onAnimationStarted() {
+                    setWaitingForTransitionStart(false);
+                }
+            };
 
     protected Context mContext;
     protected Handler mHandler;
@@ -365,6 +378,11 @@
             return;
         }
 
+        if (mWaitingForTransitionStart) {
+            mToggleFollowingTransitionStart = true;
+            return;
+        }
+
         mDraggingInRecents = false;
         mLaunchedWhileDocking = false;
         mTriggeredFromAltTab = false;
@@ -638,6 +656,18 @@
         }
     }
 
+    public void setWaitingForTransitionStart(boolean waitingForTransitionStart) {
+        if (mWaitingForTransitionStart == waitingForTransitionStart) {
+            return;
+        }
+
+        mWaitingForTransitionStart = waitingForTransitionStart;
+        if (!waitingForTransitionStart && mToggleFollowingTransitionStart) {
+            toggleRecents(DividerView.INVALID_RECENTS_GROW_TARGET);
+        }
+        mToggleFollowingTransitionStart = false;
+    }
+
     /**
      * Returns the preloaded load plan and invalidates it.
      */
@@ -865,8 +895,9 @@
             }
             AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
             specs.toArray(specsArray);
+
             return new Pair<>(ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
-                    specsArray, mHandler, null, this), null);
+                    specsArray, mHandler, mResetToggleFlagListener, this), null);
         } else {
             // Update the destination rect
             Task toTask = new Task();
@@ -884,8 +915,10 @@
                         return Lists.newArrayList(new AppTransitionAnimationSpec(
                                 toTask.key.id, thumbnail, rect));
                     });
+
             return new Pair<>(ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
-                    mHandler, future.getFuture(), null, false /* scaleUp */), future);
+                    mHandler, future.getFuture(), mResetToggleFlagListener, false /* scaleUp */),
+                    future);
         }
     }
 
@@ -991,6 +1024,10 @@
         launchState.launchedToTaskId = runningTaskId;
         launchState.launchedWithAltTab = mTriggeredFromAltTab;
 
+        // Disable toggling of recents between starting the activity and it is visible and the app
+        // has started its transition into recents.
+        setWaitingForTransitionStart(useThumbnailTransition);
+
         // Preload the icon (this will be a null-op if we have preloaded the icon already in
         // preloadRecents())
         preloadIcon(runningTaskId);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
index 3921a20..1285626 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -29,6 +29,7 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.recents.misc.ForegroundThread;
 
@@ -105,4 +106,10 @@
     public void sendLaunchRecentsEvent() throws RemoteException {
         EventBus.getDefault().post(new RecentsActivityStartingEvent());
     }
+
+    @Override
+    public void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart) {
+        EventBus.getDefault().post(new SetWaitingForTransitionStartEvent(
+                waitingForTransitionStart));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
new file mode 100644
index 0000000..d9cf5fb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
@@ -0,0 +1,31 @@
+/*
+ * 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 com.android.systemui.recents.events.component;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when we are setting/resetting the flag to wait for the transition to start.
+ */
+public class SetWaitingForTransitionStartEvent extends EventBus.Event {
+
+    public final boolean waitingForTransitionStart;
+
+    public SetWaitingForTransitionStartEvent(boolean waitingForTransitionStart) {
+        this.waitingForTransitionStart = waitingForTransitionStart;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index ce40f20..127822a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -54,6 +54,7 @@
 import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
@@ -117,31 +118,58 @@
             final Rect windowRect = Recents.getSystemServices().getWindowRect();
             transitionFuture = getAppTransitionFuture(
                     () -> composeAnimationSpecs(task, stackView, destinationStack, windowRect));
-            animStartedListener = () -> {
-                // If we are launching into another task, cancel the previous task's
-                // window transition
-                EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
-                EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
-                stackView.cancelAllTaskViewAnimations();
+            animStartedListener = new OnAnimationStartedListener() {
+                private boolean mHandled;
 
-                if (screenPinningRequested) {
-                    // Request screen pinning after the animation runs
-                    mStartScreenPinningRunnable.taskId = task.key.id;
-                    mHandler.postDelayed(mStartScreenPinningRunnable, 350);
+                @Override
+                public void onAnimationStarted() {
+                    if (mHandled) {
+                        return;
+                    }
+                    mHandled = true;
+
+                    // If we are launching into another task, cancel the previous task's
+                    // window transition
+                    EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+                    EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+                    stackView.cancelAllTaskViewAnimations();
+
+                    if (screenPinningRequested) {
+                        // Request screen pinning after the animation runs
+                        mStartScreenPinningRunnable.taskId = task.key.id;
+                        mHandler.postDelayed(mStartScreenPinningRunnable, 350);
+                    }
+
+                    // Reset the state where we are waiting for the transition to start
+                    EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
                 }
             };
         } else {
             // This is only the case if the task is not on screen (scrolled offscreen for example)
             transitionFuture = null;
-            animStartedListener = () -> {
-                // If we are launching into another task, cancel the previous task's
-                // window transition
-                EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
-                EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
-                stackView.cancelAllTaskViewAnimations();
+            animStartedListener = new OnAnimationStartedListener() {
+                private boolean mHandled;
+
+                @Override
+                public void onAnimationStarted() {
+                    if (mHandled) {
+                        return;
+                    }
+                    mHandled = true;
+
+                    // If we are launching into another task, cancel the previous task's
+                    // window transition
+                    EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+                    EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+                    stackView.cancelAllTaskViewAnimations();
+
+                    // Reset the state where we are waiting for the transition to start
+                    EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
+                }
             };
         }
 
+        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true));
         final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
                 mHandler, transitionFuture != null ? transitionFuture.future : null,
                 animStartedListener, true /* scaleUp */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 472e30c..188f216 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3714,6 +3714,9 @@
      * See {@link AmbientState#setDark}.
      */
     public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
+        if (mAmbientState.isDark() == dark) {
+            return;
+        }
         mAmbientState.setDark(dark);
         if (animate && mAnimationsEnabled) {
             mDarkNeedsAnimation = true;
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 814e4be..c23757f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -214,8 +214,7 @@
             Context.BIND_AUTO_CREATE
             | Context.BIND_NOT_VISIBLE
             | Context.BIND_NOT_FOREGROUND
-            | Context.BIND_IMPORTANT_BACKGROUND
-            | Context.BIND_SHOWING_UI;
+            | Context.BIND_IMPORTANT_BACKGROUND;
 
     /**
      * Binding flags used only while the {@link InputMethodService} is showing window.
@@ -223,7 +222,8 @@
     private static final int IME_VISIBLE_BIND_FLAGS =
             Context.BIND_AUTO_CREATE
             | Context.BIND_TREAT_LIKE_ACTIVITY
-            | Context.BIND_FOREGROUND_SERVICE;
+            | Context.BIND_FOREGROUND_SERVICE
+            | Context.BIND_SHOWING_UI;
 
     @Retention(SOURCE)
     @IntDef({HardKeyboardBehavior.WIRELESS_AFFORDANCE, HardKeyboardBehavior.WIRED_AFFORDANCE})
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 80d39f5..13e3ae6 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -598,7 +598,7 @@
             Slog.w(TAG, "bind service: " + info.getId());
         }
         if (!bindCurrentSpellCheckerService(serviceIntent, connection,
-                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)) {
+                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT_BACKGROUND)) {
             Slog.e(TAG, "Failed to get a spell checker service.");
             return null;
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bcad2fc..161bf9a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13323,7 +13323,6 @@
                 final ActivityRecord r = ActivityRecord.isInStackLocked(token);
                 if (r != null) {
                     final ActivityOptions activityOptions = r.pendingOptions;
-                    r.pendingOptions = null;
                     return activityOptions == null ? null : activityOptions.toBundle();
                 }
                 return null;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index cdf25cf..436ea3c 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -543,7 +543,9 @@
                     loadPropertiesFromResource(context, mProperties);
                     String lpp_profile = mProperties.getProperty("LPP_PROFILE");
                     // set the persist property LPP_PROFILE for the value
-                    SystemProperties.set(LPP_PROFILE, lpp_profile);
+                    if (lpp_profile != null) {
+                        SystemProperties.set(LPP_PROFILE, lpp_profile);
+                    }
                 } else {
                     // reset the persist property
                     SystemProperties.set(LPP_PROFILE, "");
@@ -1052,8 +1054,15 @@
                 // download tasks overrun.
                 synchronized (mLock) {
                     if (mDownloadXtraWakeLock.isHeld()) {
-                        mDownloadXtraWakeLock.release();
-                        if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
+                        // This wakelock may have time-out, if a timeout was specified.
+                        // Catch (and ignore) any timeout exceptions.
+                        try {
+                            mDownloadXtraWakeLock.release();
+                            if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
+                        } catch (Exception e) {
+                            Log.i(TAG, "Wakelock timeout & release race exception in "
+                                    + "handleDownloadXtraData()", e);
+                        }
                     } else {
                         Log.e(TAG, "WakeLock expired before release in "
                                 + "handleDownloadXtraData()");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index aead98b..b68630e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2722,14 +2722,17 @@
             //delete tmp files
             deleteTempPackageFiles();
 
+            final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get();
+
             // Remove any shared userIDs that have no associated packages
             mSettings.pruneSharedUsersLPw();
             final long systemScanTime = SystemClock.uptimeMillis() - startTime;
             final int systemPackagesCount = mPackages.size();
             Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
                     + " ms, packageCount: " + systemPackagesCount
-                    + " ms, timePerPackage: "
-                    + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount));
+                    + " , timePerPackage: "
+                    + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
+                    + " , cached: " + cachedSystemApps);
             if (mIsUpgrade && systemPackagesCount > 0) {
                 MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time",
                         ((int) systemScanTime) / systemPackagesCount);
@@ -2820,12 +2823,16 @@
                 // This must be done last to ensure all stubs are replaced or disabled.
                 decompressSystemApplications(stubSystemApps, scanFlags);
 
+                final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get()
+                                - cachedSystemApps;
+
                 final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
                 final int dataPackagesCount = mPackages.size() - systemPackagesCount;
                 Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
                         + " ms, packageCount: " + dataPackagesCount
-                        + " ms, timePerPackage: "
-                        + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount));
+                        + " , timePerPackage: "
+                        + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+                        + " , cached: " + cachedNonSystemApps);
                 if (mIsUpgrade && dataPackagesCount > 0) {
                     MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time",
                             ((int) dataScanTime) / dataPackagesCount);
diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
index 0508fdf..a12c2c4 100644
--- a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
+++ b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
@@ -32,7 +32,7 @@
 // the surface control.
 //
 // See cts/hostsidetests/../../SurfaceTraceReceiver.java for parsing side.
-class RemoteSurfaceTrace extends SurfaceControl {
+class RemoteSurfaceTrace extends SurfaceControlWithBackground {
     static final String TAG = "RemoteSurfaceTrace";
 
     final FileDescriptor mWriteFd;
@@ -41,7 +41,8 @@
     final WindowManagerService mService;
     final WindowState mWindow;
 
-    RemoteSurfaceTrace(FileDescriptor fd, SurfaceControl wrapped, WindowState window) {
+    RemoteSurfaceTrace(FileDescriptor fd, SurfaceControlWithBackground wrapped,
+            WindowState window) {
         super(wrapped);
 
         mWriteFd = fd;
diff --git a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
new file mode 100644
index 0000000..f5ef2e6
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
@@ -0,0 +1,306 @@
+/*
+ * 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 com.android.server.wm;
+
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+/**
+ * SurfaceControl extension that has background sized to match its container.
+ */
+class SurfaceControlWithBackground extends SurfaceControl {
+    // SurfaceControl that holds the background behind opaque letterboxed app windows.
+    private SurfaceControl mBackgroundControl;
+
+    // Flags that define whether the background should be shown.
+    private boolean mOpaque;
+    private boolean mVisible;
+
+    // Way to communicate with corresponding window.
+    private WindowSurfaceController mWindowSurfaceController;
+
+    // Rect to hold task bounds when computing metrics for background.
+    private Rect mTmpContainerRect = new Rect();
+
+    // Last metrics applied to the main SurfaceControl.
+    private float mLastWidth, mLastHeight;
+    private float mLastDsDx = 1, mLastDsDy = 1;
+    private float mLastX, mLastY;
+
+    public SurfaceControlWithBackground(SurfaceControlWithBackground other) {
+        super(other);
+        mBackgroundControl = other.mBackgroundControl;
+        mOpaque = other.mOpaque;
+        mVisible = other.mVisible;
+        mWindowSurfaceController = other.mWindowSurfaceController;
+    }
+
+    public SurfaceControlWithBackground(SurfaceSession s, String name, int w, int h, int format,
+            int flags, int windowType, int ownerUid,
+            WindowSurfaceController windowSurfaceController) throws OutOfResourcesException {
+        super(s, name, w, h, format, flags, windowType, ownerUid);
+
+        // We should only show background when the window is letterboxed in a task.
+        if (!windowSurfaceController.mAnimator.mWin.isLetterboxedAppWindow()) {
+            return;
+        }
+        mWindowSurfaceController = windowSurfaceController;
+        mLastWidth = w;
+        mLastHeight = h;
+        mOpaque = (flags & SurfaceControl.OPAQUE) != 0;
+        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+        mBackgroundControl = new SurfaceControl(s, "Background for - " + name,
+                mTmpContainerRect.width(), mTmpContainerRect.height(), PixelFormat.OPAQUE,
+                flags | SurfaceControl.FX_SURFACE_DIM);
+    }
+
+    @Override
+    public void setAlpha(float alpha) {
+        super.setAlpha(alpha);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.setAlpha(alpha);
+    }
+
+    @Override
+    public void setLayer(int zorder) {
+        super.setLayer(zorder);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        // TODO: Use setRelativeLayer(Integer.MIN_VALUE) when it's fixed.
+        mBackgroundControl.setLayer(zorder - 1);
+    }
+
+    @Override
+    public void setPosition(float x, float y) {
+        super.setPosition(x, y);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mLastX = x;
+        mLastY = y;
+        updateBgPosition();
+    }
+
+    private void updateBgPosition() {
+        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+        final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
+        final float offsetX = (mTmpContainerRect.left - winFrame.left) * mLastDsDx;
+        final float offsetY = (mTmpContainerRect.top - winFrame.top) * mLastDsDy;
+        mBackgroundControl.setPosition(mLastX + offsetX, mLastY + offsetY);
+    }
+
+    @Override
+    public void setSize(int w, int h) {
+        super.setSize(w, h);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mLastWidth = w;
+        mLastHeight = h;
+        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+        mBackgroundControl.setSize(mTmpContainerRect.width(), mTmpContainerRect.height());
+    }
+
+    @Override
+    public void setWindowCrop(Rect crop) {
+        super.setWindowCrop(crop);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        if (crop.width() < mLastWidth || crop.height() < mLastHeight) {
+            // We're animating and cropping window, compute the appropriate crop for background.
+            calculateBgCrop(crop);
+            mBackgroundControl.setWindowCrop(mTmpContainerRect);
+        } else {
+            // When not animating just set crop to container rect.
+            mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+            mBackgroundControl.setWindowCrop(mTmpContainerRect);
+        }
+    }
+
+    @Override
+    public void setFinalCrop(Rect crop) {
+        super.setFinalCrop(crop);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        if (crop.width() < mLastWidth || crop.height() < mLastHeight) {
+            // We're animating and cropping window, compute the appropriate crop for background.
+            calculateBgCrop(crop);
+            mBackgroundControl.setFinalCrop(mTmpContainerRect);
+        } else {
+            // When not animating just set crop to container rect.
+            mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+            mBackgroundControl.setFinalCrop(mTmpContainerRect);
+        }
+    }
+
+    /** Compute background crop based on current animation progress for main surface control. */
+    private void calculateBgCrop(Rect crop) {
+        // Track overall progress of animation by computing cropped portion of status bar.
+        final Rect contentInsets = mWindowSurfaceController.mAnimator.mWin.mContentInsets;
+        float d = contentInsets.top == 0 ? 0 : (float) crop.top / contentInsets.top;
+
+        // Compute additional offset for the background when app window is positioned not at (0,0).
+        // E.g. landscape with navigation bar on the left.
+        final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
+        final int offsetX = (int) (winFrame.left * mLastDsDx * d + 0.5);
+        final int offsetY = (int) (winFrame.top * mLastDsDy * d + 0.5);
+
+        // Compute new scaled width and height for background that will depend on current animation
+        // progress. Those consist of current crop rect for the main surface + scaled areas outside
+        // of letterboxed area.
+        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+        final int backgroundWidth =
+                (int) (crop.width() + (mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
+        final int backgroundHeight =
+                (int) (crop.height() + (mTmpContainerRect.height() - mLastHeight) * (1 - d) + 0.5);
+
+        mTmpContainerRect.set(crop);
+        // Make sure that part of background to left/top is visible and scaled.
+        mTmpContainerRect.offset(offsetX, offsetY);
+        // Set correct width/height, so that area to right/bottom is cropped properly.
+        mTmpContainerRect.right = mTmpContainerRect.left + backgroundWidth;
+        mTmpContainerRect.bottom = mTmpContainerRect.top + backgroundHeight;
+    }
+
+    @Override
+    public void setLayerStack(int layerStack) {
+        super.setLayerStack(layerStack);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.setLayerStack(layerStack);
+    }
+
+    @Override
+    public void setOpaque(boolean isOpaque) {
+        super.setOpaque(isOpaque);
+        mOpaque = isOpaque;
+        updateBackgroundVisibility();
+    }
+
+    @Override
+    public void setSecure(boolean isSecure) {
+        super.setSecure(isSecure);
+    }
+
+    @Override
+    public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
+        super.setMatrix(dsdx, dtdx, dtdy, dsdy);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.setMatrix(dsdx, dtdx, dtdy, dsdy);
+        mLastDsDx = dsdx;
+        mLastDsDy = dsdy;
+        updateBgPosition();
+    }
+
+    @Override
+    public void hide() {
+        super.hide();
+        mVisible = false;
+        updateBackgroundVisibility();
+    }
+
+    @Override
+    public void show() {
+        super.show();
+        mVisible = true;
+        updateBackgroundVisibility();
+    }
+
+    @Override
+    public void destroy() {
+        super.destroy();
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.destroy();
+    }
+
+    @Override
+    public void release() {
+        super.release();
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.release();
+    }
+
+    @Override
+    public void setTransparentRegionHint(Region region) {
+        super.setTransparentRegionHint(region);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.setTransparentRegionHint(region);
+    }
+
+    @Override
+    public void deferTransactionUntil(IBinder handle, long frame) {
+        super.deferTransactionUntil(handle, frame);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.deferTransactionUntil(handle, frame);
+    }
+
+    @Override
+    public void deferTransactionUntil(Surface barrier, long frame) {
+        super.deferTransactionUntil(barrier, frame);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.deferTransactionUntil(barrier, frame);
+    }
+
+    private void updateBackgroundVisibility() {
+        if (mBackgroundControl == null) {
+            return;
+        }
+        if (mOpaque && mVisible) {
+            mBackgroundControl.show();
+        } else {
+            mBackgroundControl.hide();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0c217ca..90a2892 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3244,6 +3244,15 @@
         return !isInMultiWindowMode();
     }
 
+    /** @return true when the window is in fullscreen task, but has non-fullscreen bounds set. */
+    boolean isLetterboxedAppWindow() {
+        final Task task = getTask();
+        final boolean taskIsFullscreen = task != null && task.isFullscreen();
+        final boolean appWindowIsFullscreen = mAppToken != null && !mAppToken.hasBounds();
+
+        return taskIsFullscreen && !appWindowIsFullscreen;
+    }
+
     /** Returns the appropriate bounds to use for computing frames. */
     private void getContainerBounds(Rect outBounds) {
         if (isInMultiWindowMode()) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0cc505e..86265c29 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1200,7 +1200,8 @@
         if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame="
                 + w.mDecorFrame + " mSystemDecorRect=" + mSystemDecorRect);
 
-        final boolean fullscreen = w.fillsDisplay();
+        final Task task = w.getTask();
+        final boolean fullscreen = w.fillsDisplay() || (task != null && task.isFullscreen());
         final boolean isFreeformResizing =
                 w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
 
@@ -1526,6 +1527,19 @@
         }
     }
 
+    /**
+     * Get rect of the task this window is currently in. If there is no task, rect will be set to
+     * empty.
+     */
+    void getContainerRect(Rect rect) {
+        final Task task = mWin.getTask();
+        if (task != null) {
+            task.getDimBounds(rect);
+        } else {
+            rect.left = rect.top = rect.right = rect.bottom = 0;
+        }
+    }
+
     void prepareSurfaceLocked(final boolean recoveringMemory) {
         final WindowState w = mWin;
         if (!hasSurface()) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 1728cfb..110d5cb 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -50,7 +50,7 @@
 
     final WindowStateAnimator mAnimator;
 
-    private SurfaceControl mSurfaceControl;
+    private SurfaceControlWithBackground mSurfaceControl;
 
     // Should only be set from within setShown().
     private boolean mSurfaceShown = false;
@@ -97,15 +97,10 @@
         mWindowType = windowType;
         mWindowSession = win.mSession;
 
-        if (DEBUG_SURFACE_TRACE) {
-            mSurfaceControl = new SurfaceTrace(
-                    s, name, w, h, format, flags, windowType, ownerUid);
-        } else {
-            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
-            mSurfaceControl = new SurfaceControl(
-                    s, name, w, h, format, flags, windowType, ownerUid);
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        }
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
+        mSurfaceControl = new SurfaceControlWithBackground(
+                s, name, w, h, format, flags, windowType, ownerUid, this);
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
         if (mService.mRoot.mSurfaceTraceEnabled) {
             mSurfaceControl = new RemoteSurfaceTrace(
@@ -118,7 +113,7 @@
     }
 
     void removeRemoteTrace() {
-        mSurfaceControl = new SurfaceControl(mSurfaceControl);
+        mSurfaceControl = new SurfaceControlWithBackground(mSurfaceControl);
     }
 
 
@@ -291,30 +286,30 @@
         mSurfaceControl.setGeometryAppliesWithResize();
     }
 
-    void setMatrixInTransaction(float dsdx, float dtdx, float dsdy, float dtdy,
+    void setMatrixInTransaction(float dsdx, float dtdx, float dtdy, float dsdy,
             boolean recoveringMemory) {
         final boolean matrixChanged = mLastDsdx != dsdx || mLastDtdx != dtdx ||
-                                      mLastDsdy != dsdy || mLastDtdy != dtdy;
+                                      mLastDtdy != dtdy || mLastDsdy != dsdy;
         if (!matrixChanged) {
             return;
         }
 
         mLastDsdx = dsdx;
         mLastDtdx = dtdx;
-        mLastDsdy = dsdy;
         mLastDtdy = dtdy;
+        mLastDsdy = dsdy;
 
         try {
             if (SHOW_TRANSACTIONS) logSurface(
-                    "MATRIX [" + dsdx + "," + dtdx + "," + dsdy + "," + dtdy + "]", null);
+                    "MATRIX [" + dsdx + "," + dtdx + "," + dtdy + "," + dsdy + "]", null);
             mSurfaceControl.setMatrix(
-                    dsdx, dtdx, dsdy, dtdy);
+                    dsdx, dtdx, dtdy, dsdy);
         } catch (RuntimeException e) {
             // If something goes wrong with the surface (such
             // as running out of memory), don't take down the
             // entire system.
             Slog.e(TAG, "Error setting matrix on surface surface" + title
-                    + " MATRIX [" + dsdx + "," + dtdx + "," + dsdy + "," + dtdy + "]", null);
+                    + " MATRIX [" + dsdx + "," + dtdx + "," + dtdy + "," + dsdy + "]", null);
             if (!recoveringMemory) {
                 mAnimator.reclaimSomeSurfaceMemory("matrix", true);
             }
@@ -421,6 +416,10 @@
         }
     }
 
+    void getContainerRect(Rect rect) {
+        mAnimator.getContainerRect(rect);
+    }
+
     boolean showRobustlyInTransaction() {
         if (SHOW_TRANSACTIONS) logSurface(
                 "SHOW (performLayout)", null);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2e7b19a..cee77fb 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1654,8 +1654,7 @@
      * @hide
      */
     public String getNetworkCountryIso(int subId) {
-        int phoneId = SubscriptionManager.getPhoneId(subId);
-        return getNetworkCountryIsoForPhone(phoneId);
+        return getNetworkCountryIsoForPhone(getPhoneId(subId));
     }
 
     /**
@@ -1670,7 +1669,14 @@
      */
     /** {@hide} */
     public String getNetworkCountryIsoForPhone(int phoneId) {
-        return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return "";
+            return telephony.getNetworkCountryIsoForPhone(phoneId);
+        } catch (RemoteException ex) {
+                return "";
+        }
     }
 
     /** Network type is unknown */
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a0e5b7b..654adb2 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -377,6 +377,13 @@
     Bundle getCellLocation(String callingPkg);
 
     /**
+     * Returns the ISO country code equivalent of the current registered
+     * operator's MCC (Mobile Country Code).
+     * @see android.telephony.TelephonyManager#getNetworkCountryIso
+     */
+    String getNetworkCountryIsoForPhone(int phoneId);
+
+    /**
      * Returns the neighboring cell information of the device.
      */
     List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg);
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 9a37913..a5783a5 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -268,8 +268,7 @@
       continue;
     }
 
-    if (!parser->element_namespace().empty() ||
-        parser->element_name() != "resources") {
+    if (!parser->element_namespace().empty() || parser->element_name() != "resources") {
       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
                    << "root element must be <resources>");
       return false;
@@ -328,8 +327,7 @@
     parsed_resource.comment = std::move(comment);
 
     // Extract the product name if it exists.
-    if (Maybe<StringPiece> maybe_product =
-            xml::FindNonEmptyAttribute(parser, "product")) {
+    if (Maybe<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
       parsed_resource.product = maybe_product.value().to_string();
     }
 
@@ -348,10 +346,8 @@
   for (const ResourceName& stripped_resource : stripped_resources) {
     if (!table_->FindResource(stripped_resource)) {
       // Failed to find the resource.
-      diag_->Error(DiagMessage(source_)
-                   << "resource '" << stripped_resource
-                   << "' "
-                      "was filtered out but no product variant remains");
+      diag_->Error(DiagMessage(source_) << "resource '" << stripped_resource
+                                        << "' was filtered out but no product variant remains");
       error = true;
     }
   }
@@ -589,7 +585,7 @@
     // This can only be a StyledString.
     std::unique_ptr<StyledString> styled_string =
         util::make_unique<StyledString>(table_->string_pool.MakeRef(
-            style_string, StringPool::Context(StringPool::Context::kStylePriority, config_)));
+            style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_)));
     styled_string->untranslatable_sections = std::move(untranslatable_sections);
     return std::move(styled_string);
   }
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index d47a529..1683c64 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -117,7 +117,7 @@
   StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
   ASSERT_THAT(str, NotNull());
 
-  EXPECT_THAT(*str->value->str, Eq("This is my aunt\u2019s fickle string"));
+  EXPECT_THAT(str->value->value, Eq("This is my aunt\u2019s fickle string"));
   EXPECT_THAT(str->value->spans, SizeIs(2));
   EXPECT_THAT(str->untranslatable_sections, IsEmpty());
 
@@ -190,7 +190,7 @@
 
   StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
   ASSERT_THAT(str, NotNull());
-  EXPECT_THAT(*str->value->str, Eq("There are %1$d apples"));
+  EXPECT_THAT(str->value->value, Eq("There are %1$d apples"));
   ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
 
   // We expect indices and lengths that span to include the whitespace
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 6e6a2ba..f193fe0 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -700,7 +700,7 @@
           spans++;
         }
         return util::make_unique<StyledString>(dst_pool->MakeRef(
-            style_str, StringPool::Context(StringPool::Context::kStylePriority, config)));
+            style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
       } else {
         if (type != ResourceType::kString && util::StartsWith(str, "res/")) {
           // This must be a FileReference.
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 947e091..eb59175 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -253,10 +253,9 @@
 }
 
 void StyledString::Print(std::ostream* out) const {
-  *out << "(styled string) \"" << *value->str << "\"";
+  *out << "(styled string) \"" << value->value << "\"";
   for (const StringPool::Span& span : value->spans) {
-    *out << " " << *span.name << ":" << span.first_char << ","
-         << span.last_char;
+    *out << " " << *span.name << ":" << span.first_char << "," << span.last_char;
   }
 }
 
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index 57da5f0..705b1ab 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -27,7 +27,7 @@
 #include "util/BigBuffer.h"
 #include "util/Util.h"
 
-using android::StringPiece;
+using ::android::StringPiece;
 
 namespace aapt {
 
@@ -75,9 +75,14 @@
   return &entry_->value;
 }
 
-const std::string& StringPool::Ref::operator*() const { return entry_->value; }
+const std::string& StringPool::Ref::operator*() const {
+  return entry_->value;
+}
 
-size_t StringPool::Ref::index() const { return entry_->index; }
+size_t StringPool::Ref::index() const {
+  // Account for the styles, which *always* come first.
+  return entry_->pool_->styles_.size() + entry_->index_;
+}
 
 const StringPool::Context& StringPool::Ref::GetContext() const {
   return entry_->context;
@@ -104,8 +109,7 @@
   }
 }
 
-StringPool::StyleRef& StringPool::StyleRef::operator=(
-    const StringPool::StyleRef& rhs) {
+StringPool::StyleRef& StringPool::StyleRef::operator=(const StringPool::StyleRef& rhs) {
   if (rhs.entry_ != nullptr) {
     rhs.entry_->ref_++;
   }
@@ -118,7 +122,7 @@
 }
 
 bool StringPool::StyleRef::operator==(const StyleRef& rhs) const {
-  if (entry_->str != rhs.entry_->str) {
+  if (entry_->value != rhs.entry_->value) {
     return false;
   }
 
@@ -137,7 +141,9 @@
   return true;
 }
 
-bool StringPool::StyleRef::operator!=(const StyleRef& rhs) const { return !operator==(rhs); }
+bool StringPool::StyleRef::operator!=(const StyleRef& rhs) const {
+  return !operator==(rhs);
+}
 
 const StringPool::StyleEntry* StringPool::StyleRef::operator->() const {
   return entry_;
@@ -147,23 +153,24 @@
   return *entry_;
 }
 
-size_t StringPool::StyleRef::index() const { return entry_->str.index(); }
+size_t StringPool::StyleRef::index() const {
+  return entry_->index_;
+}
 
 const StringPool::Context& StringPool::StyleRef::GetContext() const {
-  return entry_->str.GetContext();
+  return entry_->context;
 }
 
 StringPool::Ref StringPool::MakeRef(const StringPiece& str) {
   return MakeRefImpl(str, Context{}, true);
 }
 
-StringPool::Ref StringPool::MakeRef(const StringPiece& str,
-                                    const Context& context) {
+StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context) {
   return MakeRefImpl(str, context, true);
 }
 
-StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str,
-                                        const Context& context, bool unique) {
+StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context,
+                                        bool unique) {
   if (unique) {
     auto iter = indexed_strings_.find(str);
     if (iter != std::end(indexed_strings_)) {
@@ -171,82 +178,87 @@
     }
   }
 
-  Entry* entry = new Entry();
+  std::unique_ptr<Entry> entry(new Entry());
   entry->value = str.to_string();
   entry->context = context;
-  entry->index = strings_.size();
+  entry->index_ = strings_.size();
   entry->ref_ = 0;
-  strings_.emplace_back(entry);
-  indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
-  return Ref(entry);
+  entry->pool_ = this;
+
+  Entry* borrow = entry.get();
+  strings_.emplace_back(std::move(entry));
+  indexed_strings_.insert(std::make_pair(StringPiece(borrow->value), borrow));
+  return Ref(borrow);
 }
 
 StringPool::StyleRef StringPool::MakeRef(const StyleString& str) {
   return MakeRef(str, Context{});
 }
 
-StringPool::StyleRef StringPool::MakeRef(const StyleString& str,
-                                         const Context& context) {
-  Entry* entry = new Entry();
+StringPool::StyleRef StringPool::MakeRef(const StyleString& str, const Context& context) {
+  std::unique_ptr<StyleEntry> entry(new StyleEntry());
   entry->value = str.str;
   entry->context = context;
-  entry->index = strings_.size();
+  entry->index_ = styles_.size();
   entry->ref_ = 0;
-  strings_.emplace_back(entry);
-  indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
-
-  StyleEntry* style_entry = new StyleEntry();
-  style_entry->str = Ref(entry);
   for (const aapt::Span& span : str.spans) {
-    style_entry->spans.emplace_back(
-        Span{MakeRef(span.name), span.first_char, span.last_char});
+    entry->spans.emplace_back(Span{MakeRef(span.name), span.first_char, span.last_char});
   }
-  style_entry->ref_ = 0;
-  styles_.emplace_back(style_entry);
-  return StyleRef(style_entry);
+
+  StyleEntry* borrow = entry.get();
+  styles_.emplace_back(std::move(entry));
+  return StyleRef(borrow);
 }
 
 StringPool::StyleRef StringPool::MakeRef(const StyleRef& ref) {
-  Entry* entry = new Entry();
-  entry->value = *ref.entry_->str;
-  entry->context = ref.entry_->str.entry_->context;
-  entry->index = strings_.size();
+  std::unique_ptr<StyleEntry> entry(new StyleEntry());
+  entry->value = ref.entry_->value;
+  entry->context = ref.entry_->context;
+  entry->index_ = styles_.size();
   entry->ref_ = 0;
-  strings_.emplace_back(entry);
-  indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
-
-  StyleEntry* style_entry = new StyleEntry();
-  style_entry->str = Ref(entry);
   for (const Span& span : ref.entry_->spans) {
-    style_entry->spans.emplace_back(
-        Span{MakeRef(*span.name), span.first_char, span.last_char});
+    entry->spans.emplace_back(Span{MakeRef(*span.name), span.first_char, span.last_char});
   }
-  style_entry->ref_ = 0;
-  styles_.emplace_back(style_entry);
-  return StyleRef(style_entry);
+
+  StyleEntry* borrow = entry.get();
+  styles_.emplace_back(std::move(entry));
+  return StyleRef(borrow);
+}
+
+void StringPool::ReAssignIndices() {
+  // Assign the style indices.
+  const size_t style_len = styles_.size();
+  for (size_t index = 0; index < style_len; index++) {
+    styles_[index]->index_ = index;
+  }
+
+  // Assign the string indices.
+  const size_t string_len = strings_.size();
+  for (size_t index = 0; index < string_len; index++) {
+    strings_[index]->index_ = index;
+  }
 }
 
 void StringPool::Merge(StringPool&& pool) {
-  indexed_strings_.insert(pool.indexed_strings_.begin(),
-                          pool.indexed_strings_.end());
-  pool.indexed_strings_.clear();
-  std::move(pool.strings_.begin(), pool.strings_.end(),
-            std::back_inserter(strings_));
-  pool.strings_.clear();
-  std::move(pool.styles_.begin(), pool.styles_.end(),
-            std::back_inserter(styles_));
-  pool.styles_.clear();
-
-  // Assign the indices.
-  const size_t len = strings_.size();
-  for (size_t index = 0; index < len; index++) {
-    strings_[index]->index = index;
+  // First, change the owning pool for the incoming strings.
+  for (std::unique_ptr<Entry>& entry : pool.strings_) {
+    entry->pool_ = this;
   }
+
+  // Now move the styles, strings, and indices over.
+  std::move(pool.styles_.begin(), pool.styles_.end(), std::back_inserter(styles_));
+  pool.styles_.clear();
+  std::move(pool.strings_.begin(), pool.strings_.end(), std::back_inserter(strings_));
+  pool.strings_.clear();
+  indexed_strings_.insert(pool.indexed_strings_.begin(), pool.indexed_strings_.end());
+  pool.indexed_strings_.clear();
+
+  ReAssignIndices();
 }
 
-void StringPool::HintWillAdd(size_t stringCount, size_t styleCount) {
-  strings_.reserve(strings_.size() + stringCount);
-  styles_.reserve(styles_.size() + styleCount);
+void StringPool::HintWillAdd(size_t string_count, size_t style_count) {
+  strings_.reserve(strings_.size() + string_count);
+  styles_.reserve(styles_.size() + style_count);
 }
 
 void StringPool::Prune() {
@@ -262,47 +274,42 @@
 
   auto end_iter2 =
       std::remove_if(strings_.begin(), strings_.end(),
-                     [](const std::unique_ptr<Entry>& entry) -> bool {
-                       return entry->ref_ <= 0;
-                     });
+                     [](const std::unique_ptr<Entry>& entry) -> bool { return entry->ref_ <= 0; });
+  auto end_iter3 = std::remove_if(
+      styles_.begin(), styles_.end(),
+      [](const std::unique_ptr<StyleEntry>& entry) -> bool { return entry->ref_ <= 0; });
 
-  auto end_iter3 =
-      std::remove_if(styles_.begin(), styles_.end(),
-                     [](const std::unique_ptr<StyleEntry>& entry) -> bool {
-                       return entry->ref_ <= 0;
-                     });
-
-  // Remove the entries at the end or else we'll be accessing
-  // a deleted string from the StyleEntry.
+  // Remove the entries at the end or else we'll be accessing a deleted string from the StyleEntry.
   strings_.erase(end_iter2, strings_.end());
   styles_.erase(end_iter3, styles_.end());
 
-  // Reassign the indices.
-  const size_t len = strings_.size();
-  for (size_t index = 0; index < len; index++) {
-    strings_[index]->index = index;
+  ReAssignIndices();
+}
+
+template <typename E>
+static void SortEntries(
+    std::vector<std::unique_ptr<E>>& entries,
+    const std::function<int(const StringPool::Context&, const StringPool::Context&)>& cmp) {
+  using UEntry = std::unique_ptr<E>;
+
+  if (cmp != nullptr) {
+    std::sort(entries.begin(), entries.end(), [&cmp](const UEntry& a, const UEntry& b) -> bool {
+      int r = cmp(a->context, b->context);
+      if (r == 0) {
+        r = a->value.compare(b->value);
+      }
+      return r < 0;
+    });
+  } else {
+    std::sort(entries.begin(), entries.end(),
+              [](const UEntry& a, const UEntry& b) -> bool { return a->value < b->value; });
   }
 }
 
-void StringPool::Sort(
-    const std::function<bool(const Entry&, const Entry&)>& cmp) {
-  std::sort(
-      strings_.begin(), strings_.end(),
-      [&cmp](const std::unique_ptr<Entry>& a,
-             const std::unique_ptr<Entry>& b) -> bool { return cmp(*a, *b); });
-
-  // Assign the indices.
-  const size_t len = strings_.size();
-  for (size_t index = 0; index < len; index++) {
-    strings_[index]->index = index;
-  }
-
-  // Reorder the styles.
-  std::sort(styles_.begin(), styles_.end(),
-            [](const std::unique_ptr<StyleEntry>& lhs,
-               const std::unique_ptr<StyleEntry>& rhs) -> bool {
-              return lhs->str.index() < rhs->str.index();
-            });
+void StringPool::Sort(const std::function<int(const Context&, const Context&)>& cmp) {
+  SortEntries(styles_, cmp);
+  SortEntries(strings_, cmp);
+  ReAssignIndices();
 }
 
 template <typename T>
@@ -327,60 +334,31 @@
   return length > kMaxSize ? 2 : 1;
 }
 
-bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
-  const size_t start_index = out->size();
-  android::ResStringPool_header* header =
-      out->NextBlock<android::ResStringPool_header>();
-  header->header.type = android::RES_STRING_POOL_TYPE;
-  header->header.headerSize = sizeof(*header);
-  header->stringCount = pool.size();
+static void EncodeString(const std::string& str, const bool utf8, BigBuffer* out) {
   if (utf8) {
-    header->flags |= android::ResStringPool_header::UTF8_FLAG;
-  }
+    const std::string& encoded = str;
+    const ssize_t utf16_length =
+        utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str.data()), str.size());
+    CHECK(utf16_length >= 0);
 
-  uint32_t* indices =
-      pool.size() != 0 ? out->NextBlock<uint32_t>(pool.size()) : nullptr;
+    const size_t total_size = EncodedLengthUnits<char>(utf16_length) +
+                              EncodedLengthUnits<char>(encoded.length()) + encoded.size() + 1;
 
-  uint32_t* style_indices = nullptr;
-  if (!pool.styles_.empty()) {
-    header->styleCount = pool.styles_.back()->str.index() + 1;
-    style_indices = out->NextBlock<uint32_t>(header->styleCount);
-  }
+    char* data = out->NextBlock<char>(total_size);
 
-  const size_t before_strings_index = out->size();
-  header->stringsStart = before_strings_index - start_index;
+    // First encode the UTF16 string length.
+    data = EncodeLength(data, utf16_length);
 
-  for (const auto& entry : pool) {
-    *indices = out->size() - before_strings_index;
-    indices++;
-
-    if (utf8) {
-      const std::string& encoded = entry->value;
-      const ssize_t utf16_length = utf8_to_utf16_length(
-          reinterpret_cast<const uint8_t*>(entry->value.data()),
-          entry->value.size());
-      CHECK(utf16_length >= 0);
-
-      const size_t total_size = EncodedLengthUnits<char>(utf16_length) +
-                                EncodedLengthUnits<char>(encoded.length()) +
-                                encoded.size() + 1;
-
-      char* data = out->NextBlock<char>(total_size);
-
-      // First encode the UTF16 string length.
-      data = EncodeLength(data, utf16_length);
-
-      // Now encode the size of the real UTF8 string.
-      data = EncodeLength(data, encoded.length());
-      strncpy(data, encoded.data(), encoded.size());
+    // Now encode the size of the real UTF8 string.
+    data = EncodeLength(data, encoded.length());
+    strncpy(data, encoded.data(), encoded.size());
 
     } else {
-      const std::u16string encoded = util::Utf8ToUtf16(entry->value);
+      const std::u16string encoded = util::Utf8ToUtf16(str);
       const ssize_t utf16_length = encoded.size();
 
       // Total number of 16-bit words to write.
-      const size_t total_size =
-          EncodedLengthUnits<char16_t>(utf16_length) + encoded.size() + 1;
+      const size_t total_size = EncodedLengthUnits<char16_t>(utf16_length) + encoded.size() + 1;
 
       char16_t* data = out->NextBlock<char16_t>(total_size);
 
@@ -395,31 +373,55 @@
       // The null-terminating character is already here due to the block of data
       // being set to 0s on allocation.
     }
+}
+
+bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
+  const size_t start_index = out->size();
+  android::ResStringPool_header* header = out->NextBlock<android::ResStringPool_header>();
+  header->header.type = util::HostToDevice16(android::RES_STRING_POOL_TYPE);
+  header->header.headerSize = util::HostToDevice16(sizeof(*header));
+  header->stringCount = util::HostToDevice32(pool.size());
+  header->styleCount = util::HostToDevice32(pool.styles_.size());
+  if (utf8) {
+    header->flags |= android::ResStringPool_header::UTF8_FLAG;
+  }
+
+  uint32_t* indices = pool.size() != 0 ? out->NextBlock<uint32_t>(pool.size()) : nullptr;
+  uint32_t* style_indices =
+      pool.styles_.size() != 0 ? out->NextBlock<uint32_t>(pool.styles_.size()) : nullptr;
+
+  const size_t before_strings_index = out->size();
+  header->stringsStart = before_strings_index - start_index;
+
+  // Styles always come first.
+  for (const std::unique_ptr<StyleEntry>& entry : pool.styles_) {
+    *indices++ = out->size() - before_strings_index;
+    EncodeString(entry->value, utf8, out);
+  }
+
+  for (const std::unique_ptr<Entry>& entry : pool.strings_) {
+    *indices++ = out->size() - before_strings_index;
+    EncodeString(entry->value, utf8, out);
   }
 
   out->Align4();
 
-  if (!pool.styles_.empty()) {
+  if (style_indices != nullptr) {
     const size_t before_styles_index = out->size();
-    header->stylesStart = before_styles_index - start_index;
+    header->stylesStart = util::HostToDevice32(before_styles_index - start_index);
 
-    size_t current_index = 0;
-    for (const auto& entry : pool.styles_) {
-      while (entry->str.index() > current_index) {
-        style_indices[current_index++] = out->size() - before_styles_index;
+    for (const std::unique_ptr<StyleEntry>& entry : pool.styles_) {
+      *style_indices++ = out->size() - before_styles_index;
 
-        uint32_t* span_offset = out->NextBlock<uint32_t>();
-        *span_offset = android::ResStringPool_span::END;
-      }
-      style_indices[current_index++] = out->size() - before_styles_index;
-
-      android::ResStringPool_span* span =
-          out->NextBlock<android::ResStringPool_span>(entry->spans.size());
-      for (const auto& s : entry->spans) {
-        span->name.index = s.name.index();
-        span->firstChar = s.first_char;
-        span->lastChar = s.last_char;
-        span++;
+      if (!entry->spans.empty()) {
+        android::ResStringPool_span* span =
+            out->NextBlock<android::ResStringPool_span>(entry->spans.size());
+        for (const Span& s : entry->spans) {
+          span->name.index = util::HostToDevice32(s.name.index());
+          span->firstChar = util::HostToDevice32(s.first_char);
+          span->lastChar = util::HostToDevice32(s.last_char);
+          span++;
+        }
       }
 
       uint32_t* spanEnd = out->NextBlock<uint32_t>();
@@ -436,7 +438,7 @@
     memset(padding, 0xff, padding_length);
     out->Align4();
   }
-  header->header.size = out->size() - start_index;
+  header->header.size = util::HostToDevice32(out->size() - start_index);
   return true;
 }
 
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index d1232a2..8350d0d 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -42,12 +42,16 @@
   std::vector<Span> spans;
 };
 
+// A StringPool for storing the value of String and StyledString resources.
+// Styles and Strings are stored separately, since the runtime variant of this
+// class -- ResStringPool -- requires that styled strings *always* appear first, since their
+// style data is stored as an array indexed by the same indices as the main string pool array.
+// Otherwise, the style data array would have to be sparse and take up more space.
 class StringPool {
  public:
   class Context {
    public:
     enum : uint32_t {
-      kStylePriority = 0u,
       kHighPriority = 1u,
       kNormalPriority = 0x7fffffffu,
       kLowPriority = 0xffffffffu,
@@ -58,8 +62,8 @@
     Context() = default;
     Context(uint32_t p, const ConfigDescription& c) : priority(p), config(c) {}
     explicit Context(uint32_t p) : priority(p) {}
-    explicit Context(const ConfigDescription& c)
-        : priority(kNormalPriority), config(c) {}
+    explicit Context(const ConfigDescription& c) : priority(kNormalPriority), config(c) {
+    }
   };
 
   class Entry;
@@ -116,13 +120,14 @@
    public:
     std::string value;
     Context context;
-    size_t index;
 
    private:
     friend class StringPool;
     friend class Ref;
 
+    size_t index_;
     int ref_;
+    const StringPool* pool_;
   };
 
   struct Span {
@@ -133,18 +138,18 @@
 
   class StyleEntry {
    public:
-    Ref str;
+    std::string value;
+    Context context;
     std::vector<Span> spans;
 
    private:
     friend class StringPool;
     friend class StyleRef;
 
+    size_t index_;
     int ref_;
   };
 
-  using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
-
   static bool FlattenUtf8(BigBuffer* out, const StringPool& pool);
   static bool FlattenUtf16(BigBuffer* out, const StringPool& pool);
 
@@ -152,92 +157,61 @@
   StringPool(StringPool&&) = default;
   StringPool& operator=(StringPool&&) = default;
 
-  /**
-   * Adds a string to the pool, unless it already exists. Returns
-   * a reference to the string in the pool.
-   */
+  // Adds a string to the pool, unless it already exists. Returns a reference to the string in the
+  // pool.
   Ref MakeRef(const android::StringPiece& str);
 
-  /**
-   * Adds a string to the pool, unless it already exists, with a context
-   * object that can be used when sorting the string pool. Returns
-   * a reference to the string in the pool.
-   */
+  // Adds a string to the pool, unless it already exists, with a context object that can be used
+  // when sorting the string pool. Returns a reference to the string in the pool.
   Ref MakeRef(const android::StringPiece& str, const Context& context);
 
-  /**
-   * Adds a style to the string pool and returns a reference to it.
-   */
+  // Adds a style to the string pool and returns a reference to it.
   StyleRef MakeRef(const StyleString& str);
 
-  /**
-   * Adds a style to the string pool with a context object that
-   * can be used when sorting the string pool. Returns a reference
-   * to the style in the string pool.
-   */
+  // Adds a style to the string pool with a context object that can be used when sorting the string
+  // pool. Returns a reference to the style in the string pool.
   StyleRef MakeRef(const StyleString& str, const Context& context);
 
-  /**
-   * Adds a style from another string pool. Returns a reference to the
-   * style in the string pool.
-   */
+  // Adds a style from another string pool. Returns a reference to the style in the string pool.
   StyleRef MakeRef(const StyleRef& ref);
 
-  /**
-   * Moves pool into this one without coalescing strings. When this
-   * function returns, pool will be empty.
-   */
+  // Moves pool into this one without coalescing strings. When this function returns, pool will be
+  // empty.
   void Merge(StringPool&& pool);
 
-  /**
-   * Returns the number of strings in the table.
-   */
-  inline size_t size() const;
+  inline const std::vector<std::unique_ptr<Entry>>& strings() const {
+    return strings_;
+  }
 
-  /**
-   * Reserves space for strings and styles as an optimization.
-   */
+  // Returns the number of strings in the table.
+  inline size_t size() const {
+    return styles_.size() + strings_.size();
+  }
+
+  // Reserves space for strings and styles as an optimization.
   void HintWillAdd(size_t string_count, size_t style_count);
 
-  /**
-   * Sorts the strings according to some comparison function.
-   */
-  void Sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
+  // Sorts the strings according to their Context using some comparison function.
+  // Equal Contexts are further sorted by string value, lexicographically.
+  // If no comparison function is provided, values are only sorted lexicographically.
+  void Sort(const std::function<int(const Context&, const Context&)>& cmp = nullptr);
 
-  /**
-   * Removes any strings that have no references.
-   */
+  // Removes any strings that have no references.
   void Prune();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(StringPool);
 
-  friend const_iterator begin(const StringPool& pool);
-  friend const_iterator end(const StringPool& pool);
-
   static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8);
 
   Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique);
+  void ReAssignIndices();
 
   std::vector<std::unique_ptr<Entry>> strings_;
   std::vector<std::unique_ptr<StyleEntry>> styles_;
   std::unordered_multimap<android::StringPiece, Entry*> indexed_strings_;
 };
 
-//
-// Inline implementation
-//
-
-inline size_t StringPool::size() const { return strings_.size(); }
-
-inline StringPool::const_iterator begin(const StringPool& pool) {
-  return pool.strings_.begin();
-}
-
-inline StringPool::const_iterator end(const StringPool& pool) {
-  return pool.strings_.end();
-}
-
 }  // namespace aapt
 
 #endif  // AAPT_STRING_POOL_H
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index f64a8cf..b1e5ce2 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -23,8 +23,12 @@
 #include "test/Test.h"
 #include "util/Util.h"
 
-using android::StringPiece;
-using android::StringPiece16;
+using ::android::StringPiece;
+using ::android::StringPiece16;
+using ::testing::Eq;
+using ::testing::Ne;
+using ::testing::NotNull;
+using ::testing::Pointee;
 
 namespace aapt {
 
@@ -32,129 +36,127 @@
   StringPool pool;
 
   StringPool::Ref ref = pool.MakeRef("wut");
-  EXPECT_EQ(*ref, "wut");
+  EXPECT_THAT(*ref, Eq("wut"));
 }
 
 TEST(StringPoolTest, InsertTwoUniqueStrings) {
   StringPool pool;
 
-  StringPool::Ref ref = pool.MakeRef("wut");
-  StringPool::Ref ref2 = pool.MakeRef("hey");
+  StringPool::Ref ref_a = pool.MakeRef("wut");
+  StringPool::Ref ref_b = pool.MakeRef("hey");
 
-  EXPECT_EQ(*ref, "wut");
-  EXPECT_EQ(*ref2, "hey");
+  EXPECT_THAT(*ref_a, Eq("wut"));
+  EXPECT_THAT(*ref_b, Eq("hey"));
 }
 
 TEST(StringPoolTest, DoNotInsertNewDuplicateString) {
   StringPool pool;
 
-  StringPool::Ref ref = pool.MakeRef("wut");
-  StringPool::Ref ref2 = pool.MakeRef("wut");
+  StringPool::Ref ref_a = pool.MakeRef("wut");
+  StringPool::Ref ref_b = pool.MakeRef("wut");
 
-  EXPECT_EQ(*ref, "wut");
-  EXPECT_EQ(*ref2, "wut");
-  EXPECT_EQ(1u, pool.size());
+  EXPECT_THAT(*ref_a, Eq("wut"));
+  EXPECT_THAT(*ref_b, Eq("wut"));
+  EXPECT_THAT(pool.size(), Eq(1u));
 }
 
 TEST(StringPoolTest, MaintainInsertionOrderIndex) {
   StringPool pool;
 
-  StringPool::Ref ref = pool.MakeRef("z");
-  StringPool::Ref ref2 = pool.MakeRef("a");
-  StringPool::Ref ref3 = pool.MakeRef("m");
+  StringPool::Ref ref_a = pool.MakeRef("z");
+  StringPool::Ref ref_b = pool.MakeRef("a");
+  StringPool::Ref ref_c = pool.MakeRef("m");
 
-  EXPECT_EQ(0u, ref.index());
-  EXPECT_EQ(1u, ref2.index());
-  EXPECT_EQ(2u, ref3.index());
+  EXPECT_THAT(ref_a.index(), Eq(0u));
+  EXPECT_THAT(ref_b.index(), Eq(1u));
+  EXPECT_THAT(ref_c.index(), Eq(2u));
 }
 
 TEST(StringPoolTest, PruneStringsWithNoReferences) {
   StringPool pool;
 
-  StringPool::Ref refA = pool.MakeRef("foo");
-  {
-    StringPool::Ref ref = pool.MakeRef("wut");
-    EXPECT_EQ(*ref, "wut");
-    EXPECT_EQ(2u, pool.size());
-  }
-  StringPool::Ref refB = pool.MakeRef("bar");
+  StringPool::Ref ref_a = pool.MakeRef("foo");
 
-  EXPECT_EQ(3u, pool.size());
+  {
+    StringPool::Ref ref_b = pool.MakeRef("wut");
+    EXPECT_THAT(*ref_b, Eq("wut"));
+    EXPECT_THAT(pool.size(), Eq(2u));
+    pool.Prune();
+    EXPECT_THAT(pool.size(), Eq(2u));
+  }
+  EXPECT_THAT(pool.size(), Eq(2u));
+
+  {
+    StringPool::Ref ref_c = pool.MakeRef("bar");
+    EXPECT_THAT(pool.size(), Eq(3u));
+
+    pool.Prune();
+    EXPECT_THAT(pool.size(), Eq(2u));
+  }
+  EXPECT_THAT(pool.size(), Eq(2u));
+
   pool.Prune();
-  EXPECT_EQ(2u, pool.size());
-  StringPool::const_iterator iter = begin(pool);
-  EXPECT_EQ((*iter)->value, "foo");
-  EXPECT_LT((*iter)->index, 2u);
-  ++iter;
-  EXPECT_EQ((*iter)->value, "bar");
-  EXPECT_LT((*iter)->index, 2u);
+  EXPECT_THAT(pool.size(), Eq(1u));
 }
 
-TEST(StringPoolTest, SortAndMaintainIndexesInReferences) {
+TEST(StringPoolTest, SortAndMaintainIndexesInStringReferences) {
   StringPool pool;
 
-  StringPool::Ref ref = pool.MakeRef("z");
-  StringPool::StyleRef ref2 = pool.MakeRef(StyleString{{"a"}});
-  StringPool::Ref ref3 = pool.MakeRef("m");
+  StringPool::Ref ref_a = pool.MakeRef("z");
+  StringPool::Ref ref_b = pool.MakeRef("a");
+  StringPool::Ref ref_c = pool.MakeRef("m");
 
-  EXPECT_EQ(*ref, "z");
-  EXPECT_EQ(0u, ref.index());
+  EXPECT_THAT(*ref_a, Eq("z"));
+  EXPECT_THAT(ref_a.index(), Eq(0u));
 
-  EXPECT_EQ(*(ref2->str), "a");
-  EXPECT_EQ(1u, ref2.index());
+  EXPECT_THAT(*ref_b, Eq("a"));
+  EXPECT_THAT(ref_b.index(), Eq(1u));
 
-  EXPECT_EQ(*ref3, "m");
-  EXPECT_EQ(2u, ref3.index());
+  EXPECT_THAT(*ref_c, Eq("m"));
+  EXPECT_THAT(ref_c.index(), Eq(2u));
 
-  pool.Sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
-    return a.value < b.value;
-  });
+  pool.Sort();
 
-  EXPECT_EQ(*ref, "z");
-  EXPECT_EQ(2u, ref.index());
+  EXPECT_THAT(*ref_a, Eq("z"));
+  EXPECT_THAT(ref_a.index(), Eq(2u));
 
-  EXPECT_EQ(*(ref2->str), "a");
-  EXPECT_EQ(0u, ref2.index());
+  EXPECT_THAT(*ref_b, Eq("a"));
+  EXPECT_THAT(ref_b.index(), Eq(0u));
 
-  EXPECT_EQ(*ref3, "m");
-  EXPECT_EQ(1u, ref3.index());
+  EXPECT_THAT(*ref_c, Eq("m"));
+  EXPECT_THAT(ref_c.index(), Eq(1u));
 }
 
 TEST(StringPoolTest, SortAndStillDedupe) {
   StringPool pool;
 
-  StringPool::Ref ref = pool.MakeRef("z");
-  StringPool::Ref ref2 = pool.MakeRef("a");
-  StringPool::Ref ref3 = pool.MakeRef("m");
+  StringPool::Ref ref_a = pool.MakeRef("z");
+  StringPool::Ref ref_b = pool.MakeRef("a");
+  StringPool::Ref ref_c = pool.MakeRef("m");
 
-  pool.Sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
-    return a.value < b.value;
-  });
+  pool.Sort();
 
-  StringPool::Ref ref4 = pool.MakeRef("z");
-  StringPool::Ref ref5 = pool.MakeRef("a");
-  StringPool::Ref ref6 = pool.MakeRef("m");
+  StringPool::Ref ref_d = pool.MakeRef("z");
+  StringPool::Ref ref_e = pool.MakeRef("a");
+  StringPool::Ref ref_f = pool.MakeRef("m");
 
-  EXPECT_EQ(ref4.index(), ref.index());
-  EXPECT_EQ(ref5.index(), ref2.index());
-  EXPECT_EQ(ref6.index(), ref3.index());
+  EXPECT_THAT(ref_d.index(), Eq(ref_a.index()));
+  EXPECT_THAT(ref_e.index(), Eq(ref_b.index()));
+  EXPECT_THAT(ref_f.index(), Eq(ref_c.index()));
 }
 
 TEST(StringPoolTest, AddStyles) {
   StringPool pool;
 
-  StyleString str{{"android"}, {Span{{"b"}, 2, 6}}};
-
-  StringPool::StyleRef ref = pool.MakeRef(str);
-
-  EXPECT_EQ(0u, ref.index());
-  EXPECT_EQ(std::string("android"), *(ref->str));
-  ASSERT_EQ(1u, ref->spans.size());
+  StringPool::StyleRef ref = pool.MakeRef(StyleString{{"android"}, {Span{{"b"}, 2, 6}}});
+  EXPECT_THAT(ref.index(), Eq(0u));
+  EXPECT_THAT(ref->value, Eq("android"));
+  ASSERT_THAT(ref->spans.size(), Eq(1u));
 
   const StringPool::Span& span = ref->spans.front();
-  EXPECT_EQ(*(span.name), "b");
-  EXPECT_EQ(2u, span.first_char);
-  EXPECT_EQ(6u, span.last_char);
+  EXPECT_THAT(*span.name, Eq("b"));
+  EXPECT_THAT(span.first_char, Eq(2u));
+  EXPECT_THAT(span.last_char, Eq(6u));
 }
 
 TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) {
@@ -163,9 +165,25 @@
   StringPool::Ref ref = pool.MakeRef("android");
 
   StyleString str{{"android"}};
-  StringPool::StyleRef styleRef = pool.MakeRef(str);
+  StringPool::StyleRef style_ref = pool.MakeRef(StyleString{{"android"}});
 
-  EXPECT_NE(ref.index(), styleRef.index());
+  EXPECT_THAT(ref.index(), Ne(style_ref.index()));
+}
+
+TEST(StringPoolTest, StylesAndStringsAreSeparateAfterSorting) {
+  StringPool pool;
+
+  StringPool::StyleRef ref_a = pool.MakeRef(StyleString{{"beta"}});
+  StringPool::Ref ref_b = pool.MakeRef("alpha");
+  StringPool::StyleRef ref_c = pool.MakeRef(StyleString{{"alpha"}});
+
+  EXPECT_THAT(ref_b.index(), Ne(ref_c.index()));
+
+  pool.Sort();
+
+  EXPECT_THAT(ref_c.index(), Eq(0u));
+  EXPECT_THAT(ref_a.index(), Eq(1u));
+  EXPECT_THAT(ref_b.index(), Eq(2u));
 }
 
 TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
@@ -177,7 +195,7 @@
 
   std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
   ResStringPool test;
-  ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+  ASSERT_THAT(test.setTo(data.get(), buffer.size()), Eq(NO_ERROR));
 }
 
 TEST(StringPoolTest, FlattenOddCharactersUtf16) {
@@ -193,9 +211,9 @@
   ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
   size_t len = 0;
   const char16_t* str = test.stringAt(0, &len);
-  EXPECT_EQ(1u, len);
-  EXPECT_EQ(u'\u093f', *str);
-  EXPECT_EQ(0u, str[1]);
+  EXPECT_THAT(len, Eq(1u));
+  EXPECT_THAT(str, Pointee(Eq(u'\u093f')));
+  EXPECT_THAT(str[1], Eq(0u));
 }
 
 constexpr const char* sLongString =
@@ -210,18 +228,20 @@
 
   StringPool pool;
 
-  StringPool::Ref ref1 = pool.MakeRef("hello");
-  StringPool::Ref ref2 = pool.MakeRef("goodbye");
-  StringPool::Ref ref3 = pool.MakeRef(sLongString);
-  StringPool::Ref ref4 = pool.MakeRef("");
-  StringPool::StyleRef ref5 = pool.MakeRef(
-      StyleString{{"style"}, {Span{{"b"}, 0, 1}, Span{{"i"}, 2, 3}}});
+  StringPool::Ref ref_a = pool.MakeRef("hello");
+  StringPool::Ref ref_b = pool.MakeRef("goodbye");
+  StringPool::Ref ref_c = pool.MakeRef(sLongString);
+  StringPool::Ref ref_d = pool.MakeRef("");
+  StringPool::StyleRef ref_e =
+      pool.MakeRef(StyleString{{"style"}, {Span{{"b"}, 0, 1}, Span{{"i"}, 2, 3}}});
 
-  EXPECT_EQ(0u, ref1.index());
-  EXPECT_EQ(1u, ref2.index());
-  EXPECT_EQ(2u, ref3.index());
-  EXPECT_EQ(3u, ref4.index());
-  EXPECT_EQ(4u, ref5.index());
+  // Styles are always first.
+  EXPECT_THAT(ref_e.index(), Eq(0u));
+
+  EXPECT_THAT(ref_a.index(), Eq(1u));
+  EXPECT_THAT(ref_b.index(), Eq(2u));
+  EXPECT_THAT(ref_c.index(), Eq(3u));
+  EXPECT_THAT(ref_d.index(), Eq(4u));
 
   BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)};
   StringPool::FlattenUtf8(&buffers[0], pool);
@@ -234,38 +254,37 @@
     ResStringPool test;
     ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
 
-    EXPECT_EQ(std::string("hello"), util::GetString(test, 0));
-    EXPECT_EQ(StringPiece16(u"hello"), util::GetString16(test, 0));
+    EXPECT_THAT(util::GetString(test, 1), Eq("hello"));
+    EXPECT_THAT(util::GetString16(test, 1), Eq(u"hello"));
 
-    EXPECT_EQ(std::string("goodbye"), util::GetString(test, 1));
-    EXPECT_EQ(StringPiece16(u"goodbye"), util::GetString16(test, 1));
+    EXPECT_THAT(util::GetString(test, 2), Eq("goodbye"));
+    EXPECT_THAT(util::GetString16(test, 2), Eq(u"goodbye"));
 
-    EXPECT_EQ(StringPiece(sLongString), util::GetString(test, 2));
-    EXPECT_EQ(util::Utf8ToUtf16(sLongString), util::GetString16(test, 2).to_string());
+    EXPECT_THAT(util::GetString(test, 3), Eq(sLongString));
+    EXPECT_THAT(util::GetString16(test, 3), Eq(util::Utf8ToUtf16(sLongString)));
 
     size_t len;
-    EXPECT_TRUE(test.stringAt(3, &len) != nullptr ||
-                test.string8At(3, &len) != nullptr);
+    EXPECT_TRUE(test.stringAt(4, &len) != nullptr || test.string8At(4, &len) != nullptr);
 
-    EXPECT_EQ(std::string("style"), util::GetString(test, 4));
-    EXPECT_EQ(StringPiece16(u"style"), util::GetString16(test, 4));
+    EXPECT_THAT(util::GetString(test, 0), Eq("style"));
+    EXPECT_THAT(util::GetString16(test, 0), Eq(u"style"));
 
-    const ResStringPool_span* span = test.styleAt(4);
-    ASSERT_NE(nullptr, span);
-    EXPECT_EQ(std::string("b"), util::GetString(test, span->name.index));
-    EXPECT_EQ(StringPiece16(u"b"), util::GetString16(test, span->name.index));
-    EXPECT_EQ(0u, span->firstChar);
-    EXPECT_EQ(1u, span->lastChar);
+    const ResStringPool_span* span = test.styleAt(0);
+    ASSERT_THAT(span, NotNull());
+    EXPECT_THAT(util::GetString(test, span->name.index), Eq("b"));
+    EXPECT_THAT(util::GetString16(test, span->name.index), Eq(u"b"));
+    EXPECT_THAT(span->firstChar, Eq(0u));
+    EXPECT_THAT(span->lastChar, Eq(1u));
     span++;
 
-    ASSERT_NE(ResStringPool_span::END, span->name.index);
-    EXPECT_EQ(std::string("i"), util::GetString(test, span->name.index));
-    EXPECT_EQ(StringPiece16(u"i"), util::GetString16(test, span->name.index));
-    EXPECT_EQ(2u, span->firstChar);
-    EXPECT_EQ(3u, span->lastChar);
+    ASSERT_THAT(span->name.index, Ne(ResStringPool_span::END));
+    EXPECT_THAT(util::GetString(test, span->name.index), Eq("i"));
+    EXPECT_THAT(util::GetString16(test, span->name.index), Eq(u"i"));
+    EXPECT_THAT(span->firstChar, Eq(2u));
+    EXPECT_THAT(span->lastChar, Eq(3u));
     span++;
 
-    EXPECT_EQ(ResStringPool_span::END, span->name.index);
+    EXPECT_THAT(span->name.index, Eq(ResStringPool_span::END));
   }
 }
 
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 8741b7b..e1c45d6 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -179,6 +179,13 @@
         xml::Attribute{"", "configForSplit", app_info.split_name.value()});
   }
 
+  // Splits may contain more configurations than originally desired (fallback densities, etc.).
+  // This makes programmatic discovery of split targetting difficult. Encode the original
+  // split constraints intended for this split.
+  std::stringstream target_config_str;
+  target_config_str << util::Joiner(constraints.configs, ",");
+  manifest_el->attributes.push_back(xml::Attribute{"", "targetConfig", target_config_str.str()});
+
   std::unique_ptr<xml::Element> application_el = util::make_unique<xml::Element>();
   application_el->name = "application";
   application_el->attributes.push_back(
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index a031ea4..871ed4f 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -120,7 +120,7 @@
 
   // All Span indices are UTF-16 based, according to the resources.arsc format expected by the
   // runtime. So we will do all our processing in UTF-16, then convert back.
-  const std::u16string text16 = util::Utf8ToUtf16(*string->value->str);
+  const std::u16string text16 = util::Utf8ToUtf16(string->value->value);
 
   // Convenient wrapper around the text that allows us to work with StringPieces.
   const StringPiece16 text(text16);
diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
index b08e1da..711558a 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
@@ -31,7 +31,7 @@
       util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
       Pseudolocalizer::Method::kNone, &pool);
 
-  EXPECT_EQ(original_style.str, *new_string->value->str);
+  EXPECT_EQ(original_style.str, new_string->value->value);
   ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
 
   EXPECT_EQ(std::string("i"), *new_string->value->spans[0].name);
@@ -52,7 +52,7 @@
       util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
       Pseudolocalizer::Method::kAccent, &pool);
 
-  EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *new_string->value->str);
+  EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), new_string->value->value);
   ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
 
   EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
@@ -79,7 +79,7 @@
       Pseudolocalizer::Method::kAccent, &pool);
   ASSERT_NE(nullptr, new_string);
   ASSERT_EQ(2u, new_string->value->spans.size());
-  EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str);
+  EXPECT_EQ(std::string("[ɓöļð one]"), new_string->value->value);
 
   EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
   EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
@@ -101,7 +101,7 @@
       Pseudolocalizer::Method::kAccent, &pool);
   ASSERT_NE(nullptr, new_string);
   ASSERT_EQ(2u, new_string->value->spans.size());
-  EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str);
+  EXPECT_EQ(std::string("[ɓöļð one]"), new_string->value->value);
 
   EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
   EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
@@ -126,7 +126,7 @@
   ASSERT_EQ(4u, new_string->value->spans.size());
   EXPECT_EQ(std::string(
                 "[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļļ. one two three four five six]"),
-            *new_string->value->str);
+            new_string->value->value);
 
   EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
   EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš").size(), new_string->value->spans[0].first_char);
@@ -165,7 +165,7 @@
   ASSERT_NE(nullptr, new_string);
   ASSERT_EQ(2u, new_string->value->spans.size());
   EXPECT_EQ(std::string("[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžéð. one two three four]"),
-            *new_string->value->str);
+            new_string->value->value);
 
   EXPECT_EQ(std::string("em"), *new_string->value->spans[0].name);
   EXPECT_EQ(std::u16string(u"[Ţĥîš").size(), new_string->value->spans[0].first_char);
@@ -265,7 +265,7 @@
   ASSERT_NE(nullptr, new_styled_string);
 
   // "world" should be untranslated.
-  EXPECT_NE(std::string::npos, new_styled_string->value->str->find("world"));
+  EXPECT_NE(std::string::npos, new_styled_string->value->value.find("world"));
 
   String* new_string = test::GetValueForConfig<String>(table.get(), "android:string/bar",
                                                        test::ParseConfigOrDie("en-rXA"));
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index f4d0226..e5993a6 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -557,19 +557,15 @@
 }  // namespace
 
 bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
-  // We must do this before writing the resources, since the string pool IDs may
-  // change.
-  table->string_pool.Sort(
-      [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
-        int diff = a.context.priority - b.context.priority;
-        if (diff < 0) return true;
-        if (diff > 0) return false;
-        diff = a.context.config.compare(b.context.config);
-        if (diff < 0) return true;
-        if (diff > 0) return false;
-        return a.value < b.value;
-      });
+  // We must do this before writing the resources, since the string pool IDs may change.
   table->string_pool.Prune();
+  table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
+    int diff = util::compare(a.priority, b.priority);
+    if (diff == 0) {
+      diff = a.config.compare(b.config);
+    }
+    return diff;
+  });
 
   // Write the ResTable header.
   ChunkWriter table_writer(buffer_);
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index bfebedef..331ef78 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -180,8 +180,7 @@
     flatNode->lineNumber = util::HostToDevice32(node->line_number);
     flatNode->comment.index = util::HostToDevice32(-1);
 
-    ResXMLTree_namespaceExt* flat_ns =
-        writer.NextBlock<ResXMLTree_namespaceExt>();
+    ResXMLTree_namespaceExt* flat_ns = writer.NextBlock<ResXMLTree_namespaceExt>();
     AddString(node->namespace_prefix, kLowPriority, &flat_ns->prefix);
     AddString(node->namespace_uri, kLowPriority, &flat_ns->uri);
 
@@ -289,8 +288,7 @@
   BigBuffer* buffer_;
   XmlFlattenerOptions options_;
 
-  // Scratch vector to filter attributes. We avoid allocations
-  // making this a member.
+  // Scratch vector to filter attributes. We avoid allocations making this a member.
   std::vector<xml::Attribute*> filtered_attrs_;
 };
 
@@ -307,10 +305,9 @@
   }
 
   // Sort the string pool so that attribute resource IDs show up first.
-  visitor.pool.Sort(
-      [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
-        return a.context.priority < b.context.priority;
-      });
+  visitor.pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
+    return util::compare(a.priority, b.priority);
+  });
 
   // Now we flatten the string pool references into the correct places.
   for (const auto& ref_entry : visitor.string_refs) {
@@ -328,15 +325,13 @@
     // Write the array of resource IDs, indexed by StringPool order.
     ChunkWriter res_id_map_writer(buffer_);
     res_id_map_writer.StartChunk<ResChunk_header>(RES_XML_RESOURCE_MAP_TYPE);
-    for (const auto& str : visitor.pool) {
-      ResourceId id = {str->context.priority};
-      if (id.id == kLowPriority || !id.is_valid()) {
-        // When we see the first non-resource ID,
-        // we're done.
+    for (const auto& str : visitor.pool.strings()) {
+      ResourceId id(str->context.priority);
+      if (str->context.priority == kLowPriority || !id.is_valid()) {
+        // When we see the first non-resource ID, we're done.
         break;
       }
-
-      *res_id_map_writer.NextBlock<uint32_t>() = id.id;
+      *res_id_map_writer.NextBlock<uint32_t>() = util::HostToDevice32(id.id);
     }
     res_id_map_writer.Finish();
   }
diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp
index 38bf4e3..6b21364 100644
--- a/tools/aapt2/proto/ProtoHelpers.cpp
+++ b/tools/aapt2/proto/ProtoHelpers.cpp
@@ -18,8 +18,7 @@
 
 namespace aapt {
 
-void SerializeStringPoolToPb(const StringPool& pool,
-                             pb::StringPool* out_pb_pool) {
+void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool) {
   BigBuffer buffer(1024);
   StringPool::FlattenUtf8(&buffer, pool);
 
@@ -28,14 +27,12 @@
 
   size_t offset = 0;
   for (const BigBuffer::Block& block : buffer) {
-    data->insert(data->begin() + offset, block.buffer.get(),
-                 block.buffer.get() + block.size);
+    data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
     offset += block.size;
   }
 }
 
-void SerializeSourceToPb(const Source& source, StringPool* src_pool,
-                         pb::Source* out_pb_source) {
+void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source) {
   StringPool::Ref ref = src_pool->MakeRef(source.path);
   out_pb_source->set_path_idx(static_cast<uint32_t>(ref.index()));
   if (source.line) {
@@ -43,8 +40,7 @@
   }
 }
 
-void DeserializeSourceFromPb(const pb::Source& pb_source,
-                             const android::ResStringPool& src_pool,
+void DeserializeSourceFromPb(const pb::Source& pb_source, const android::ResStringPool& src_pool,
                              Source* out_source) {
   if (pb_source.has_path_idx()) {
     out_source->path = util::GetString(src_pool, pb_source.path_idx());
@@ -80,8 +76,7 @@
   return SymbolState::kUndefined;
 }
 
-void SerializeConfig(const ConfigDescription& config,
-                     pb::ConfigDescription* out_pb_config) {
+void SerializeConfig(const ConfigDescription& config, pb::ConfigDescription* out_pb_config) {
   android::ResTable_config flat_config = config;
   flat_config.size = sizeof(flat_config);
   flat_config.swapHtoD();
@@ -99,8 +94,7 @@
     return false;
   }
 
-  config = reinterpret_cast<const android::ResTable_config*>(
-      pb_config.data().data());
+  config = reinterpret_cast<const android::ResTable_config*>(pb_config.data().data());
   out_config->copyFromDtoH(*config);
   return true;
 }
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 4b56192..37d5ed0 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -195,8 +195,7 @@
           spans++;
         }
         return util::make_unique<StyledString>(pool->MakeRef(
-            style_str,
-            StringPool::Context(StringPool::Context::kStylePriority, config)));
+            style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
       }
       return util::make_unique<String>(
           pool->MakeRef(str, StringPool::Context(config)));
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index d87d64e..730442c 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -87,7 +87,9 @@
     pb_prim->set_data(val.data);
   }
 
-  void VisitItem(Item* item) override { LOG(FATAL) << "unimplemented item"; }
+  void VisitItem(Item* item) override {
+    LOG(FATAL) << "unimplemented item";
+  }
 
   void Visit(Attribute* attr) override {
     pb::Attribute* pb_attr = pb_compound_value()->mutable_attr();
@@ -207,19 +209,15 @@
 }  // namespace
 
 std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
-  // We must do this before writing the resources, since the string pool IDs may
-  // change.
-  table->string_pool.Sort(
-      [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
-        int diff = a.context.priority - b.context.priority;
-        if (diff < 0) return true;
-        if (diff > 0) return false;
-        diff = a.context.config.compare(b.context.config);
-        if (diff < 0) return true;
-        if (diff > 0) return false;
-        return a.value < b.value;
-      });
+  // We must do this before writing the resources, since the string pool IDs may change.
   table->string_pool.Prune();
+  table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
+    int diff = util::compare(a.priority, b.priority);
+    if (diff == 0) {
+      diff = a.config.compare(b.config);
+    }
+    return diff;
+  });
 
   auto pb_table = util::make_unique<pb::ResourceTable>();
   SerializeStringPoolToPb(table->string_pool, pb_table->mutable_string_pool());
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index f311670..728d1f4 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -38,20 +38,17 @@
 
 using namespace android;
 
-using android::base::StringPrintf;
+using ::android::base::StringPrintf;
 
 namespace {
 
-/*
- * Visitor that converts a reference's resource ID to a resource name,
- * given a mapping from resource ID to resource name.
- */
+// Visitor that converts a reference's resource ID to a resource name, given a mapping from
+// resource ID to resource name.
 class ReferenceIdToNameVisitor : public ValueVisitor {
  public:
   using ValueVisitor::Visit;
 
-  explicit ReferenceIdToNameVisitor(
-      const std::map<ResourceId, ResourceName>* mapping)
+  explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping)
       : mapping_(mapping) {
     CHECK(mapping_ != nullptr);
   }
@@ -99,7 +96,7 @@
   if (parser.chunk()->type != android::RES_TABLE_TYPE) {
     context_->GetDiagnostics()->Error(DiagMessage(source_)
                                       << StringPrintf("unknown chunk of type 0x%02x",
-                                                      (int)parser.chunk()->type));
+                                                      static_cast<int>(parser.chunk()->type)));
     return false;
   }
 
@@ -115,7 +112,7 @@
       context_->GetDiagnostics()->Warn(
           DiagMessage(source_) << StringPrintf(
               "unexpected chunk of type 0x%02x trailing RES_TABLE_TYPE",
-              (int)parser.chunk()->type));
+              static_cast<int>(parser.chunk()->type)));
     }
   }
   return true;
@@ -165,9 +162,8 @@
 
       default:
         context_->GetDiagnostics()->Warn(
-            DiagMessage(source_)
-            << "unexpected chunk type "
-            << (int)util::DeviceToHost16(parser.chunk()->type));
+            DiagMessage(source_) << "unexpected chunk type "
+                                 << static_cast<int>(util::DeviceToHost16(parser.chunk()->type)));
         break;
     }
   }
@@ -245,8 +241,7 @@
             return false;
           }
         } else {
-          context_->GetDiagnostics()->Warn(DiagMessage(source_)
-                                           << "unexpected string pool");
+          context_->GetDiagnostics()->Warn(DiagMessage(source_) << "unexpected string pool");
         }
         break;
 
@@ -270,9 +265,8 @@
 
       default:
         context_->GetDiagnostics()->Warn(
-            DiagMessage(source_)
-            << "unexpected chunk type "
-            << (int)util::DeviceToHost16(parser.chunk()->type));
+            DiagMessage(source_) << "unexpected chunk type "
+                                 << static_cast<int>(util::DeviceToHost16(parser.chunk()->type)));
         break;
     }
   }
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index b9ada77..ad3989e 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -70,12 +70,6 @@
 android::StringPiece TrimWhitespace(const android::StringPiece& str);
 
 /**
- * UTF-16 isspace(). It basically checks for lower range characters that are
- * whitespace.
- */
-inline bool isspace16(char16_t c) { return c < 0x0080 && isspace(c); }
-
-/**
  * Returns an iterator to the first character that is not alpha-numeric and that
  * is not in the allowedChars set.
  */
@@ -104,6 +98,16 @@
 Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
                                               const android::StringPiece& class_name);
 
+template <typename T>
+typename std::enable_if<std::is_arithmetic<T>::value, int>::type compare(const T& a, const T& b) {
+  if (a < b) {
+    return -1;
+  } else if (a > b) {
+    return 1;
+  }
+  return 0;
+}
+
 /**
  * Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
  * This will be present in C++14 and can be removed then.