Merge changes I5cad0b5d,I6863eca2

* changes:
  Render VectorDrawables in software and then upload to the VDAtlas.
  Adding traces and some generic cleanup.
diff --git a/api/current.txt b/api/current.txt
index 0ba052b..23a0394 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30679,6 +30679,7 @@
     field public static final int N_MR1 = 25; // 0x19
     field public static final int O = 26; // 0x1a
     field public static final int O_MR1 = 27; // 0x1b
+    field public static final int P = 10000; // 0x2710
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -37081,13 +37082,14 @@
 
   public final class FillEventHistory implements android.os.Parcelable {
     method public int describeContents();
-    method public android.os.Bundle getClientState();
+    method public deprecated android.os.Bundle getClientState();
     method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
   }
 
   public static final class FillEventHistory.Event {
+    method public android.os.Bundle getClientState();
     method public java.lang.String getDatasetId();
     method public int getType();
     field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
@@ -39659,6 +39661,7 @@
     field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
+    field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
diff --git a/api/system-current.txt b/api/system-current.txt
index 3e2eddb..a843e6d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -33396,6 +33396,7 @@
     field public static final int N_MR1 = 25; // 0x19
     field public static final int O = 26; // 0x1a
     field public static final int O_MR1 = 27; // 0x1b
+    field public static final int P = 10000; // 0x2710
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -40172,13 +40173,14 @@
 
   public final class FillEventHistory implements android.os.Parcelable {
     method public int describeContents();
-    method public android.os.Bundle getClientState();
+    method public deprecated android.os.Bundle getClientState();
     method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
   }
 
   public static final class FillEventHistory.Event {
+    method public android.os.Bundle getClientState();
     method public java.lang.String getDatasetId();
     method public int getType();
     field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
@@ -43102,6 +43104,7 @@
     field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
+    field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
diff --git a/api/test-current.txt b/api/test-current.txt
index 545d11c..f5b31d1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -30823,6 +30823,7 @@
     field public static final int N_MR1 = 25; // 0x19
     field public static final int O = 26; // 0x1a
     field public static final int O_MR1 = 27; // 0x1b
+    field public static final int P = 10000; // 0x2710
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -37279,13 +37280,14 @@
 
   public final class FillEventHistory implements android.os.Parcelable {
     method public int describeContents();
-    method public android.os.Bundle getClientState();
+    method public deprecated android.os.Bundle getClientState();
     method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
   }
 
   public static final class FillEventHistory.Event {
+    method public android.os.Bundle getClientState();
     method public java.lang.String getDatasetId();
     method public int getType();
     field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
@@ -39918,6 +39920,7 @@
     field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
+    field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 595ecd2..fb11272 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -964,7 +964,7 @@
 
     // TODO(adamlesinski): Make this accept more than just overlay directories.
     final void applyNewResourceDirsLocked(@NonNull final String baseCodePath,
-            @NonNull final String[] newResourceDirs) {
+            @Nullable final String[] newResourceDirs) {
         try {
             Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
                     "ResourcesManager#applyNewResourceDirsLocked");
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 1242cb0..8a1eae2 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -40,7 +40,6 @@
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Adapter;
 import android.widget.AdapterView;
@@ -469,7 +468,9 @@
                 // We've already done this -- nothing to do.
                 return ;
             }
-            Log.w(TAG, "updateAppWidget couldn't find any view, using error view", exception);
+            if (exception != null) {
+                Log.w(TAG, "Error inflating RemoteViews : " + exception.toString());
+            }
             content = getErrorView();
             mViewMode = VIEW_MODE_ERROR;
         }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 92f4849..48587b3 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1103,12 +1103,12 @@
     }
 
     /** @hide */
-    public void dump(Printer pw, String prefix, int flags) {
+    public void dump(Printer pw, String prefix, int dumpFlags) {
         super.dumpFront(pw, prefix);
         if (permission != null) {
             pw.println(prefix + "permission=" + permission);
         }
-        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
             pw.println(prefix + "taskAffinity=" + taskAffinity
                     + " targetActivity=" + targetActivity
                     + " persistableMode=" + persistableModeToString());
@@ -1127,7 +1127,7 @@
         if (uiOptions != 0) {
             pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
         }
-        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
             pw.println(prefix + "lockTaskLaunchMode="
                     + lockTaskLaunchModeToString(lockTaskLaunchMode));
         }
@@ -1143,7 +1143,7 @@
         if (maxAspectRatio != 0) {
             pw.println(prefix + "maxAspectRatio=" + maxAspectRatio);
         }
-        super.dumpBack(pw, prefix, flags);
+        super.dumpBack(pw, prefix, dumpFlags);
     }
 
     public String toString() {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2aa3d09..ad7a5ab 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1046,22 +1046,22 @@
     }
 
     /** @hide */
-    public void dump(Printer pw, String prefix, int flags) {
+    public void dump(Printer pw, String prefix, int dumpFlags) {
         super.dumpFront(pw, prefix);
-        if ((flags&DUMP_FLAG_DETAILS) != 0 && className != null) {
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && className != null) {
             pw.println(prefix + "className=" + className);
         }
         if (permission != null) {
             pw.println(prefix + "permission=" + permission);
         }
         pw.println(prefix + "processName=" + processName);
-        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
             pw.println(prefix + "taskAffinity=" + taskAffinity);
         }
         pw.println(prefix + "uid=" + uid + " flags=0x" + Integer.toHexString(flags)
                 + " privateFlags=0x" + Integer.toHexString(privateFlags)
                 + " theme=0x" + Integer.toHexString(theme));
-        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
             pw.println(prefix + "requiresSmallestWidthDp=" + requiresSmallestWidthDp
                     + " compatibleWidthLimitDp=" + compatibleWidthLimitDp
                     + " largestWidthLimitDp=" + largestWidthLimitDp);
@@ -1080,12 +1080,12 @@
         if (resourceDirs != null) {
             pw.println(prefix + "resourceDirs=" + Arrays.toString(resourceDirs));
         }
-        if ((flags&DUMP_FLAG_DETAILS) != 0 && seInfo != null) {
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && seInfo != null) {
             pw.println(prefix + "seinfo=" + seInfo);
             pw.println(prefix + "seinfoUser=" + seInfoUser);
         }
         pw.println(prefix + "dataDir=" + dataDir);
-        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
             pw.println(prefix + "deviceProtectedDataDir=" + deviceProtectedDataDir);
             pw.println(prefix + "credentialProtectedDataDir=" + credentialProtectedDataDir);
             if (sharedLibraryFiles != null) {
@@ -1104,7 +1104,7 @@
                 + " targetSdkVersion=" + targetSdkVersion
                 + " versionCode=" + versionCode
                 + " targetSandboxVersion=" + targetSandboxVersion);
-        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
             if (manageSpaceActivityName != null) {
                 pw.println(prefix + "manageSpaceActivityName=" + manageSpaceActivityName);
             }
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index 53be953..6b1222f 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -183,12 +183,12 @@
     protected void dumpBack(Printer pw, String prefix) {
         dumpBack(pw, prefix, DUMP_FLAG_ALL);
     }
-    
-    void dumpBack(Printer pw, String prefix, int flags) {
-        if ((flags&DUMP_FLAG_APPLICATION) != 0) {
+
+    void dumpBack(Printer pw, String prefix, int dumpFlags) {
+        if ((dumpFlags & DUMP_FLAG_APPLICATION) != 0) {
             if (applicationInfo != null) {
                 pw.println(prefix + "ApplicationInfo:");
-                applicationInfo.dump(pw, prefix + "  ", flags);
+                applicationInfo.dump(pw, prefix + "  ", dumpFlags);
             } else {
                 pw.println(prefix + "ApplicationInfo: null");
             }
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index 91dc06e..379b783 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -125,11 +125,11 @@
     }
 
     /** @hide */
-    public void dump(Printer pw, String prefix, int flags) {
+    public void dump(Printer pw, String prefix, int dumpFlags) {
         super.dumpFront(pw, prefix);
         pw.println(prefix + "authority=" + authority);
         pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
-        super.dumpBack(pw, prefix, flags);
+        super.dumpBack(pw, prefix, dumpFlags);
     }
 
     public int describeContents() {
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index f312204..7993167 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -282,7 +282,7 @@
     }
 
     /** @hide */
-    public void dump(Printer pw, String prefix, int flags) {
+    public void dump(Printer pw, String prefix, int dumpFlags) {
         if (filter != null) {
             pw.println(prefix + "Filter:");
             filter.dump(pw, prefix + "  ");
@@ -302,13 +302,13 @@
         }
         if (activityInfo != null) {
             pw.println(prefix + "ActivityInfo:");
-            activityInfo.dump(pw, prefix + "  ", flags);
+            activityInfo.dump(pw, prefix + "  ", dumpFlags);
         } else if (serviceInfo != null) {
             pw.println(prefix + "ServiceInfo:");
-            serviceInfo.dump(pw, prefix + "  ", flags);
+            serviceInfo.dump(pw, prefix + "  ", dumpFlags);
         } else if (providerInfo != null) {
             pw.println(prefix + "ProviderInfo:");
-            providerInfo.dump(pw, prefix + "  ", flags);
+            providerInfo.dump(pw, prefix + "  ", dumpFlags);
         }
     }
 
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index c683ea5..91f884c 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -91,13 +91,13 @@
     }
 
     /** @hide */
-    void dump(Printer pw, String prefix, int flags) {
+    void dump(Printer pw, String prefix, int dumpFlags) {
         super.dumpFront(pw, prefix);
         pw.println(prefix + "permission=" + permission);
         pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
-        super.dumpBack(pw, prefix, flags);
+        super.dumpBack(pw, prefix, dumpFlags);
     }
-    
+
     public String toString() {
         return "ServiceInfo{"
             + Integer.toHexString(System.identityHashCode(this))
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 0627998..935f5f3 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -774,6 +774,11 @@
          * O MR1.
          */
         public static final int O_MR1 = 27;
+
+        /**
+         * P.
+         */
+        public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
     }
 
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index fe9e8c6..da0ed54 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -149,14 +149,43 @@
      * provide a better experience than you could otherwise build using the generic building
      * blocks.
      *
+     * This will fallback to a generic pattern if one exists and there does not exist a
+     * hardware-specific implementation of the effect.
+     *
      * @param effectId The ID of the effect to perform:
-     * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}.
+     *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
      *
      * @return The desired effect.
      * @hide
      */
     public static VibrationEffect get(int effectId) {
-        VibrationEffect effect = new Prebaked(effectId);
+        return get(effectId, true);
+    }
+
+    /**
+     * Get a predefined vibration effect.
+     *
+     * Predefined effects are a set of common vibration effects that should be identical, regardless
+     * of the app they come from, in order to provide a cohesive experience for users across
+     * the entire device. They also may be custom tailored to the device hardware in order to
+     * provide a better experience than you could otherwise build using the generic building
+     * blocks.
+     *
+     * Some effects you may only want to play if there's a hardware specific implementation because
+     * they may, for example, be too disruptive to the user without tuning. The {@code fallback}
+     * parameter allows you to decide whether you want to fallback to the generic implementation or
+     * only play if there's a tuned, hardware specific one available.
+     *
+     * @param effectId The ID of the effect to perform:
+     *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
+     * @param fallback Whether to fallback to a generic pattern if a hardware specific
+     *                 implementation doesn't exist.
+     *
+     * @return The desired effect.
+     * @hide
+     */
+    public static VibrationEffect get(int effectId, boolean fallback) {
+        VibrationEffect effect = new Prebaked(effectId, fallback);
         effect.validate();
         return effect;
     }
@@ -374,19 +403,29 @@
     /** @hide */
     public static class Prebaked extends VibrationEffect implements Parcelable {
         private int mEffectId;
+        private boolean mFallback;
 
         public Prebaked(Parcel in) {
-            this(in.readInt());
+            this(in.readInt(), in.readByte() != 0);
         }
 
-        public Prebaked(int effectId) {
+        public Prebaked(int effectId, boolean fallback) {
             mEffectId = effectId;
+            mFallback = fallback;
         }
 
         public int getId() {
             return mEffectId;
         }
 
+        /**
+         * Whether the effect should fall back to a generic pattern if there's no hardware specific
+         * implementation of it.
+         */
+        public boolean shouldFallback() {
+            return mFallback;
+        }
+
         @Override
         public void validate() {
             switch (mEffectId) {
@@ -406,7 +445,7 @@
                 return false;
             }
             VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
-            return mEffectId == other.mEffectId;
+            return mEffectId == other.mEffectId && mFallback == other.mFallback;
         }
 
         @Override
@@ -416,7 +455,7 @@
 
         @Override
         public String toString() {
-            return "Prebaked{mEffectId=" + mEffectId + "}";
+            return "Prebaked{mEffectId=" + mEffectId + ", mFallback=" + mFallback + "}";
         }
 
 
@@ -424,6 +463,7 @@
         public void writeToParcel(Parcel out, int flags) {
             out.writeInt(PARCEL_TOKEN_EFFECT);
             out.writeInt(mEffectId);
+            out.writeByte((byte) (mFallback ? 1 : 0));
         }
 
         public static final Parcelable.Creator<Prebaked> CREATOR =
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index f7dc1c5..60c1c9a 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -81,10 +81,13 @@
     /**
      * Returns the client state set in the previous {@link FillResponse}.
      *
-     * <p><b>NOTE: </b>the state is associated with the app that was autofilled in the previous
+     * <p><b>Note: </b>the state is associated with the app that was autofilled in the previous
      * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
      * , which is not necessary the same app being autofilled now.
+     *
+     * @deprecated use {@link #getEvents()} then {@link Event#getClientState()} instead.
      */
+    @Deprecated
     @Nullable public Bundle getClientState() {
         return mClientState;
     }
@@ -126,7 +129,6 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeBundle(mClientState);
-
         if (mEvents == null) {
             dest.writeInt(0);
         } else {
@@ -137,6 +139,7 @@
                 Event event = mEvents.get(i);
                 dest.writeInt(event.getType());
                 dest.writeString(event.getDatasetId());
+                dest.writeBundle(event.getClientState());
             }
         }
     }
@@ -177,6 +180,7 @@
 
         @EventIds private final int mEventType;
         @Nullable private final String mDatasetId;
+        @Nullable private final Bundle mClientState;
 
         /**
          * Returns the type of the event.
@@ -197,18 +201,32 @@
         }
 
         /**
+         * Returns the client state from the {@link FillResponse} used to generate this event.
+         *
+         * <p><b>Note: </b>the state is associated with the app that was autofilled in the previous
+         * {@link
+         * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)},
+         * which is not necessary the same app being autofilled now.
+         */
+        @Nullable public Bundle getClientState() {
+            return mClientState;
+        }
+
+        /**
          * Creates a new event.
          *
          * @param eventType The type of the event
          * @param datasetId The dataset the event was on, or {@code null} if the event was on the
          *                  whole response.
+         * @param clientState The client state associated with the event.
          *
          * @hide
          */
-        public Event(int eventType, String datasetId) {
+        public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState) {
             mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_SAVE_SHOWN,
                     "eventType");
             mDatasetId = datasetId;
+            mClientState = clientState;
         }
     }
 
@@ -220,7 +238,8 @@
 
                     int numEvents = parcel.readInt();
                     for (int i = 0; i < numEvents; i++) {
-                        selection.addEvent(new Event(parcel.readInt(), parcel.readString()));
+                        selection.addEvent(new Event(parcel.readInt(), parcel.readString(),
+                                parcel.readBundle()));
                     }
 
                     return selection;
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index b6a9a26..3b09c67 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -302,7 +302,7 @@
         // TODO: create a dump() method instead
         return new StringBuilder(
                 "FillResponse : [mRequestId=" + mRequestId)
-                .append(", datasets=").append(mDatasets)
+                .append(", datasets=").append(mDatasets == null ? "N/A" : mDatasets.getList())
                 .append(", saveInfo=").append(mSaveInfo)
                 .append(", clientState=").append(mClientState != null)
                 .append(", hasPresentation=").append(mPresentation != null)
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
new file mode 100644
index 0000000..0be1a8c
--- /dev/null
+++ b/core/java/android/util/StatsLog.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007 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.util;
+
+/**
+ * Logging access for platform metrics.
+ *
+ * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
+ * These diagnostic stats are for system integrators, not application authors.
+ *
+ * <p>Stats use integer tag codes.
+ * They carry a payload of one or more int, long, or String values.
+ * @hide
+ */
+public class StatsLog {
+    /** @hide */ public StatsLog() {}
+
+    private static final String TAG = "StatsLog";
+
+    // We assume that the native methods deal with any concurrency issues.
+
+    /**
+     * Records an stats log message.
+     * @param tag The stats type tag code
+     * @param value A value to log
+     * @return The number of bytes written
+     */
+    public static native int writeInt(int tag, int value);
+
+    /**
+     * Records an stats log message.
+     * @param tag The stats type tag code
+     * @param value A value to log
+     * @return The number of bytes written
+     */
+    public static native int writeLong(int tag, long value);
+
+    /**
+     * Records an stats log message.
+     * @param tag The stats type tag code
+     * @param value A value to log
+     * @return The number of bytes written
+     */
+    public static native int writeFloat(int tag, float value);
+
+    /**
+     * Records an stats log message.
+     * @param tag The stats type tag code
+     * @param str A value to log
+     * @return The number of bytes written
+     */
+    public static native int writeString(int tag, String str);
+
+    /**
+     * Records an stats log message.
+     * @param tag The stats type tag code
+     * @param list A list of values to log. All values should
+     * be of type int, long, float or String.
+     * @return The number of bytes written
+     */
+    public static native int writeArray(int tag, Object... list);
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ffb3203..e5bd5ac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7621,6 +7621,9 @@
      *   <li>Call
      *    {@link android.view.autofill.AutofillManager#notifyValueChanged(View, int, AutofillValue)}
      *       when the value of a virtual child changed.
+     *   <li>Call {@link
+     *    android.view.autofill.AutofillManager#notifyViewVisibilityChanged(View, int, boolean)}
+     *       when the visibility of a virtual child changed.
      *   <li>Call {@link AutofillManager#commit()} when the autofill context of the view structure
      *       changed and the current context should be committed (for example, when the user tapped
      *       a {@code SUBMIT} button in an HTML page).
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5adbdbe..46742b8 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -73,6 +73,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -188,17 +191,12 @@
 
     private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
 
-    private static final Object[] sMethodsLock = new Object[0];
-    private static final ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>> sMethods =
-            new ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>>();
-    private static final ArrayMap<Method, Method> sAsyncMethods = new ArrayMap<>();
+    private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
 
-    private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
-        @Override
-        protected Object[] initialValue() {
-            return new Object[1];
-        }
-    };
+    /**
+     * This key is used to perform lookups in sMethods without causing allocations.
+     */
+    private static final MethodKey sLookupKey = new MethodKey();
 
     /**
      * @hide
@@ -255,37 +253,47 @@
     }
 
     /**
-     * Handle with care!
+     * Stores information related to reflection method lookup.
      */
-    static class MutablePair<F, S> {
-        F first;
-        S second;
-
-        MutablePair(F first, S second) {
-            this.first = first;
-            this.second = second;
-        }
+    static class MethodKey {
+        public Class targetClass;
+        public Class paramClass;
+        public String methodName;
 
         @Override
         public boolean equals(Object o) {
-            if (!(o instanceof MutablePair)) {
+            if (!(o instanceof MethodKey)) {
                 return false;
             }
-            MutablePair<?, ?> p = (MutablePair<?, ?>) o;
-            return Objects.equal(p.first, first) && Objects.equal(p.second, second);
+            MethodKey p = (MethodKey) o;
+            return Objects.equal(p.targetClass, targetClass)
+                    && Objects.equal(p.paramClass, paramClass)
+                    && Objects.equal(p.methodName, methodName);
         }
 
         @Override
         public int hashCode() {
-            return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
+            return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass)
+                    ^ Objects.hashCode(methodName);
+        }
+
+        public void set(Class targetClass, Class paramClass, String methodName) {
+            this.targetClass = targetClass;
+            this.paramClass = paramClass;
+            this.methodName = methodName;
         }
     }
 
+
     /**
-     * This pair is used to perform lookups in sMethods without causing allocations.
+     * Stores information related to reflection method lookup result.
      */
-    private final MutablePair<String, Class<?>> mPair =
-            new MutablePair<String, Class<?>>(null, null);
+    static class MethodArgs {
+        public MethodHandle syncMethod;
+        public MethodHandle asyncMethod;
+        public String asyncMethodName;
+    }
+
 
     /**
      * This annotation indicates that a subclass of View is allowed to be used
@@ -307,6 +315,12 @@
         public ActionException(String message) {
             super(message);
         }
+        /**
+         * @hide
+         */
+        public ActionException(Throwable t) {
+            super(t);
+        }
     }
 
     /** @hide */
@@ -943,73 +957,66 @@
         return rect;
     }
 
-    private Method getMethod(View view, String methodName, Class<?> paramType) {
-        Method method;
+    private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
+            boolean async) {
+        MethodArgs result;
         Class<? extends View> klass = view.getClass();
 
-        synchronized (sMethodsLock) {
-            ArrayMap<MutablePair<String, Class<?>>, Method> methods = sMethods.get(klass);
-            if (methods == null) {
-                methods = new ArrayMap<MutablePair<String, Class<?>>, Method>();
-                sMethods.put(klass, methods);
-            }
+        synchronized (sMethods) {
+            // The key is defined by the view class, param class and method name.
+            sLookupKey.set(klass, paramType, methodName);
+            result = sMethods.get(sLookupKey);
 
-            mPair.first = methodName;
-            mPair.second = paramType;
-
-            method = methods.get(mPair);
-            if (method == null) {
+            if (result == null) {
+                Method method;
                 try {
                     if (paramType == null) {
                         method = klass.getMethod(methodName);
                     } else {
                         method = klass.getMethod(methodName, paramType);
                     }
-                } catch (NoSuchMethodException ex) {
+                    if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
+                        throw new ActionException("view: " + klass.getName()
+                                + " can't use method with RemoteViews: "
+                                + methodName + getParameters(paramType));
+                    }
+
+                    result = new MethodArgs();
+                    result.syncMethod = MethodHandles.publicLookup().unreflect(method);
+                    result.asyncMethodName =
+                            method.getAnnotation(RemotableViewMethod.class).asyncImpl();
+                } catch (NoSuchMethodException | IllegalAccessException ex) {
                     throw new ActionException("view: " + klass.getName() + " doesn't have method: "
                             + methodName + getParameters(paramType));
                 }
 
-                if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
-                    throw new ActionException("view: " + klass.getName()
-                            + " can't use method with RemoteViews: "
-                            + methodName + getParameters(paramType));
-                }
-
-                methods.put(new MutablePair<String, Class<?>>(methodName, paramType), method);
-            }
-        }
-
-        return method;
-    }
-
-    /**
-     * @return the async implementation of the provided method.
-     */
-    private Method getAsyncMethod(Method method) {
-        synchronized (sAsyncMethods) {
-            int valueIndex = sAsyncMethods.indexOfKey(method);
-            if (valueIndex >= 0) {
-                return sAsyncMethods.valueAt(valueIndex);
+                MethodKey key = new MethodKey();
+                key.set(klass, paramType, methodName);
+                sMethods.put(key, result);
             }
 
-            RemotableViewMethod annotation = method.getAnnotation(RemotableViewMethod.class);
-            Method asyncMethod = null;
-            if (!annotation.asyncImpl().isEmpty()) {
+            if (!async) {
+                return result.syncMethod;
+            }
+            // Check this so see if async method is implemented or not.
+            if (result.asyncMethodName.isEmpty()) {
+                return null;
+            }
+            // Async method is lazily loaded. If it is not yet loaded, load now.
+            if (result.asyncMethod == null) {
+                MethodType asyncType = result.syncMethod.type()
+                        .dropParameterTypes(0, 1).changeReturnType(Runnable.class);
                 try {
-                    asyncMethod = method.getDeclaringClass()
-                            .getMethod(annotation.asyncImpl(), method.getParameterTypes());
-                    if (!asyncMethod.getReturnType().equals(Runnable.class)) {
-                        throw new ActionException("Async implementation for " + method.getName() +
-                            " does not return a Runnable");
-                    }
-                } catch (NoSuchMethodException ex) {
-                    throw new ActionException("Async implementation declared but not defined for " +
-                            method.getName());
+                    result.asyncMethod = MethodHandles.publicLookup().findVirtual(
+                            klass, result.asyncMethodName, asyncType);
+                } catch (NoSuchMethodException | IllegalAccessException ex) {
+                    throw new ActionException("Async implementation declared as "
+                            + result.asyncMethodName + " but not defined for " + methodName
+                            + ": public Runnable " + result.asyncMethodName + " ("
+                            + TextUtils.join(",", asyncType.parameterArray()) + ")");
                 }
             }
-            sAsyncMethods.put(method, asyncMethod);
-            return asyncMethod;
+            return result.asyncMethod;
         }
     }
 
@@ -1018,12 +1025,6 @@
         return "(" + paramType + ")";
     }
 
-    private static Object[] wrapArg(Object value) {
-        Object[] args = sInvokeArgsTls.get();
-        args[0] = value;
-        return args;
-    }
-
     /**
      * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
@@ -1140,10 +1141,8 @@
             if (view == null) return;
 
             try {
-                getMethod(view, this.methodName, null).invoke(view);
-            } catch (ActionException e) {
-                throw e;
-            } catch (Exception ex) {
+                getMethod(view, this.methodName, null, false /* async */).invoke(view);
+            } catch (Throwable ex) {
                 throw new ActionException(ex);
             }
         }
@@ -1516,12 +1515,9 @@
             if (param == null) {
                 throw new ActionException("bad type: " + this.type);
             }
-
             try {
-                getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
-            } catch (ActionException e) {
-                throw e;
-            } catch (Exception ex) {
+                getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
+            } catch (Throwable ex) {
                 throw new ActionException(ex);
             }
         }
@@ -1537,11 +1533,10 @@
             }
 
             try {
-                Method method = getMethod(view, this.methodName, param);
-                Method asyncMethod = getAsyncMethod(method);
+                MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
 
-                if (asyncMethod != null) {
-                    Runnable endAction = (Runnable) asyncMethod.invoke(view, wrapArg(this.value));
+                if (method != null) {
+                    Runnable endAction = (Runnable) method.invoke(view, this.value);
                     if (endAction == null) {
                         return ACTION_NOOP;
                     } else {
@@ -1555,9 +1550,7 @@
                         return new RunnableAction(endAction);
                     }
                 }
-            } catch (ActionException e) {
-                throw e;
-            } catch (Exception ex) {
+            } catch (Throwable ex) {
                 throw new ActionException(ex);
             }
 
@@ -2672,7 +2665,7 @@
      * given {@link RemoteViews}.
      *
      * @param viewId The id of the parent {@link ViewGroup} to add the child into.
-     * @param nestedView {@link RemoveViews} of the child to add.
+     * @param nestedView {@link RemoteViews} of the child to add.
      * @param index The position at which to add the child.
      *
      * @hide
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 2561ffe..cbb8d88 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -248,6 +248,9 @@
         // with the Smart Select animation
         layout.getSelection(start, end, (left, top, right, bottom) ->
                 result.add(new RectF(left, top, right, bottom)));
+
+        result.sort(SmartSelectSprite.RECTANGLE_COMPARATOR);
+
         return result;
     }
 
diff --git a/core/java/android/widget/SmartSelectSprite.java b/core/java/android/widget/SmartSelectSprite.java
index 844c714..45466c2 100644
--- a/core/java/android/widget/SmartSelectSprite.java
+++ b/core/java/android/widget/SmartSelectSprite.java
@@ -75,6 +75,10 @@
     private final int mStrokeColor;
     private Set<Drawable> mExistingAnimationDrawables = new HashSet<>();
 
+    static final Comparator<RectF> RECTANGLE_COMPARATOR = Comparator
+            .<RectF>comparingDouble(e -> e.bottom)
+            .thenComparingDouble(e -> e.left);
+
     /**
      * Represents a set of points connected by lines.
      */
@@ -110,7 +114,7 @@
      */
     private static final class RoundedRectangleShape extends Shape {
 
-        private static final String PROPERTY_ROUND_PERCENTAGE = "roundPercentage";
+        private static final String PROPERTY_ROUND_RATIO = "roundRatio";
 
         @Retention(SOURCE)
         @IntDef({ExpansionDirection.LEFT, ExpansionDirection.CENTER, ExpansionDirection.RIGHT})
@@ -134,7 +138,7 @@
 
         private final float mStrokeWidth;
         private final RectF mBoundingRectangle;
-        private float mRoundPercentage = 1.0f;
+        private float mRoundRatio = 1.0f;
         private final @ExpansionDirection int mExpansionDirection;
         private final @RectangleBorderType int mRectangleBorderType;
 
@@ -158,9 +162,9 @@
             mStrokeWidth = strokeWidth;
 
             if (boundingRectangle.height() > boundingRectangle.width()) {
-                setRoundPercentage(0.0f);
+                setRoundRatio(0.0f);
             } else {
-                setRoundPercentage(1.0f);
+                setRoundRatio(1.0f);
             }
         }
 
@@ -218,13 +222,12 @@
             canvas.restore();
         }
 
-        public void setRoundPercentage(
-                @FloatRange(from = 0.0, to = 1.0) final float newPercentage) {
-            mRoundPercentage = newPercentage;
+        public void setRoundRatio(@FloatRange(from = 0.0, to = 1.0) final float roundRatio) {
+            mRoundRatio = roundRatio;
         }
 
-        public float getRoundPercentage() {
-            return mRoundPercentage;
+        public float getRoundRatio() {
+            return mRoundRatio;
         }
 
         private void setLeftBoundary(final float leftBoundary) {
@@ -240,7 +243,7 @@
         }
 
         private float getAdjustedCornerRadius() {
-            return (getCornerRadius() * mRoundPercentage);
+            return (getCornerRadius() * mRoundRatio);
         }
 
         private float getBoundingWidth() {
@@ -267,13 +270,6 @@
 
         private RectangleList(List<RoundedRectangleShape> rectangles) {
             mRectangles = new LinkedList<>(rectangles);
-            mRectangles.sort((o1, o2) -> {
-                if (o1.mBoundingRectangle.top == o2.mBoundingRectangle.top) {
-                    return Float.compare(o1.mBoundingRectangle.left, o2.mBoundingRectangle.left);
-                } else {
-                    return Float.compare(o1.mBoundingRectangle.top, o2.mBoundingRectangle.top);
-                }
-            });
             mReversedRectangles = new LinkedList<>(rectangles);
             Collections.reverse(mReversedRectangles);
         }
@@ -425,7 +421,9 @@
      * @param start                 The point from which the animation will start. Must be inside
      *                              destinationRectangles.
      * @param destinationRectangles The rectangles which the animation will fill out by its
-     *                              "selection" and finally join them into a single polygon.
+     *                              "selection" and finally join them into a single polygon. In
+     *                              order to get the correct visual behavior, these rectangles
+     *                              should be sorted according to {@link #RECTANGLE_COMPARATOR}.
      * @param onAnimationEnd        The callback which will be invoked once the whole animation
      *                              completes.
      * @throws IllegalArgumentException if the given start point is not in any of the
@@ -583,8 +581,8 @@
             final ValueAnimator.AnimatorUpdateListener listener) {
         final ObjectAnimator animator = ObjectAnimator.ofFloat(
                 shape,
-                RoundedRectangleShape.PROPERTY_ROUND_PERCENTAGE,
-                shape.getRoundPercentage(), 0.0F);
+                RoundedRectangleShape.PROPERTY_ROUND_RATIO,
+                shape.getRoundRatio(), 0.0F);
         animator.setDuration(CORNER_DURATION);
         animator.addUpdateListener(listener);
         animator.setInterpolator(mCornerInterpolator);
@@ -592,8 +590,7 @@
     }
 
     private static @RoundedRectangleShape.ExpansionDirection int[] generateDirections(
-            final RectF centerRectangle,
-            final List<RectF> rectangles) throws IllegalArgumentException {
+            final RectF centerRectangle, final List<RectF> rectangles) {
         final @RoundedRectangleShape.ExpansionDirection int[] result = new int[rectangles.size()];
 
         final int centerRectangleIndex = rectangles.indexOf(centerRectangle);
@@ -601,7 +598,17 @@
         for (int i = 0; i < centerRectangleIndex - 1; ++i) {
             result[i] = RoundedRectangleShape.ExpansionDirection.LEFT;
         }
-        result[centerRectangleIndex] = RoundedRectangleShape.ExpansionDirection.CENTER;
+
+        if (rectangles.size() == 1) {
+            result[centerRectangleIndex] = RoundedRectangleShape.ExpansionDirection.CENTER;
+        } else if (centerRectangleIndex == 0) {
+            result[centerRectangleIndex] = RoundedRectangleShape.ExpansionDirection.LEFT;
+        } else if (centerRectangleIndex == rectangles.size() - 1) {
+            result[centerRectangleIndex] = RoundedRectangleShape.ExpansionDirection.RIGHT;
+        } else {
+            result[centerRectangleIndex] = RoundedRectangleShape.ExpansionDirection.CENTER;
+        }
+
         for (int i = centerRectangleIndex + 1; i < result.length; ++i) {
             result[i] = RoundedRectangleShape.ExpansionDirection.RIGHT;
         }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 80f4b99..ceb06f5 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1941,7 +1941,8 @@
             final int checkedPos = mAdapterView.getCheckedItemPosition();
             final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
             if (!useLayoutWithDefault()
-                    && (!hasValidSelection || mLastSelected != checkedPos)) {
+                    && (!hasValidSelection || mLastSelected != checkedPos)
+                    && mAlwaysButton != null) {
                 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
                 mOnceButton.setEnabled(hasValidSelection);
                 if (hasValidSelection) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index bd94fc7..8ea0242 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1807,7 +1807,7 @@
             mFloatingActionMode.finish();
         }
         cleanupFloatingActionModeViews();
-        mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
+        mFloatingToolbar = new FloatingToolbar(mWindow);
         final FloatingActionMode mode =
                 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
         mFloatingActionModeOriginatingView = originatingView;
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 1d56e1a..f63b5a2 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -120,8 +120,10 @@
     /**
      * Initializes a floating toolbar.
      */
-    public FloatingToolbar(Context context, Window window) {
-        mContext = applyDefaultTheme(Preconditions.checkNotNull(context));
+    public FloatingToolbar(Window window) {
+        // TODO(b/65172902): Pass context in constructor when DecorView (and other callers)
+        // supports multi-display.
+        mContext = applyDefaultTheme(window.getContext());
         mWindow = Preconditions.checkNotNull(window);
         mPopup = new FloatingToolbarPopup(mContext, window.getDecorView());
     }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c629341..d63e22c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -103,6 +103,7 @@
         "android_nio_utils.cpp",
         "android_util_AssetManager.cpp",
         "android_util_Binder.cpp",
+	"android_util_StatsLog.cpp",
         "android_util_EventLog.cpp",
         "android_util_MemoryIntArray.cpp",
         "android_util_Log.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5afd067..02c9848 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -111,6 +111,7 @@
 extern int register_android_app_admin_SecurityLog(JNIEnv* env);
 extern int register_android_content_AssetManager(JNIEnv* env);
 extern int register_android_util_EventLog(JNIEnv* env);
+extern int register_android_util_StatsLog(JNIEnv* env);
 extern int register_android_util_Log(JNIEnv* env);
 extern int register_android_util_MemoryIntArray(JNIEnv* env);
 extern int register_android_util_PathParser(JNIEnv* env);
@@ -1311,6 +1312,7 @@
     REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
     REG_JNI(register_android_os_SystemClock),
     REG_JNI(register_android_util_EventLog),
+    REG_JNI(register_android_util_StatsLog),
     REG_JNI(register_android_util_Log),
     REG_JNI(register_android_util_MemoryIntArray),
     REG_JNI(register_android_util_PathParser),
diff --git a/core/jni/android_util_StatsLog.cpp b/core/jni/android_util_StatsLog.cpp
new file mode 100644
index 0000000..c992365
--- /dev/null
+++ b/core/jni/android_util_StatsLog.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2007-2014 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.
+ */
+
+#include <fcntl.h>
+#include <log/log_event_list.h>
+
+#include <log/log.h>
+
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+#define UNUSED  __attribute__((__unused__))
+
+namespace android {
+
+static jclass gCollectionClass;
+static jmethodID gCollectionAddID;
+
+static jclass gIntegerClass;
+static jfieldID gIntegerValueID;
+
+static jclass gLongClass;
+static jfieldID gLongValueID;
+
+static jclass gFloatClass;
+static jfieldID gFloatValueID;
+
+static jclass gStringClass;
+
+/*
+ * In class android.util.StatsLog:
+ *  static native int writeInt(int tag, int value)
+ */
+static jint android_util_StatsLog_write_Integer(JNIEnv* env UNUSED,
+                                                     jobject clazz UNUSED,
+                                                     jint tag, jint value)
+{
+    android_log_event_list ctx(tag);
+    ctx << (int32_t)value;
+    return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ *  static native int writeLong(long tag, long value)
+ */
+static jint android_util_StatsLog_write_Long(JNIEnv* env UNUSED,
+                                                  jobject clazz UNUSED,
+                                                  jint tag, jlong value)
+{
+    android_log_event_list ctx(tag);
+    ctx << (int64_t)value;
+    return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ *  static native int writeFloat(long tag, float value)
+ */
+static jint android_util_StatsLog_write_Float(JNIEnv* env UNUSED,
+                                                  jobject clazz UNUSED,
+                                                  jint tag, jfloat value)
+{
+    android_log_event_list ctx(tag);
+    ctx << (float)value;
+    return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ *  static native int writeString(int tag, String value)
+ */
+static jint android_util_StatsLog_write_String(JNIEnv* env,
+                                                    jobject clazz UNUSED,
+                                                    jint tag, jstring value) {
+    android_log_event_list ctx(tag);
+    // Don't throw NPE -- I feel like it's sort of mean for a logging function
+    // to be all crashy if you pass in NULL -- but make the NULL value explicit.
+    if (value != NULL) {
+        const char *str = env->GetStringUTFChars(value, NULL);
+        ctx << str;
+        env->ReleaseStringUTFChars(value, str);
+    } else {
+        ctx << "NULL";
+    }
+    return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ *  static native int writeArray(long tag, Object... value)
+ */
+static jint android_util_StatsLog_write_Array(JNIEnv* env, jobject clazz,
+                                                   jint tag, jobjectArray value) {
+    android_log_event_list ctx(tag);
+
+    if (value == NULL) {
+        ctx << "[NULL]";
+        return ctx.write(LOG_ID_STATS);
+    }
+
+    jsize copied = 0, num = env->GetArrayLength(value);
+    for (; copied < num && copied < 255; ++copied) {
+        if (ctx.status()) break;
+        jobject item = env->GetObjectArrayElement(value, copied);
+        if (item == NULL) {
+            ctx << "NULL";
+        } else if (env->IsInstanceOf(item, gStringClass)) {
+            const char *str = env->GetStringUTFChars((jstring) item, NULL);
+            ctx << str;
+            env->ReleaseStringUTFChars((jstring) item, str);
+        } else if (env->IsInstanceOf(item, gIntegerClass)) {
+            ctx << (int32_t)env->GetIntField(item, gIntegerValueID);
+        } else if (env->IsInstanceOf(item, gLongClass)) {
+            ctx << (int64_t)env->GetLongField(item, gLongValueID);
+        } else if (env->IsInstanceOf(item, gFloatClass)) {
+            ctx << (float)env->GetFloatField(item, gFloatValueID);
+        } else {
+            jniThrowException(env,
+                    "java/lang/IllegalArgumentException",
+                    "Invalid payload item type");
+            return -1;
+        }
+        env->DeleteLocalRef(item);
+    }
+    return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gRegisterMethods[] = {
+    /* name, signature, funcPtr */
+    { "writeInt", "(II)I", (void*) android_util_StatsLog_write_Integer },
+    { "writeLong", "(IJ)I", (void*) android_util_StatsLog_write_Long },
+    { "writeFloat", "(IF)I", (void*) android_util_StatsLog_write_Float },
+    { "writeString",
+      "(ILjava/lang/String;)I",
+      (void*) android_util_StatsLog_write_String
+    },
+    { "writeArray",
+      "(I[Ljava/lang/Object;)I",
+      (void*) android_util_StatsLog_write_Array
+    },
+};
+
+static struct { const char *name; jclass *clazz; } gClasses[] = {
+    { "java/lang/Integer", &gIntegerClass },
+    { "java/lang/Long", &gLongClass },
+    { "java/lang/Float", &gFloatClass },
+    { "java/lang/String", &gStringClass },
+    { "java/util/Collection", &gCollectionClass },
+};
+
+static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
+    { &gIntegerClass, "value", "I", &gIntegerValueID },
+    { &gLongClass, "value", "J", &gLongValueID },
+    { &gFloatClass, "value", "F", &gFloatValueID },
+};
+
+static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
+    { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
+};
+
+int register_android_util_StatsLog(JNIEnv* env) {
+    for (int i = 0; i < NELEM(gClasses); ++i) {
+        jclass clazz = FindClassOrDie(env, gClasses[i].name);
+        *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz);
+    }
+
+    for (int i = 0; i < NELEM(gFields); ++i) {
+        *gFields[i].id = GetFieldIDOrDie(env,
+                *gFields[i].c, gFields[i].name, gFields[i].ft);
+    }
+
+    for (int i = 0; i < NELEM(gMethods); ++i) {
+        *gMethods[i].id = GetMethodIDOrDie(env,
+                *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
+    }
+
+    return RegisterMethodsOrDie(
+            env,
+            "android/util/StatsLog",
+            gRegisterMethods, NELEM(gRegisterMethods));
+}
+
+}; // namespace android
diff --git a/core/res/res/drawable/ic_corp_icon.xml b/core/res/res/drawable/ic_corp_icon.xml
index a6b68f1..48531dd 100644
--- a/core/res/res/drawable/ic_corp_icon.xml
+++ b/core/res/res/drawable/ic_corp_icon.xml
@@ -1,12 +1,9 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="48dp"
         android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
     <path
-        android:pathData="M24,24m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
-        android:fillColor="#FF6D00"/>
-    <path
-        android:pathData="M35.2,15.6h-5.6v-2.8c0,-1.55 -1.25,-2.8 -2.8,-2.8h-5.6c-1.55,0 -2.8,1.25 -2.8,2.8v2.8h-5.6c-1.55,0 -2.79,1.25 -2.79,2.8L10,33.8c0,1.55 1.25,2.8 2.8,2.8h22.4c1.55,0 2.8,-1.25 2.8,-2.8V18.4C38,16.85 36.75,15.6 35.2,15.6zM24,28.2c-1.54,0 -2.8,-1.26 -2.8,-2.8s1.26,-2.8 2.8,-2.8c1.54,0 2.8,1.26 2.8,2.8S25.54,28.2 24,28.2zM26.8,15.6h-5.6v-2.8h5.6V15.6z"
-        android:fillColor="#FFFFFF"/>
+        android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM14,6h-4L10,4h4v2z"
+        android:fillColor="#000000"/>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 7e9f561..f5b350b 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -181,7 +181,7 @@
     <shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
 
     <!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
-    <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963" />
+    <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963|76551" />
 
     <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
     <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288" />
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 2627380..8def036 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -96,7 +96,9 @@
     public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
         final int iconSize = UserIconDrawable.getSizeForList(context);
         if (user.isManagedProfile()) {
-            return context.getDrawable(com.android.internal.R.drawable.ic_corp_icon);
+            Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_icon);
+            drawable.setBounds(0, 0, iconSize, iconSize);
+            return drawable;
         }
         if (user.iconPath != null) {
             Bitmap icon = um.getUserIcon(user.id);
diff --git a/packages/SystemUI/res/layout/navigation_layout.xml b/packages/SystemUI/res/layout/navigation_layout.xml
index 53f5dfe..3e60794 100644
--- a/packages/SystemUI/res/layout/navigation_layout.xml
+++ b/packages/SystemUI/res/layout/navigation_layout.xml
@@ -21,8 +21,8 @@
     android:layout_height="match_parent"
     android:layout_marginStart="@dimen/rounded_corner_content_padding"
     android:layout_marginEnd="@dimen/rounded_corner_content_padding"
-    android:paddingStart="8dp"
-    android:paddingEnd="8dp">
+    android:paddingStart="@dimen/nav_content_padding"
+    android:paddingEnd="@dimen/nav_content_padding">
 
     <com.android.systemui.statusbar.phone.NearestTouchFrame
         android:id="@+id/nav_buttons"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 849bea4..2cf3e4a 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -37,6 +37,7 @@
         android:ellipsize="marquee"
         android:textAppearance="?android:attr/textAppearanceSmall"
         android:textColor="?android:attr/textColorPrimary"
+        android:textDirection="locale"
         android:singleLine="true" />
 
     <com.android.systemui.BatteryMeterView android:id="@+id/battery"
diff --git a/packages/SystemUI/res/values-sw372dp/dimens.xml b/packages/SystemUI/res/values-sw372dp/dimens.xml
new file mode 100644
index 0000000..635185d
--- /dev/null
+++ b/packages/SystemUI/res/values-sw372dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2006, 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.
+*/
+-->
+<resources>
+    <dimen name="nav_content_padding">8dp</dimen>
+    <dimen name="rounded_corner_content_padding">8dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c8e6021..54421f74 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -846,7 +846,8 @@
     <dimen name="edge_margin">16dp</dimen>
 
     <dimen name="rounded_corner_radius">0dp</dimen>
-    <dimen name="rounded_corner_content_padding">8dp</dimen>
+    <dimen name="rounded_corner_content_padding">0dp</dimen>
+    <dimen name="nav_content_padding">0dp</dimen>
 
     <!-- Intended corner radius when drawing the mobile signal -->
     <dimen name="stat_sys_mobile_signal_corner_radius">0.75dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index c4de63b..fd2447b 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -18,6 +18,7 @@
 import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
 
 import android.animation.ArgbEvaluator;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -40,6 +41,7 @@
 
 import com.android.settingslib.Utils;
 import com.android.settingslib.graph.BatteryMeterDrawableBase;
+import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -58,6 +60,7 @@
     private final BatteryMeterDrawableBase mDrawable;
     private final String mSlotBattery;
     private final ImageView mBatteryIconView;
+    private final CurrentUserTracker mUserTracker;
     private TextView mBatteryPercentView;
 
     private BatteryController mBatteryController;
@@ -72,6 +75,7 @@
     private int mLightModeBackgroundColor;
     private int mLightModeFillColor;
     private float mDarkIntensity;
+    private int mUser;
 
     public BatteryMeterView(Context context) {
         this(context, null, 0);
@@ -120,6 +124,16 @@
 
         // Init to not dark at all.
         onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
+        mUserTracker = new CurrentUserTracker(mContext) {
+            @Override
+            public void onUserSwitched(int newUserId) {
+                mUser = newUserId;
+                getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
+                getContext().getContentResolver().registerContentObserver(
+                        Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
+                        newUserId);
+            }
+        };
     }
 
     public void setForceShowPercent(boolean show) {
@@ -145,16 +159,19 @@
         super.onAttachedToWindow();
         mBatteryController = Dependency.get(BatteryController.class);
         mBatteryController.addCallback(this);
+        mUser = ActivityManager.getCurrentUser();
         getContext().getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver);
+                Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
         updateShowPercent();
         Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
         Dependency.get(ConfigurationController.class).addCallback(this);
+        mUserTracker.startTracking();
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
+        mUserTracker.stopTracking();
         mBatteryController.removeCallback(this);
         getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
         Dependency.get(TunerService.class).removeTunable(this);
@@ -191,8 +208,8 @@
 
     private void updateShowPercent() {
         final boolean showing = mBatteryPercentView != null;
-        if (0 != Settings.System.getInt(getContext().getContentResolver(),
-                SHOW_BATTERY_PERCENT, 0) || mForceShowPercent) {
+        if (0 != Settings.System.getIntForUser(getContext().getContentResolver(),
+                SHOW_BATTERY_PERCENT, 0, mUser) || mForceShowPercent) {
             if (!showing) {
                 mBatteryPercentView = loadPercentView();
                 if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
index f663315..82c0128 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -136,11 +136,12 @@
         return disableAny;
     }
 
-    public void disableAll() {
+    public boolean disableAll() {
         ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
         for (int i = 0; i < plugins.size(); i++) {
             disable(plugins.get(i));
         }
+        return plugins.size() != 0;
     }
 
     private void disable(PluginInfo info) {
@@ -182,6 +183,7 @@
                     if (DEBUG) Log.d(TAG, "onPluginConnected");
                     PluginPrefs.setHasPlugins(mContext);
                     PluginInfo<T> info = (PluginInfo<T>) msg.obj;
+                    mManager.handleWtfs();
                     if (!(msg.obj instanceof PluginFragment)) {
                         // Only call onDestroy for plugins that aren't fragments, as fragments
                         // will get the onCreate as part of the fragment lifecycle.
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
index 493d244..03747d5 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
@@ -36,6 +36,9 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
+import android.util.Log.TerribleFailure;
+import android.util.Log.TerribleFailureHandler;
 import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -71,10 +74,11 @@
     private boolean mListening;
     private boolean mHasOneShot;
     private Looper mLooper;
+    private boolean mWtfsSet;
 
     public PluginManagerImpl(Context context) {
         this(context, new PluginInstanceManagerFactory(),
-                Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
+                Build.IS_DEBUGGABLE, Thread.getUncaughtExceptionPreHandler());
     }
 
     @VisibleForTesting
@@ -88,7 +92,7 @@
 
         PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
                 defaultHandler);
-        Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
+        Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
         if (isDebuggable) {
             new Handler(mLooper).post(() -> {
                 // Plugin dependencies that don't have another good home can go here, but
@@ -290,6 +294,15 @@
         return false;
     }
 
+    public void handleWtfs() {
+        if (!mWtfsSet) {
+            mWtfsSet = true;
+            Log.setWtfHandler((tag, what, system) -> {
+                throw new CrashWhilePluginActiveException(what);
+            });
+        }
+    }
+
     @VisibleForTesting
     public static class PluginInstanceManagerFactory {
         public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
@@ -339,9 +352,12 @@
                 // disable all the plugins, so we can be sure that SysUI is running as
                 // best as possible.
                 for (PluginInstanceManager manager : mPluginMap.values()) {
-                    manager.disableAll();
+                    disabledAny |= manager.disableAll();
                 }
             }
+            if (disabledAny) {
+                throwable = new CrashWhilePluginActiveException(throwable);
+            }
 
             // Run the normal exception handler so we can crash and cleanup our state.
             mHandler.uncaughtException(thread, throwable);
@@ -358,4 +374,10 @@
             return disabledAny | checkStack(throwable.getCause());
         }
     }
+
+    private class CrashWhilePluginActiveException extends RuntimeException {
+        public CrashWhilePluginActiveException(Throwable throwable) {
+            super(throwable);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 10514a7..8b434a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -323,7 +323,7 @@
             post(new Runnable() {
                 @Override
                 public void run() {
-                    handleShowingDetail(detail, x, y, true /* toggleQs */);
+                    handleShowingDetail(detail, x, y, false /* toggleQs */);
                 }
             });
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index f91aa9a..b4cc4b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -184,7 +184,11 @@
             }
             view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
             final ImageView iv = (ImageView) view.findViewById(android.R.id.icon);
-            iv.setImageResource(item.icon);
+            if (item.iconDrawable != null) {
+                iv.setImageDrawable(item.iconDrawable);
+            } else {
+                iv.setImageResource(item.icon);
+            }
             iv.getOverlay().clear();
             if (item.overlay != null) {
                 item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize);
@@ -254,6 +258,7 @@
 
     public static class Item {
         public int icon;
+        public Drawable iconDrawable;
         public Drawable overlay;
         public CharSequence line1;
         public CharSequence line2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 0a0d2ce..bdc5e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -556,7 +556,7 @@
 
         @Override
         public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
-            if (viewHolder.getItemViewType() == TYPE_EDIT) {
+            if (viewHolder.getItemViewType() == TYPE_EDIT || viewHolder.getItemViewType() == TYPE_DIVIDER) {
                 return makeMovementFlags(0, 0);
             }
             int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 12fccda..bc6233d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs.tiles;
 
+import static com.android.settingslib.graph.BluetoothDeviceLayerDrawable.createLayerDrawable;
+
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
@@ -32,8 +34,10 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.R.drawable;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -127,6 +131,15 @@
             if (connected) {
                 state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connected);
                 state.label = mController.getLastDeviceName();
+                CachedBluetoothDevice lastDevice = mController.getLastDevice();
+                if (lastDevice != null) {
+                    int batteryLevel = lastDevice.getBatteryLevel();
+                    if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+                        BluetoothDeviceLayerDrawable drawable = createLayerDrawable(mContext,
+                                R.drawable.ic_qs_bluetooth_connected, batteryLevel);
+                        state.icon = new DrawableIcon(drawable);
+                    }
+                }
                 state.contentDescription = mContext.getString(
                         R.string.accessibility_bluetooth_name, state.label);
             } else if (state.isTransient) {
@@ -278,6 +291,8 @@
                         item.icon = R.drawable.ic_qs_bluetooth_connected;
                         int batteryLevel = device.getBatteryLevel();
                         if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+                            item.iconDrawable = createLayerDrawable(mContext, item.icon,
+                                    batteryLevel);
                             item.line2 = mContext.getString(
                                     R.string.quick_settings_connected_battery_level,
                                     Utils.formatPercentage(batteryLevel));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 74737c4..569e58d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -184,8 +184,15 @@
         mVisible = visible;
         mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
         if (visible) {
-            hideTransientIndication();
+            // If this is called after an error message was already shown, we should not clear it.
+            // Otherwise the error message won't be shown
+            if  (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
+                hideTransientIndication();
+            }
             updateIndication();
+        } else if (!visible) {
+            // If we unlock and return to keyguard quickly, previous error should not be shown
+            hideTransientIndication();
         }
     }
 
@@ -389,7 +396,6 @@
                 hideTransientIndication();
             } else if (msg.what == MSG_CLEAR_FP_MSG) {
                 mLockIcon.setTransientFpError(false);
-                hideTransientIndication();
             }
         }
     };
@@ -443,10 +449,10 @@
             int errorColor = Utils.getColorError(mContext);
             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
-            } else if (updateMonitor.isDeviceInteractive()
-                    || mDozing && updateMonitor.isScreenOn()) {
+            } else if (updateMonitor.isScreenOn()) {
                 mLockIcon.setTransientFpError(true);
                 showTransientIndication(helpString, errorColor);
+                hideTransientIndicationDelayed(TRANSIENT_FP_ERROR_TIMEOUT);
                 mHandler.removeMessages(MSG_CLEAR_FP_MSG);
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
                         TRANSIENT_FP_ERROR_TIMEOUT);
@@ -459,7 +465,8 @@
         @Override
         public void onFingerprintError(int msgId, String errString) {
             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
-            if (!updateMonitor.isUnlockingWithFingerprintAllowed()
+            if ((!updateMonitor.isUnlockingWithFingerprintAllowed()
+                    && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
                     || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
                 return;
             }
@@ -472,7 +479,7 @@
                 if (mLastSuccessiveErrorMessage != msgId) {
                     mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
                 }
-            } else if (updateMonitor.isDeviceInteractive()) {
+            } else if (updateMonitor.isScreenOn()) {
                 showTransientIndication(errString, errorColor);
                 // We want to keep this message around in case the screen was off
                 hideTransientIndicationDelayed(HIDE_DELAY_MS);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 03f42a6..d7f11f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -422,7 +422,7 @@
             mFloatingActionMode.finish();
         }
         cleanupFloatingActionModeViews();
-        mFloatingToolbar = new FloatingToolbar(mContext, mFakeWindow);
+        mFloatingToolbar = new FloatingToolbar(mFakeWindow);
         final FloatingActionMode mode =
                 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
         mFloatingActionModeOriginatingView = originatingView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index 9daa199..b693ebb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -39,6 +39,7 @@
 
     int getMaxConnectionState(CachedBluetoothDevice device);
     int getBondState(CachedBluetoothDevice device);
+    CachedBluetoothDevice getLastDevice();
 
     public interface Callback {
         void onBluetoothStateChange(boolean enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 5b24f9c..3b15c2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -121,6 +121,11 @@
     }
 
     @Override
+    public CachedBluetoothDevice getLastDevice() {
+        return mLastDevice;
+    }
+
+    @Override
     public int getMaxConnectionState(CachedBluetoothDevice device) {
         return getCachedState(device).mMaxConnectionState;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
index 1ea23bb..4d95969 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
@@ -22,9 +22,11 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.widget.Checkable;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -65,6 +67,11 @@
         mDone = mButtons.findViewById(android.R.id.button1);
         mDone.setOnClickListener(this);
         ((TextView) mDone).setText(R.string.quick_settings_done);
+        // Hide the resizing space because it causes issues in the volume panel.
+        ViewGroup detail_header = findViewById(R.id.tuner_zen_switch);
+        detail_header.getChildAt(0).setVisibility(View.GONE);
+        // No background so it can blend with volume panel.
+        findViewById(R.id.edit_container).setBackground(null);
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
index b8e9fcd..94dbc2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
@@ -67,7 +67,7 @@
     public void setup() throws Exception {
         mDependency.injectTestDependency(Dependency.BG_LOOPER,
                 TestableLooper.get(this).getLooper());
-        mRealExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
+        mRealExceptionHandler = Thread.getUncaughtExceptionPreHandler();
         mMockExceptionHandler = mock(UncaughtExceptionHandler.class);
         mMockFactory = mock(PluginInstanceManagerFactory.class);
         mMockPluginInstance = mock(PluginInstanceManager.class);
@@ -167,9 +167,9 @@
     }
 
     private void resetExceptionHandler() {
-        mPluginExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
+        mPluginExceptionHandler = Thread.getUncaughtExceptionPreHandler();
         // Set back the real exception handler so the test can crash if it wants to.
-        Thread.setDefaultUncaughtExceptionHandler(mRealExceptionHandler);
+        Thread.setUncaughtExceptionPreHandler(mRealExceptionHandler);
     }
 
     @ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
index 9ec096a..44c4983 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -93,4 +93,9 @@
     public int getBondState(CachedBluetoothDevice device) {
         return 0;
     }
+
+    @Override
+    public CachedBluetoothDevice getLastDevice() {
+        return null;
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 20ccee2..2b8b25e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -522,10 +522,11 @@
     /**
      * Updates the last fill selection when an authentication was selected.
      */
-    void setAuthenticationSelected(int sessionId) {
+    void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState) {
         synchronized (mLock) {
             if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
-                mEventHistory.addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null));
+                mEventHistory
+                        .addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState));
             }
         }
     }
@@ -533,11 +534,13 @@
     /**
      * Updates the last fill selection when an dataset authentication was selected.
      */
-    void setDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId) {
+    void setDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
+            @Nullable Bundle clientState) {
         synchronized (mLock) {
             if (isValidEventLocked("setDatasetAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
-                        new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset));
+                        new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
+                                clientState));
             }
         }
     }
@@ -545,10 +548,10 @@
     /**
      * Updates the last fill selection when an save Ui is shown.
      */
-    void setSaveShown(int sessionId) {
+    void setSaveShown(int sessionId, @Nullable Bundle clientState) {
         synchronized (mLock) {
             if (isValidEventLocked("setSaveShown()", sessionId)) {
-                mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null));
+                mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState));
             }
         }
     }
@@ -556,10 +559,12 @@
     /**
      * Updates the last fill response when a dataset was selected.
      */
-    void setDatasetSelected(@Nullable String selectedDataset, int sessionId) {
+    void setDatasetSelected(@Nullable String selectedDataset, int sessionId,
+            @Nullable Bundle clientState) {
         synchronized (mLock) {
             if (isValidEventLocked("setDatasetSelected()", sessionId)) {
-                mEventHistory.addEvent(new Event(Event.TYPE_DATASET_SELECTED, selectedDataset));
+                mEventHistory.addEvent(
+                        new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState));
             }
         }
     }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 95db603..8d9f0aa2 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -597,7 +597,7 @@
                     getFillContextByRequestIdLocked(requestId).getStructure(), extras);
         }
 
-        mService.setAuthenticationSelected(id);
+        mService.setAuthenticationSelected(id, mClientState);
 
         final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
         mHandlerCaller.getHandler().post(() -> startAuthentication(authenticationId,
@@ -970,7 +970,7 @@
                 }
 
                 if (sDebug) Slog.d(TAG, "Good news, everyone! All checks passed, show save UI!");
-                mService.setSaveShown(id);
+                mService.setSaveShown(id, mClientState);
                 final IAutoFillManagerClient client = getClient();
                 mPendingSaveUi = new PendingUi(mActivityToken);
                 getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo,
@@ -1532,14 +1532,14 @@
             }
             // Autofill it directly...
             if (dataset.getAuthentication() == null) {
-                mService.setDatasetSelected(dataset.getId(), id);
+                mService.setDatasetSelected(dataset.getId(), id, mClientState);
 
                 autoFillApp(dataset);
                 return;
             }
 
             // ...or handle authentication.
-            mService.setDatasetAuthenticationSelected(dataset.getId(), id);
+            mService.setDatasetAuthenticationSelected(dataset.getId(), id, mClientState);
             setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
             final Intent fillInIntent = createAuthFillInIntent(
                     getFillContextByRequestIdLocked(requestId).getStructure(), mClientState);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 29d562b..c1a7f68 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -10475,7 +10475,7 @@
     public ComponentName[] listAllTransportComponents() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "listAllTransportComponents");
-        return mTransportManager.getAllTransportCompenents();
+        return mTransportManager.getAllTransportComponents();
     }
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index b01cfc5..83b6693 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -2762,7 +2762,7 @@
     public ComponentName[] listAllTransportComponents() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "listAllTransportComponents");
-        return mTransportManager.getAllTransportCompenents();
+        return mTransportManager.getAllTransportComponents();
     }
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index fb2982e..321ef26 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -41,10 +41,12 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.EventLogTags;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -57,7 +59,9 @@
 
     private static final String TAG = "BackupTransportManager";
 
-    private static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
+    @VisibleForTesting
+    /* package */ static final String SERVICE_ACTION_TRANSPORT_HOST =
+            "android.backup.TRANSPORT_HOST";
 
     private static final long REBINDING_TIMEOUT_UNPROVISIONED_MS = 30 * 1000; // 30 sec
     private static final long REBINDING_TIMEOUT_PROVISIONED_MS = 5 * 60 * 1000; // 5 mins
@@ -95,7 +99,11 @@
             TransportBoundListener listener, Looper looper) {
         mContext = context;
         mPackageManager = context.getPackageManager();
-        mTransportWhitelist = (whitelist != null) ? whitelist : new ArraySet<>();
+        if (whitelist != null) {
+            mTransportWhitelist = whitelist;
+        } else {
+            mTransportWhitelist = new ArraySet<>();
+        }
         mCurrentTransportName = defaultTransport;
         mTransportBoundListener = listener;
         mHandler = new RebindOnTimeoutHandler(looper);
@@ -186,7 +194,7 @@
         }
     }
 
-    ComponentName[] getAllTransportCompenents() {
+    ComponentName[] getAllTransportComponents() {
         synchronized (mTransportLock) {
             return mValidTransports.keySet().toArray(new ComponentName[mValidTransports.size()]);
         }
@@ -208,7 +216,8 @@
         }
     }
 
-    void ensureTransportReady(ComponentName transportComponent, SelectBackupTransportCallback listener) {
+    void ensureTransportReady(ComponentName transportComponent,
+            SelectBackupTransportCallback listener) {
         synchronized (mTransportLock) {
             TransportConnection conn = mValidTransports.get(transportComponent);
             if (conn == null) {
@@ -252,7 +261,7 @@
                 intent, 0, UserHandle.USER_SYSTEM);
         if (hosts != null) {
             for (ResolveInfo host : hosts) {
-                final ComponentName infoComponentName = host.serviceInfo.getComponentName();
+                final ComponentName infoComponentName = getComponentName(host.serviceInfo);
                 boolean shouldBind = false;
                 if (components != null && packageName != null) {
                     for (String component : components) {
@@ -310,7 +319,7 @@
         Intent intent = new Intent(mTransportServiceIntent)
                 .setComponent(componentName);
         return mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
-                UserHandle.SYSTEM);
+                createSystemUserHandle());
     }
 
     private class TransportConnection implements ServiceConnection {
@@ -333,7 +342,7 @@
                 boolean success = false;
 
                 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
-                    component.flattenToShortString(), 1);
+                        component.flattenToShortString(), 1);
 
                 try {
                     mTransportName = mBinder.name();
@@ -437,7 +446,8 @@
         }
 
         private long getRebindTimeout() {
-            final boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
+            final boolean isDeviceProvisioned = Settings.Global.getInt(
+                    mContext.getContentResolver(),
                     Settings.Global.DEVICE_PROVISIONED, 0) != 0;
             return isDeviceProvisioned
                     ? REBINDING_TIMEOUT_PROVISIONED_MS
@@ -465,7 +475,7 @@
                 synchronized (mTransportLock) {
                     if (mBoundTransports.containsValue(transportComponent)) {
                         Slog.d(TAG, "Explicit rebinding timeout passed, but already bound to "
-                            + componentShortString + " so not attempting to rebind");
+                                + componentShortString + " so not attempting to rebind");
                         return;
                     }
                     Slog.d(TAG, "Explicit rebinding timeout passed, attempting rebinding to: "
@@ -492,4 +502,18 @@
             Slog.v(TAG, message);
         }
     }
+
+    // These only exists to make it testable with Robolectric, which is not updated to API level 24
+    // yet.
+    // TODO: Get rid of this once Robolectric is updated.
+    private static ComponentName getComponentName(ServiceInfo serviceInfo) {
+        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+    }
+
+    // These only exists to make it testable with Robolectric, which is not updated to API level 24
+    // yet.
+    // TODO: Get rid of this once Robolectric is updated.
+    private static UserHandle createSystemUserHandle() {
+        return new UserHandle(UserHandle.USER_SYSTEM);
+    }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 17292b4..adf536b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2011,7 +2011,16 @@
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
-                    handleUpdateLinkProperties(nai, (LinkProperties) msg.obj);
+                    if (VDBG) {
+                        log("Update of LinkProperties for " + nai.name() +
+                                "; created=" + nai.created +
+                                "; everConnected=" + nai.everConnected);
+                    }
+                    LinkProperties oldLp = nai.linkProperties;
+                    synchronized (nai) {
+                        nai.linkProperties = (LinkProperties)msg.obj;
+                    }
+                    if (nai.everConnected) updateLinkProperties(nai, oldLp);
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
@@ -2260,7 +2269,7 @@
             }
             nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
             mNetworkAgentInfos.remove(msg.replyTo);
-            nai.maybeStopClat();
+            maybeStopClat(nai);
             synchronized (mNetworkForNetId) {
                 // Remove the NetworkAgent, but don't mark the netId as
                 // available until we've told netd to delete it below.
@@ -4374,7 +4383,7 @@
         updateDnses(newLp, oldLp, netId);
 
         // Start or stop clat accordingly to network state.
-        networkAgent.updateClat(mNetd);
+        updateClat(networkAgent);
         if (isDefaultNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
         } else {
@@ -4389,6 +4398,32 @@
         mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
     }
 
+    private void updateClat(NetworkAgentInfo nai) {
+        if (Nat464Xlat.requiresClat(nai)) {
+            maybeStartClat(nai);
+        } else {
+            maybeStopClat(nai);
+        }
+    }
+
+    /** Ensure clat has started for this network. */
+    private void maybeStartClat(NetworkAgentInfo nai) {
+        if (nai.clatd != null && nai.clatd.isStarted()) {
+            return;
+        }
+        nai.clatd = new Nat464Xlat(mNetd, mTrackerHandler, nai);
+        nai.clatd.start();
+    }
+
+    /** Ensure clat has stopped for this network. */
+    private void maybeStopClat(NetworkAgentInfo nai) {
+        if (nai.clatd == null) {
+            return;
+        }
+        nai.clatd.stop();
+        nai.clatd = null;
+    }
+
     private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
         // Marks are only available on WiFi interaces. Checking for
         // marks on unsupported interfaces is harmless.
@@ -4623,21 +4658,6 @@
         }
     }
 
-    public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
-        if (VDBG) {
-            log("Update of LinkProperties for " + nai.name() +
-                    "; created=" + nai.created +
-                    "; everConnected=" + nai.everConnected);
-        }
-        LinkProperties oldLp = nai.linkProperties;
-        synchronized (nai) {
-            nai.linkProperties = newLp;
-        }
-        if (nai.everConnected) {
-            updateLinkProperties(nai, oldLp);
-        }
-    }
-
     private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
         for (int i = 0; i < nai.numNetworkRequests(); i++) {
             NetworkRequest nr = nai.requestAt(i);
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 4733840..046eb76 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -728,6 +728,9 @@
                     return timeout;
                 }
             }
+            if (!prebaked.shouldFallback()) {
+                return 0;
+            }
             final int id = prebaked.getId();
             if (id < 0 || id >= mFallbackEffects.length || mFallbackEffects[id] == null) {
                 Slog.w(TAG, "Failed to play prebaked effect, no fallback");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bbd3369..f3bc62f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13277,7 +13277,7 @@
             synchronized (this) {
                 final ActivityRecord r = ActivityRecord.isInStackLocked(token);
                 if (r != null) {
-                    final ActivityOptions activityOptions = r.pendingOptions;
+                    final ActivityOptions activityOptions = r.takeOptionsLocked();
                     return activityOptions == null ? null : activityOptions.toBundle();
                 }
                 return null;
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 4433b84..2c79d2e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -652,14 +652,14 @@
         }
     }
 
-    void updatePictureInPictureMode(Rect targetStackBounds) {
+    void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
         if (task == null || task.getStack() == null || app == null || app.thread == null) {
             return;
         }
 
         final boolean inPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID) &&
                 (targetStackBounds != null);
-        if (inPictureInPictureMode != mLastReportedPictureInPictureMode) {
+        if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
             // Picture-in-picture mode changes also trigger a multi-window mode change as well, so
             // update that here in order
             mLastReportedPictureInPictureMode = inPictureInPictureMode;
@@ -674,8 +674,7 @@
     private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
         try {
             app.thread.schedulePictureInPictureModeChanged(appToken,
-                    mLastReportedPictureInPictureMode,
-                    overrideConfig);
+                    mLastReportedPictureInPictureMode, overrideConfig);
         } catch (Exception e) {
             // If process died, no one cares.
         }
@@ -1521,7 +1520,12 @@
     }
 
     void notifyUnknownVisibilityLaunched() {
-        mWindowContainerController.notifyUnknownVisibilityLaunched();
+
+        // No display activities never add a window, so there is no point in waiting them for
+        // relayout.
+        if (!noDisplay) {
+            mWindowContainerController.notifyUnknownVisibilityLaunched();
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 6a9a4fe..2da4377 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -5062,24 +5062,29 @@
     }
 
     boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
-            boolean dumpClient, String dumpPackage, boolean needSep, String header) {
-        boolean printed = false;
+            boolean dumpClient, String dumpPackage, boolean needSep) {
+
+        if (mTaskHistory.isEmpty()) {
+            return false;
+        }
+        final String prefix = "    ";
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
-            printed |= ActivityStackSupervisor.dumpHistoryList(fd, pw,
-                    mTaskHistory.get(taskNdx).mActivities, "    ", "Hist", true, !dumpAll,
-                    dumpClient, dumpPackage, needSep, header,
-                    "    Task id #" + task.taskId + "\n" +
-                    "    mFullscreen=" + task.mFullscreen + "\n" +
-                    "    mBounds=" + task.mBounds + "\n" +
-                    "    mMinWidth=" + task.mMinWidth + "\n" +
-                    "    mMinHeight=" + task.mMinHeight + "\n" +
-                    "    mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
-            if (printed) {
-                header = null;
+            if (needSep) {
+                pw.println("");
             }
+            pw.println(prefix + "Task id #" + task.taskId);
+            pw.println(prefix + "mFullscreen=" + task.mFullscreen);
+            pw.println(prefix + "mBounds=" + task.mBounds);
+            pw.println(prefix + "mMinWidth=" + task.mMinWidth);
+            pw.println(prefix + "mMinHeight=" + task.mMinHeight);
+            pw.println(prefix + "mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
+            pw.println(prefix + "* " + task);
+            task.dump(pw, prefix + "  ");
+            ActivityStackSupervisor.dumpHistoryList(fd, pw, mTaskHistory.get(taskNdx).mActivities,
+                    prefix, "Hist", true, !dumpAll, dumpClient, dumpPackage, false, null, task);
         }
-        return printed;
+        return true;
     }
 
     ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1107390..17b7f98 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1268,6 +1268,10 @@
 
             r.app = app;
 
+            if (mKeyguardController.isKeyguardLocked()) {
+                r.notifyUnknownVisibilityLaunched();
+            }
+
             // Have the window manager re-evaluate the orientation of the screen based on the new
             // activity order.  Note that as a result of this, it can call back into the activity
             // manager with a new orientation.  We don't care about that, because the activity is
@@ -1294,9 +1298,6 @@
                 r.setVisibility(true);
             }
 
-            if (mKeyguardController.isKeyguardLocked()) {
-                r.notifyUnknownVisibilityLaunched();
-            }
             final int applicationInfoUid =
                     (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
             if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
@@ -3644,25 +3645,14 @@
             ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
-                StringBuilder stackHeader = new StringBuilder(128);
-                stackHeader.append("  Stack #");
-                stackHeader.append(stack.mStackId);
-                stackHeader.append(":");
-                stackHeader.append("\n");
-                stackHeader.append("  mFullscreen=" + stack.mFullscreen);
-                stackHeader.append("\n");
-                stackHeader.append("  isSleeping=" + stack.shouldSleepActivities());
-                stackHeader.append("\n");
-                stackHeader.append("  mBounds=" + stack.mBounds);
+                pw.println();
+                pw.println("  Stack #" + stack.mStackId + ":");
+                pw.println("  mFullscreen=" + stack.mFullscreen);
+                pw.println("  isSleeping=" + stack.shouldSleepActivities());
+                pw.println("  mBounds=" + stack.mBounds);
 
-                final boolean printedStackHeader = stack.dumpActivitiesLocked(fd, pw, dumpAll,
-                        dumpClient, dumpPackage, needSep, stackHeader.toString());
-                printed |= printedStackHeader;
-                if (!printedStackHeader) {
-                    // Ensure we always dump the stack header even if there are no activities
-                    pw.println();
-                    pw.println(stackHeader);
-                }
+                printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
+                        needSep);
 
                 printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false,
                         !dumpAll, false, dumpPackage, true,
@@ -3710,8 +3700,7 @@
 
     static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
             String prefix, String label, boolean complete, boolean brief, boolean client,
-            String dumpPackage, boolean needNL, String header1, String header2) {
-        TaskRecord lastTask = null;
+            String dumpPackage, boolean needNL, String header, TaskRecord lastTask) {
         String innerPrefix = null;
         String[] args = null;
         boolean printed = false;
@@ -3730,13 +3719,9 @@
                 pw.println("");
                 needNL = false;
             }
-            if (header1 != null) {
-                pw.println(header1);
-                header1 = null;
-            }
-            if (header2 != null) {
-                pw.println(header2);
-                header2 = null;
+            if (header != null) {
+                pw.println(header);
+                header = null;
             }
             if (lastTask != r.getTask()) {
                 lastTask = r.getTask();
@@ -4151,31 +4136,29 @@
             return;
         }
 
-        scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds, false /* immediate */);
+        scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds);
     }
 
-    void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds,
-            boolean immediate) {
-
-        if (immediate) {
-            mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
-            for (int i = task.mActivities.size() - 1; i >= 0; i--) {
-                final ActivityRecord r = task.mActivities.get(i);
-                if (r.app != null && r.app.thread != null) {
-                    r.updatePictureInPictureMode(targetStackBounds);
-                }
+    void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
+        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = task.mActivities.get(i);
+            if (r.app != null && r.app.thread != null) {
+                mPipModeChangedActivities.add(r);
             }
-        } else {
-            for (int i = task.mActivities.size() - 1; i >= 0; i--) {
-                final ActivityRecord r = task.mActivities.get(i);
-                if (r.app != null && r.app.thread != null) {
-                    mPipModeChangedActivities.add(r);
-                }
-            }
-            mPipModeChangedTargetStackBounds = targetStackBounds;
+        }
+        mPipModeChangedTargetStackBounds = targetStackBounds;
 
-            if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
-                mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
+        if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
+            mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
+        }
+    }
+
+    void updatePictureInPictureMode(TaskRecord task, Rect targetStackBounds, boolean forceUpdate) {
+        mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
+        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = task.mActivities.get(i);
+            if (r.app != null && r.app.thread != null) {
+                r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
             }
         }
     }
@@ -4237,7 +4220,8 @@
                     synchronized (mService) {
                         for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
                             final ActivityRecord r = mPipModeChangedActivities.remove(i);
-                            r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds);
+                            r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
+                                    false /* forceUpdate */);
                         }
                     }
                 } break;
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index a1b95f9..86ee3f4 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -92,15 +92,16 @@
         return mWindowContainerController.deferScheduleMultiWindowModeChanged();
     }
 
-    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
+    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+            boolean forceUpdate) {
         // It is guaranteed that the activities requiring the update will be in the pinned stack at
         // this point (either reparented before the animation into PiP, or before reparenting after
         // the animation out of PiP)
         synchronized(this) {
             ArrayList<TaskRecord> tasks = getAllTasks();
             for (int i = 0; i < tasks.size(); i++ ) {
-                mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(tasks.get(i),
-                        targetStackBounds, true /* immediate */);
+                mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
+                        forceUpdate);
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 10c8b8b..f8d23d4 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -20,21 +20,22 @@
 import android.net.ConnectivityManager;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.NetworkAgent;
 import android.net.RouteInfo;
+import android.os.Handler;
+import android.os.Message;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.util.Slog;
 
-import com.android.internal.util.ArrayUtils;
 import com.android.server.net.BaseNetworkObserver;
+import com.android.internal.util.ArrayUtils;
 
 import java.net.Inet4Address;
 import java.util.Objects;
 
 /**
- * Class to manage a 464xlat CLAT daemon. Nat464Xlat is not thread safe and should be manipulated
- * from a consistent and unique thread context. It is the responsability of ConnectivityService to
- * call into this class from its own Handler thread.
+ * Class to manage a 464xlat CLAT daemon.
  *
  * @hide
  */
@@ -54,6 +55,9 @@
 
     private final INetworkManagementService mNMService;
 
+    // ConnectivityService Handler for LinkProperties updates.
+    private final Handler mHandler;
+
     // The network we're running on, and its type.
     private final NetworkAgentInfo mNetwork;
 
@@ -63,12 +67,16 @@
         RUNNING;    // start() called, and the stacked iface is known to be up.
     }
 
+    // Once mIface is non-null and isStarted() is true, methods called by ConnectivityService on
+    // its handler thread must not modify any internal state variables; they are only updated by the
+    // interface observers, called on the notification threads.
     private String mBaseIface;
     private String mIface;
-    private State mState = State.IDLE;
+    private volatile State mState = State.IDLE;
 
-    public Nat464Xlat(INetworkManagementService nmService, NetworkAgentInfo nai) {
+    public Nat464Xlat(INetworkManagementService nmService, Handler handler, NetworkAgentInfo nai) {
         mNMService = nmService;
+        mHandler = handler;
         mNetwork = nai;
     }
 
@@ -97,13 +105,6 @@
     }
 
     /**
-     * @return true if clatd has been started but the stacked interface is not yet up.
-     */
-    public boolean isStarting() {
-        return mState == State.STARTING;
-    }
-
-    /**
      * @return true if clatd has been started and the stacked interface is up.
      */
     public boolean isRunning() {
@@ -120,7 +121,7 @@
     }
 
     /**
-     * Clears internal state.
+     * Clears internal state. Must not be called by ConnectivityService.
      */
     private void enterIdleState() {
         mIface = null;
@@ -129,7 +130,7 @@
     }
 
     /**
-     * Starts the clat daemon.
+     * Starts the clat daemon. Called by ConnectivityService on the handler thread.
      */
     public void start() {
         if (isStarted()) {
@@ -166,7 +167,7 @@
     }
 
     /**
-     * Stops the clat daemon.
+     * Stops the clat daemon. Called by ConnectivityService on the handler thread.
      */
     public void stop() {
         if (!isStarted()) {
@@ -180,8 +181,15 @@
         } catch(RemoteException|IllegalStateException e) {
             Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
         }
-        // When clatd stops and its interface is deleted, handleInterfaceRemoved() will trigger
-        // ConnectivityService#handleUpdateLinkProperties and call enterIdleState().
+        // When clatd stops and its interface is deleted, interfaceRemoved() will notify
+        // ConnectivityService and call enterIdleState().
+    }
+
+    private void updateConnectivityService(LinkProperties lp) {
+        Message msg = mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, lp);
+        msg.replyTo = mNetwork.messenger;
+        Slog.i(TAG, "sending message to ConnectivityService: " + msg);
+        msg.sendToTarget();
     }
 
     /**
@@ -249,15 +257,19 @@
     }
 
     /**
-     * Adds stacked link on base link and transitions to RUNNING state.
+     * Adds stacked link on base link and transitions to Running state
+     * This is called by the InterfaceObserver on its own thread, so can race with stop().
      */
-    private void handleInterfaceLinkStateChanged(String iface, boolean up) {
-        if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
+    @Override
+    public void interfaceLinkStateChanged(String iface, boolean up) {
+        if (!isStarted() || !up || !Objects.equals(mIface, iface)) {
+            return;
+        }
+        if (isRunning()) {
             return;
         }
         LinkAddress clatAddress = getLinkAddress(iface);
         if (clatAddress == null) {
-            Slog.e(TAG, "cladAddress was null for stacked iface " + iface);
             return;
         }
         mState = State.RUNNING;
@@ -267,14 +279,15 @@
         maybeSetIpv6NdOffload(mBaseIface, false);
         LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
         lp.addStackedLink(makeLinkProperties(clatAddress));
-        mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
+        updateConnectivityService(lp);
     }
 
-    /**
-     * Removes stacked link on base link and transitions to IDLE state.
-     */
-    private void handleInterfaceRemoved(String iface) {
-        if (!isRunning() || !Objects.equals(mIface, iface)) {
+    @Override
+    public void interfaceRemoved(String iface) {
+        if (!isStarted() || !Objects.equals(mIface, iface)) {
+            return;
+        }
+        if (!isRunning()) {
             return;
         }
 
@@ -282,28 +295,21 @@
         // The interface going away likely means clatd has crashed. Ask netd to stop it,
         // because otherwise when we try to start it again on the same base interface netd
         // will complain that it's already started.
+        //
+        // Note that this method can be called by the interface observer at the same time
+        // that ConnectivityService calls stop(). In this case, the second call to
+        // stopClatd() will just throw IllegalStateException, which we'll ignore.
         try {
             mNMService.unregisterObserver(this);
-            // TODO: add STOPPING state to avoid calling stopClatd twice.
             mNMService.stopClatd(mBaseIface);
-        } catch(RemoteException|IllegalStateException e) {
-            Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
+        } catch (RemoteException|IllegalStateException e) {
+            // Well, we tried.
         }
         maybeSetIpv6NdOffload(mBaseIface, true);
         LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
         lp.removeStackedLink(mIface);
         enterIdleState();
-        mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
-    }
-
-    @Override
-    public void interfaceLinkStateChanged(String iface, boolean up) {
-        mNetwork.handler.post(() -> { handleInterfaceLinkStateChanged(iface, up); });
-    }
-
-    @Override
-    public void interfaceRemoved(String iface) {
-        mNetwork.handler.post(() -> { handleInterfaceRemoved(iface); });
+        updateConnectivityService(lp);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 7c4ef0f..872923a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -27,9 +27,7 @@
 import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.os.Handler;
-import android.os.INetworkManagementService;
 import android.os.Messenger;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.SparseArray;
@@ -249,9 +247,9 @@
 
     private static final String TAG = ConnectivityService.class.getSimpleName();
     private static final boolean VDBG = false;
-    public final ConnectivityService connService;
+    private final ConnectivityService mConnService;
     private final Context mContext;
-    final Handler handler;
+    private final Handler mHandler;
 
     public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
@@ -263,10 +261,10 @@
         linkProperties = lp;
         networkCapabilities = nc;
         currentScore = score;
-        this.connService = connService;
+        mConnService = connService;
         mContext = context;
-        this.handler = handler;
-        networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
+        mHandler = handler;
+        networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
         networkMisc = misc;
     }
 
@@ -432,7 +430,7 @@
     private boolean ignoreWifiUnvalidationPenalty() {
         boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
                 networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
-        boolean avoidBadWifi = connService.avoidBadWifi() || avoidUnvalidated;
+        boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated;
         return isWifi && !avoidBadWifi && everValidated;
     }
 
@@ -516,8 +514,8 @@
         }
 
         if (newExpiry > 0) {
-            mLingerMessage = connService.makeWakeupMessage(
-                    mContext, handler,
+            mLingerMessage = mConnService.makeWakeupMessage(
+                    mContext, mHandler,
                     "NETWORK_LINGER_COMPLETE." + network.netId,
                     EVENT_NETWORK_LINGER_COMPLETE, this);
             mLingerMessage.schedule(newExpiry);
@@ -553,32 +551,6 @@
         for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
     }
 
-    public void updateClat(INetworkManagementService netd) {
-        if (Nat464Xlat.requiresClat(this)) {
-            maybeStartClat(netd);
-        } else {
-            maybeStopClat();
-        }
-    }
-
-    /** Ensure clat has started for this network. */
-    public void maybeStartClat(INetworkManagementService netd) {
-        if (clatd != null && clatd.isStarted()) {
-            return;
-        }
-        clatd = new Nat464Xlat(netd, this);
-        clatd.start();
-    }
-
-    /** Ensure clat has stopped for this network. */
-    public void maybeStopClat() {
-        if (clatd == null) {
-            return;
-        }
-        clatd.stop();
-        clatd = null;
-    }
-
     public String toString() {
         return "NetworkAgentInfo{ ni{" + networkInfo + "}  " +
                 "network{" + network + "}  nethandle{" + network.getNetworkHandle() + "}  " +
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 79bed73..c6998d6 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -520,11 +520,12 @@
             if (DEBUG) {
                 Slog.d(TAG, "Receieved: " + action);
             }
+            final String pkgName = getPackageName(intent);
+            final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+
             if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
                 // Purge the app's jobs if the whole package was just disabled.  When this is
                 // the case the component name will be a bare package name.
-                final String pkgName = getPackageName(intent);
-                final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                 if (pkgName != null && pkgUid != -1) {
                     final String[] changedComponents = intent.getStringArrayExtra(
                             Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
@@ -544,7 +545,8 @@
                                             Slog.d(TAG, "Removing jobs for package " + pkgName
                                                     + " in user " + userId);
                                         }
-                                        cancelJobsForUid(pkgUid, "app package state changed");
+                                        cancelJobsForPackageAndUid(pkgName, pkgUid,
+                                                "app disabled");
                                     }
                                 } catch (RemoteException|IllegalArgumentException e) {
                                     /*
@@ -573,7 +575,7 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
                     }
-                    cancelJobsForUid(uidRemoved, "app uninstalled");
+                    cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
                 }
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -584,8 +586,6 @@
             } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
                 // Has this package scheduled any jobs, such that we will take action
                 // if it were to be force-stopped?
-                final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                final String pkgName = intent.getData().getSchemeSpecificPart();
                 if (pkgUid != -1) {
                     List<JobStatus> jobsForUid;
                     synchronized (mLock) {
@@ -604,13 +604,11 @@
                 }
             } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
                 // possible force-stop
-                final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                final String pkgName = intent.getData().getSchemeSpecificPart();
                 if (pkgUid != -1) {
                     if (DEBUG) {
                         Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
                     }
-                    cancelJobsForPackageAndUid(pkgName, pkgUid);
+                    cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
                 }
             }
         }
@@ -790,13 +788,17 @@
         }
     }
 
-    void cancelJobsForPackageAndUid(String pkgName, int uid) {
+    void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
+        if ("android".equals(pkgName)) {
+            Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
+            return;
+        }
         synchronized (mLock) {
             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
             for (int i = jobsForUid.size() - 1; i >= 0; i--) {
                 final JobStatus job = jobsForUid.get(i);
                 if (job.getSourcePackageName().equals(pkgName)) {
-                    cancelJobImplLocked(job, null, "app force stopped");
+                    cancelJobImplLocked(job, null, reason);
                 }
             }
         }
@@ -811,8 +813,7 @@
      */
     public void cancelJobsForUid(int uid, String reason) {
         if (uid == Process.SYSTEM_UID) {
-            // This really shouldn't happen.
-            Slog.wtfStack(TAG, "cancelJobsForUid() called for system uid");
+            Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
             return;
         }
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 5927b2f..a1b8456 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -74,6 +74,7 @@
 import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
+import android.security.keystore.UserNotAuthenticatedException;
 import android.service.gatekeeper.GateKeeperResponse;
 import android.service.gatekeeper.IGateKeeperService;
 import android.text.TextUtils;
@@ -503,12 +504,34 @@
         maybeShowEncryptionNotificationForUser(userId);
     }
 
+    /**
+     * Check if profile got unlocked but the keystore is still locked. This happens on full disk
+     * encryption devices since the profile may not yet be running when we consider unlocking it
+     * during the normal flow. In this case unlock the keystore for the profile.
+     */
+    private void ensureProfileKeystoreUnlocked(int userId) {
+        final KeyStore ks = KeyStore.getInstance();
+        if (ks.state(userId) == KeyStore.State.LOCKED
+                && tiedManagedProfileReadyToUnlock(mUserManager.getUserInfo(userId))) {
+            Slog.i(TAG, "Managed profile got unlocked, will unlock its keystore");
+            try {
+                // If boot took too long and the password in vold got expired, parent keystore will
+                // be still locked, we ignore this case since the user will be prompted to unlock
+                // the device after boot.
+                unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to unlock child profile");
+            }
+        }
+    }
+
     public void onUnlockUser(final int userId) {
         // Perform tasks which require locks in LSS on a handler, as we are callbacks from
         // ActivityManager.unlockUser()
         mHandler.post(new Runnable() {
             @Override
             public void run() {
+                ensureProfileKeystoreUnlocked(userId);
                 // Hide notification first, as tie managed profile lock takes time
                 hideEncryptionNotification(new UserHandle(userId));
 
@@ -1027,7 +1050,8 @@
         return new String(decryptionResult, StandardCharsets.UTF_8);
     }
 
-    private void unlockChildProfile(int profileHandle) throws RemoteException {
+    private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated)
+            throws RemoteException {
         try {
             doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
                     LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
@@ -1038,6 +1062,8 @@
                 | BadPaddingException | CertificateException | IOException e) {
             if (e instanceof FileNotFoundException) {
                 Slog.i(TAG, "Child profile key not found");
+            } else if (ignoreUserNotAuthenticated && e instanceof UserNotAuthenticatedException) {
+                Slog.i(TAG, "Parent keystore seems locked, ignoring");
             } else {
                 Slog.e(TAG, "Failed to decrypt child profile key", e);
             }
@@ -1081,11 +1107,8 @@
                 final List<UserInfo> profiles = mUserManager.getProfiles(userId);
                 for (UserInfo pi : profiles) {
                     // Unlock managed profile with unified lock
-                    if (pi.isManagedProfile()
-                            && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id)
-                            && mStorage.hasChildProfileLock(pi.id)
-                            && mUserManager.isUserRunning(pi.id)) {
-                        unlockChildProfile(pi.id);
+                    if (tiedManagedProfileReadyToUnlock(pi)) {
+                        unlockChildProfile(pi.id, false /* ignoreUserNotAuthenticated */);
                     }
                 }
             }
@@ -1094,6 +1117,13 @@
         }
     }
 
+    private boolean tiedManagedProfileReadyToUnlock(UserInfo userInfo) {
+        return userInfo.isManagedProfile()
+                && !mLockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id)
+                && mStorage.hasChildProfileLock(userInfo.id)
+                && mUserManager.isUserRunning(userInfo.id);
+    }
+
     private Map<Integer, String> getDecryptedPasswordsForAllTiedProfiles(int userId) {
         if (mUserManager.getUserInfo(userId).isManagedProfile()) {
             return null;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 93458c8..b224069 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -33,6 +33,7 @@
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -191,8 +192,6 @@
 
 import libcore.io.IoUtils;
 
-import com.google.android.collect.Lists;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
 
@@ -373,8 +372,6 @@
 
     /** Defined network policies. */
     final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>();
-    /** Currently active network rules for ifaces. */
-    final ArrayMap<NetworkPolicy, String[]> mNetworkRules = new ArrayMap<>();
 
     /** Map from subId to subscription plans. */
     final SparseArray<SubscriptionPlan[]> mSubscriptionPlans = new SparseArray<>();
@@ -927,7 +924,8 @@
 
     /**
      * Receiver that watches for {@link WifiConfiguration} to be loaded so that
-     * we can perform upgrade logic.
+     * we can perform upgrade logic. After initial upgrade logic, it updates
+     * {@link #mMeteredIfaces} based on configuration changes.
      */
     final private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() {
         @Override
@@ -935,10 +933,9 @@
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
                     upgradeWifiMeteredOverrideAL();
+                    updateNetworkRulesNL();
                 }
             }
-            // Only need to perform upgrade logic once
-            mContext.unregisterReceiver(this);
         }
     };
 
@@ -1445,6 +1442,22 @@
     }
 
     /**
+     * Collect all ifaces from a {@link NetworkState} into the given set.
+     */
+    private static void collectIfaces(ArraySet<String> ifaces, NetworkState state) {
+        final String baseIface = state.linkProperties.getInterfaceName();
+        if (baseIface != null) {
+            ifaces.add(baseIface);
+        }
+        for (LinkProperties stackedLink : state.linkProperties.getStackedLinks()) {
+            final String stackedIface = stackedLink.getInterfaceName();
+            if (stackedIface != null) {
+                ifaces.add(stackedIface);
+            }
+        }
+    }
+
+    /**
      * Examine all connected {@link NetworkState}, looking for
      * {@link NetworkPolicy} that need to be enforced. When matches found, set
      * remaining quota based on usage cycle and historical stats.
@@ -1462,60 +1475,33 @@
 
         // First, generate identities of all connected networks so we can
         // quickly compare them against all defined policies below.
-        final ArrayList<Pair<String, NetworkIdentity>> connIdents = new ArrayList<>(states.length);
-        final ArraySet<String> connIfaces = new ArraySet<String>(states.length);
+        final ArrayMap<NetworkState, NetworkIdentity> identified = new ArrayMap<>();
         for (NetworkState state : states) {
             if (state.networkInfo != null && state.networkInfo.isConnected()) {
                 final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
-
-                final String baseIface = state.linkProperties.getInterfaceName();
-                if (baseIface != null) {
-                    connIdents.add(Pair.create(baseIface, ident));
-                }
-
-                // Stacked interfaces are considered to have same identity as
-                // their parent network.
-                final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
-                for (LinkProperties stackedLink : stackedLinks) {
-                    final String stackedIface = stackedLink.getInterfaceName();
-                    if (stackedIface != null) {
-                        connIdents.add(Pair.create(stackedIface, ident));
-                    }
-                }
+                identified.put(state, ident);
             }
         }
 
-        // Apply policies against all connected interfaces found above
-        mNetworkRules.clear();
-        final ArrayList<String> ifaceList = Lists.newArrayList();
-        for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
-            final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
-
-            ifaceList.clear();
-            for (int j = connIdents.size() - 1; j >= 0; j--) {
-                final Pair<String, NetworkIdentity> ident = connIdents.get(j);
-                if (policy.template.matches(ident.second)) {
-                    ifaceList.add(ident.first);
-                }
-            }
-
-            if (ifaceList.size() > 0) {
-                final String[] ifaces = ifaceList.toArray(new String[ifaceList.size()]);
-                mNetworkRules.put(policy, ifaces);
-            }
-        }
-
+        final ArraySet<String> newMeteredIfaces = new ArraySet<>();
         long lowestRule = Long.MAX_VALUE;
-        final ArraySet<String> newMeteredIfaces = new ArraySet<String>(states.length);
 
-        // apply each policy that we found ifaces for; compute remaining data
-        // based on current cycle and historical stats, and push to kernel.
-        for (int i = mNetworkRules.size()-1; i >= 0; i--) {
-            final NetworkPolicy policy = mNetworkRules.keyAt(i);
-            final String[] ifaces = mNetworkRules.valueAt(i);
+        // For every well-defined policy, compute remaining data based on
+        // current cycle and historical stats, and push to kernel.
+        final ArraySet<String> matchingIfaces = new ArraySet<>();
+        for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
+           final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
+
+            // Collect all ifaces that match this policy
+            matchingIfaces.clear();
+            for (int j = identified.size() - 1; j >= 0; j--) {
+                if (policy.template.matches(identified.valueAt(j))) {
+                    collectIfaces(matchingIfaces, identified.keyAt(j));
+                }
+            }
 
             if (LOGD) {
-                Slog.d(TAG, "applying policy " + policy + " to ifaces " + Arrays.toString(ifaces));
+                Slog.d(TAG, "Applying " + policy + " to ifaces " + matchingIfaces);
             }
 
             final boolean hasWarning = policy.warningBytes != LIMIT_DISABLED;
@@ -1545,16 +1531,14 @@
                     quotaBytes = Long.MAX_VALUE;
                 }
 
-                if (ifaces.length > 1) {
+                if (matchingIfaces.size() > 1) {
                     // TODO: switch to shared quota once NMS supports
                     Slog.w(TAG, "shared quota unsupported; generating rule for each iface");
                 }
 
-                for (String iface : ifaces) {
-                    // long quotaBytes split up into two ints to fit in message
-                    mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA,
-                            (int) (quotaBytes >> 32), (int) (quotaBytes & 0xFFFFFFFF), iface)
-                            .sendToTarget();
+                for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
+                    final String iface = matchingIfaces.valueAt(j);
+                    setInterfaceQuotaAsync(iface, quotaBytes);
                     newMeteredIfaces.add(iface);
                 }
             }
@@ -1568,29 +1552,36 @@
             }
         }
 
-        for (int i = connIfaces.size()-1; i >= 0; i--) {
-            String iface = connIfaces.valueAt(i);
-            // long quotaBytes split up into two ints to fit in message
-            mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA,
-                    (int) (Long.MAX_VALUE >> 32), (int) (Long.MAX_VALUE & 0xFFFFFFFF), iface)
-                    .sendToTarget();
-            newMeteredIfaces.add(iface);
+        // One final pass to catch any metered ifaces that don't have explicitly
+        // defined policies; typically Wi-Fi networks.
+        for (NetworkState state : states) {
+            if (state.networkInfo != null && state.networkInfo.isConnected()
+                    && !state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+                matchingIfaces.clear();
+                collectIfaces(matchingIfaces, state);
+                for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
+                    final String iface = matchingIfaces.valueAt(j);
+                    if (!newMeteredIfaces.contains(iface)) {
+                        setInterfaceQuotaAsync(iface, Long.MAX_VALUE);
+                        newMeteredIfaces.add(iface);
+                    }
+                }
+            }
         }
 
-        mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget();
-
-        // remove quota on any trailing interfaces
+        // Remove quota from any interfaces that are no longer metered.
         for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
             final String iface = mMeteredIfaces.valueAt(i);
             if (!newMeteredIfaces.contains(iface)) {
-                mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTA, iface)
-                        .sendToTarget();
+                removeInterfaceQuotaAsync(iface);
             }
         }
         mMeteredIfaces = newMeteredIfaces;
 
         final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
         mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget();
+
+        mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget();
     }
 
     /**
@@ -3973,6 +3964,12 @@
         }
     }
 
+    private void setInterfaceQuotaAsync(String iface, long quotaBytes) {
+        // long quotaBytes split up into two ints to fit in message
+        mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA, (int) (quotaBytes >> 32),
+                (int) (quotaBytes & 0xFFFFFFFF), iface).sendToTarget();
+    }
+
     private void setInterfaceQuota(String iface, long quotaBytes) {
         try {
             mNetworkManager.setInterfaceQuota(iface, quotaBytes);
@@ -3983,6 +3980,10 @@
         }
     }
 
+    private void removeInterfaceQuotaAsync(String iface) {
+        mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTA, iface).sendToTarget();
+    }
+
     private void removeInterfaceQuota(String iface) {
         try {
             mNetworkManager.removeInterfaceQuota(iface);
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 04d91f8..807c343 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -92,26 +92,10 @@
         return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())).isFile();
     }
 
-    boolean isDangerous(@NonNull final PackageInfo overlayPackage, final int userId) {
-        // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
-        return isDangerous(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath()));
-    }
-
     private String getIdmapPath(@NonNull final String baseCodePath) {
         final StringBuilder sb = new StringBuilder("/data/resource-cache/");
         sb.append(baseCodePath.substring(1).replace('/', '@'));
         sb.append("@idmap");
         return sb.toString();
     }
-
-    private boolean isDangerous(@NonNull final String idmapPath) {
-        try (DataInputStream dis = new DataInputStream(new FileInputStream(idmapPath))) {
-            final int magic = dis.readInt();
-            final int version = dis.readInt();
-            final int dangerous = dis.readInt();
-            return dangerous != 0;
-        } catch (IOException e) {
-            return true;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index db6e974..c3957f43 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -169,8 +169,9 @@
         }
 
         final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
-        updateAllOverlaysForTarget(packageName, userId, targetPackage);
-        mListener.onOverlaysChanged(packageName, userId);
+        if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+            mListener.onOverlaysChanged(packageName, userId);
+        }
     }
 
     void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
@@ -210,7 +211,9 @@
             Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAllOverlaysForTarget(packageName, userId, null);
+        if (updateAllOverlaysForTarget(packageName, userId, null)) {
+            mListener.onOverlaysChanged(packageName, userId);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d21100c..68913c3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -7820,13 +7820,13 @@
             case HapticFeedbackConstants.VIRTUAL_KEY:
                 return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
             case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
-                return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+                return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
             case HapticFeedbackConstants.KEYBOARD_PRESS:  // == HapticFeedbackConstants.KEYBOARD_TAP
                 return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
             case HapticFeedbackConstants.KEYBOARD_RELEASE:
-                return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+                return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
             case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
-                return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+                return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
             default:
                 return null;
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index f628d5e..66e0a15 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -365,7 +365,6 @@
                 // Now that the app is going invisible, we can remove it. It will be restarted
                 // if made visible again.
                 wtoken.removeDeadWindows();
-                mService.mUnknownAppVisibilityController.appRemovedOrHidden(wtoken);
             } else {
                 if (!mService.mAppTransition.isTransitionSet()
                         && mService.mAppTransition.isReady()) {
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index cff2fad..7953ee4 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -21,7 +21,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.animation.AnimationHandler;
-import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
@@ -32,13 +31,11 @@
 import android.os.Debug;
 import android.util.ArrayMap;
 import android.util.Slog;
-import android.view.Choreographer;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.WindowManagerInternal;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -142,9 +139,6 @@
         // True if this this animation was canceled and will be replaced the another animation from
         // the same {@link #BoundsAnimationTarget} target.
         private boolean mSkipFinalResize;
-        // True if this animation replaced a previous animation of the same
-        // {@link #BoundsAnimationTarget} target.
-        private final boolean mSkipAnimationStart;
         // True if this animation was canceled by the user, not as a part of a replacing animation
         private boolean mSkipAnimationEnd;
 
@@ -159,6 +153,7 @@
 
         // Whether to schedule PiP mode changes on animation start/end
         private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
+        private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState;
 
         // Depending on whether we are animating from
         // a smaller to a larger size
@@ -171,14 +166,14 @@
 
         BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
                 @SchedulePipModeChangedState int schedulePipModeChangedState,
-                boolean moveFromFullscreen, boolean moveToFullscreen,
-                boolean replacingExistingAnimation) {
+                @SchedulePipModeChangedState int prevShedulePipModeChangedState,
+                boolean moveFromFullscreen, boolean moveToFullscreen) {
             super();
             mTarget = target;
             mFrom.set(from);
             mTo.set(to);
-            mSkipAnimationStart = replacingExistingAnimation;
             mSchedulePipModeChangedState = schedulePipModeChangedState;
+            mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState;
             mMoveFromFullscreen = moveFromFullscreen;
             mMoveToFullscreen = moveToFullscreen;
             addUpdateListener(this);
@@ -200,7 +195,7 @@
         @Override
         public void onAnimationStart(Animator animation) {
             if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
-                    + " mSkipAnimationStart=" + mSkipAnimationStart
+                    + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
                     + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
             mFinishAnimationAfterTransition = false;
             mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
@@ -210,18 +205,26 @@
             // running
             updateBooster();
 
-            // Ensure that we have prepared the target for animation before
-            // we trigger any size changes, so it can swap surfaces
-            // in to appropriate modes, or do as it wishes otherwise.
-            if (!mSkipAnimationStart) {
+            // Ensure that we have prepared the target for animation before we trigger any size
+            // changes, so it can swap surfaces in to appropriate modes, or do as it wishes
+            // otherwise.
+            if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
                 mTarget.onAnimationStart(mSchedulePipModeChangedState ==
-                        SCHEDULE_PIP_MODE_CHANGED_ON_START);
+                        SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */);
 
                 // When starting an animation from fullscreen, pause here and wait for the
                 // windows-drawn signal before we start the rest of the transition down into PiP.
                 if (mMoveFromFullscreen) {
                     pause();
                 }
+            } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
+                    mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
+                // We are replacing a running animation into PiP, but since it hasn't completed, the
+                // client will not currently receive any picture-in-picture mode change callbacks.
+                // However, we still need to report to them that they are leaving PiP, so this will
+                // force an update via a mode changed callback.
+                mTarget.onAnimationStart(true /* schedulePipModeChangedCallback */,
+                        true /* forceUpdate */);
             }
 
             // Immediately update the task bounds if they have to become larger, but preserve
@@ -388,6 +391,8 @@
             boolean moveFromFullscreen, boolean moveToFullscreen) {
         final BoundsAnimator existing = mRunningAnimations.get(target);
         final boolean replacing = existing != null;
+        @SchedulePipModeChangedState int prevSchedulePipModeChangedState =
+                NO_PIP_MODE_CHANGED_CALLBACKS;
 
         if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
                 + " schedulePipModeChangedState=" + schedulePipModeChangedState
@@ -403,6 +408,9 @@
                 return existing;
             }
 
+            // Save the previous state
+            prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState;
+
             // Update the PiP callback states if we are replacing the animation
             if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
                 if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
@@ -428,7 +436,8 @@
             existing.cancel();
         }
         final BoundsAnimator animator = new BoundsAnimator(target, from, to,
-                schedulePipModeChangedState, moveFromFullscreen, moveToFullscreen, replacing);
+                schedulePipModeChangedState, prevSchedulePipModeChangedState,
+                moveFromFullscreen, moveToFullscreen);
         mRunningAnimations.put(target, animator);
         animator.setFloatValues(0f, 1f);
         animator.setDuration((animationDuration != -1 ? animationDuration
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
index 8b1bf7b..647a2d6 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -31,7 +31,7 @@
      * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
      * callbacks
      */
-    void onAnimationStart(boolean schedulePipModeChangedCallback);
+    void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);
 
     /**
      * Sets the size of the target (without any intermediate steps, like scheduling animation)
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 135f400..b5c9b99 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -204,10 +204,12 @@
      */
 
     /** Calls directly into activity manager so window manager lock shouldn't held. */
-    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
+    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+            boolean forceUpdate) {
         if (mListener != null) {
             PinnedStackWindowListener listener = (PinnedStackWindowListener) mListener;
-            listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
+            listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds,
+                    forceUpdate);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
index 12b9c1f..33e8a60 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
@@ -28,5 +28,6 @@
      * Called when the stack container pinned stack animation will change the picture-in-picture
      * mode. This is a direct call into ActivityManager.
      */
-    default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {}
+    default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+            boolean forceUpdate) {}
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 1a6e2c8..c876887 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1525,7 +1525,7 @@
     }
 
     @Override  // AnimatesBounds
-    public void onAnimationStart(boolean schedulePipModeChangedCallback) {
+    public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
         synchronized (mService.mWindowMap) {
             mBoundsAnimatingRequested = false;
@@ -1550,9 +1550,11 @@
             final PinnedStackWindowController controller =
                     (PinnedStackWindowController) getController();
             if (schedulePipModeChangedCallback && controller != null) {
-                // We need to schedule the PiP mode change after the animation down, so use the
-                // final bounds
-                controller.updatePictureInPictureModeForPinnedStackAnimation(null);
+                // We need to schedule the PiP mode change before the animation up. It is possible
+                // in this case for the animation down to not have been completed, so always
+                // force-schedule and update to the client to ensure that it is notified that it
+                // is no longer in picture-in-picture mode
+                controller.updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
             }
         }
     }
@@ -1580,7 +1582,7 @@
                 // We need to schedule the PiP mode change after the animation down, so use the
                 // final bounds
                 controller.updatePictureInPictureModeForPinnedStackAnimation(
-                        mBoundsAnimationTarget);
+                        mBoundsAnimationTarget, false /* forceUpdate */);
             }
 
             if (finalStackSize != null) {
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 0370490..d2f374d 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -153,9 +153,9 @@
     if (status == Status::OK) {
         return lengthMs;
     } else if (status != Status::UNSUPPORTED_OPERATION) {
-        // Don't warn on UNSUPPORTED_OPERATION, that's a normal even and just means the motor
-        // doesn't have a pre-defined waveform to perform for it, so we should just fall back
-        // to the framework waveforms.
+        // Don't warn on UNSUPPORTED_OPERATION, that's a normal event and just means the motor
+        // doesn't have a pre-defined waveform to perform for it, so we should just give the
+        // opportunity to fall back to the framework waveforms.
         ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32
                 ", error=%" PRIu32 ").", static_cast<int64_t>(effect),
                 static_cast<int32_t>(strength), static_cast<uint32_t>(status));
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1620df1..57271fa 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1331,11 +1331,13 @@
                     traceEnd();
                 }
 
-                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
-                    traceBeginAndSlog("StartVoiceRecognitionManager");
-                    mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
-                    traceEnd();
-                }
+                // We need to always start this service, regardless of whether the
+                // FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
+                // of initializing various settings.  It will internally modify its behavior
+                // based on that feature.
+                traceBeginAndSlog("StartVoiceRecognitionManager");
+                mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
+                traceEnd();
 
                 if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
                     traceBeginAndSlog("StartGestureLauncher");
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
new file mode 100644
index 0000000..3e82d3e
--- /dev/null
+++ b/services/robotests/Android.mk
@@ -0,0 +1,76 @@
+# Copyright (C) 2016 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.
+
+
+############################################################
+# FrameworksServicesLib app just for Robolectric test target.  #
+############################################################
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := FrameworksServicesLib
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    frameworks-base-testutils \
+    services.backup \
+    services.core \
+    android-support-test \
+    mockito-target-minus-junit4 \
+    platform-test-annotations \
+    truth-prebuilt
+
+include $(BUILD_PACKAGE)
+
+#############################################
+# FrameworksServices Robolectric test target. #
+#############################################
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Include the testing libraries (JUnit4 + Robolectric libs).
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    platform-system-robolectric \
+    truth-prebuilt
+
+LOCAL_JAVA_LIBRARIES := \
+    junit \
+    platform-robolectric-prebuilt
+
+LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib
+LOCAL_MODULE := FrameworksServicesRoboTests
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+#############################################################
+# FrameworksServices runner target to run the previous target. #
+#############################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunFrameworksServicesRoboTests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    FrameworksServicesRoboTests
+
+LOCAL_TEST_PACKAGE := FrameworksServicesLib
+
+include prebuilts/misc/common/robolectric/run_robotests.mk
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
new file mode 100644
index 0000000..0f7a091
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.RequiresPermission;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.UserHandle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.res.ResourceLoader;
+import org.robolectric.res.builder.DefaultPackageManager;
+import org.robolectric.res.builder.RobolectricPackageManager;
+import org.robolectric.shadows.ShadowContextImpl;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(
+        manifest = Config.NONE,
+        sdk = 23,
+        shadows = {TransportManagerTest.ShadowContextImplWithBindServiceAsUser.class}
+)
+public class TransportManagerTest {
+    private static final String PACKAGE_NAME = "some.package.name";
+    private static final String TRANSPORT1_NAME = "transport1.name";
+    private static final String TRANSPORT2_NAME = "transport2.name";
+    private static final ComponentName TRANSPORT1_COMPONENT_NAME = new ComponentName(PACKAGE_NAME,
+            TRANSPORT1_NAME);
+    private static final ComponentName TRANSPORT2_COMPONENT_NAME = new ComponentName(PACKAGE_NAME,
+            TRANSPORT2_NAME);
+    private static final List<ComponentName> TRANSPORTS_COMPONENT_NAMES = Arrays.asList(
+            TRANSPORT1_COMPONENT_NAME, TRANSPORT2_COMPONENT_NAME);
+
+    private RobolectricPackageManager mPackageManager;
+
+    @Mock private TransportManager.TransportBoundListener mTransportBoundListener;
+
+    @Before
+    public void setUp() {
+        mPackageManager = new DefaultPackageManagerWithQueryIntentServicesAsUser(
+                RuntimeEnvironment.getAppResourceLoader());
+        RuntimeEnvironment.setRobolectricPackageManager(mPackageManager);
+    }
+
+    @Test
+    public void onPackageAdded_bindsToAllTransports() {
+        Intent intent = new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST);
+        intent.setPackage(PACKAGE_NAME);
+
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = PACKAGE_NAME;
+        packageInfo.applicationInfo = new ApplicationInfo();
+        packageInfo.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+
+        mPackageManager.addPackage(packageInfo);
+
+        ResolveInfo transport1 = new ResolveInfo();
+        transport1.serviceInfo = new ServiceInfo();
+        transport1.serviceInfo.packageName = PACKAGE_NAME;
+        transport1.serviceInfo.name = TRANSPORT1_NAME;
+
+        ResolveInfo transport2 = new ResolveInfo();
+        transport2.serviceInfo = new ServiceInfo();
+        transport2.serviceInfo.packageName = PACKAGE_NAME;
+        transport2.serviceInfo.name = TRANSPORT2_NAME;
+
+        mPackageManager.addResolveInfoForIntent(intent, Arrays.asList(transport1, transport2));
+
+        TransportManager transportManager = new TransportManager(
+                RuntimeEnvironment.application.getApplicationContext(),
+                new HashSet<>(TRANSPORTS_COMPONENT_NAMES),
+                null,
+                mTransportBoundListener,
+                ShadowLooper.getMainLooper());
+        transportManager.onPackageAdded(PACKAGE_NAME);
+
+        assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+                TRANSPORTS_COMPONENT_NAMES);
+    }
+
+    private static class DefaultPackageManagerWithQueryIntentServicesAsUser extends
+            DefaultPackageManager {
+
+        /* package */ DefaultPackageManagerWithQueryIntentServicesAsUser(
+                ResourceLoader appResourceLoader) {
+            super(appResourceLoader);
+        }
+
+        @Override
+        public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
+            return super.queryIntentServices(intent, flags);
+        }
+    }
+
+    @Implements(className = ShadowContextImpl.CLASS_NAME)
+    public static class ShadowContextImplWithBindServiceAsUser extends ShadowContextImpl {
+
+        @Implementation
+        public boolean bindServiceAsUser(@RequiresPermission Intent service, ServiceConnection conn,
+                int flags, UserHandle user) {
+            return true;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 9d32496..0081214 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -126,6 +126,7 @@
         boolean mMovedToFullscreen;
         boolean mAnimationStarted;
         boolean mSchedulePipModeChangedOnStart;
+        boolean mForcePipModeChangedCallback;
         boolean mAnimationEnded;
         Rect mAnimationEndFinalStackBounds;
         boolean mSchedulePipModeChangedOnEnd;
@@ -140,6 +141,7 @@
             mAnimationStarted = false;
             mAnimationEnded = false;
             mAnimationEndFinalStackBounds = null;
+            mForcePipModeChangedCallback = false;
             mSchedulePipModeChangedOnStart = false;
             mSchedulePipModeChangedOnEnd = false;
             mStackBounds = from;
@@ -148,10 +150,11 @@
         }
 
         @Override
-        public void onAnimationStart(boolean schedulePipModeChangedCallback) {
+        public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
             mAwaitingAnimationStart = false;
             mAnimationStarted = true;
             mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
+            mForcePipModeChangedCallback = forceUpdate;
         }
 
         @Override
@@ -232,7 +235,7 @@
             return this;
         }
 
-        BoundsAnimationDriver restart(Rect to) {
+        BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) {
             if (mAnimator == null) {
                 throw new IllegalArgumentException("Call start() to start a new animation");
             }
@@ -251,8 +254,15 @@
                 assertSame(oldAnimator, mAnimator);
             }
 
-            // No animation start for replacing animation
-            assertTrue(!mTarget.mAnimationStarted);
+            if (expectStartedAndPipModeChangedCallback) {
+                // Replacing animation with pending pip mode changed callback, ensure we update
+                assertTrue(mTarget.mAnimationStarted);
+                assertTrue(mTarget.mSchedulePipModeChangedOnStart);
+                assertTrue(mTarget.mForcePipModeChangedCallback);
+            } else {
+                // No animation start for replacing animation
+                assertTrue(!mTarget.mAnimationStarted);
+            }
             mTarget.mAnimationStarted = true;
             return this;
         }
@@ -467,7 +477,7 @@
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
-                .restart(BOUNDS_FLOATING)
+                .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */)
                 .end()
                 .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
     }
@@ -478,7 +488,8 @@
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
-                .restart(BOUNDS_SMALLER_FLOATING)
+                .restart(BOUNDS_SMALLER_FLOATING,
+                        false /* expectStartedAndPipModeChangedCallback */)
                 .end()
                 .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
     }
@@ -486,10 +497,12 @@
     @UiThreadTest
     @Test
     public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() throws Exception {
+        // When animating from fullscreen and the animation is interruped, we expect the animation
+        // start callback to be made, with a forced pip mode change callback
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
-                .restart(BOUNDS_FULL)
+                .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */)
                 .end()
                 .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
     }
@@ -512,7 +525,7 @@
         mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
                 .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
-                .restart(BOUNDS_FULL)
+                .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */)
                 .end()
                 .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
     }
@@ -523,7 +536,8 @@
         mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
                 .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
-                .restart(BOUNDS_SMALLER_FLOATING)
+                .restart(BOUNDS_SMALLER_FLOATING,
+                        false /* expectStartedAndPipModeChangedCallback */)
                 .end()
                 .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
     }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 4ffacfd..1569ac3 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -183,7 +183,7 @@
         private final boolean mEnableService;
 
         VoiceInteractionManagerServiceStub() {
-            mEnableService = shouldEnableService(mContext.getResources());
+            mEnableService = shouldEnableService(mContext);
         }
 
         // TODO: VI Make sure the caller is the current user or profile
@@ -348,10 +348,15 @@
             }
         }
 
-        private boolean shouldEnableService(Resources res) {
-            // VoiceInteractionService should not be enabled on low ram devices unless it has the config flag.
-            return !ActivityManager.isLowRamDeviceStatic() ||
-                    getForceVoiceInteractionServicePackage(res) != null;
+        private boolean shouldEnableService(Context context) {
+            // VoiceInteractionService should not be enabled on any low RAM devices
+            // or devices that have not declared the recognition feature, unless the
+            // device's configuration has explicitly set the config flag for a fixed
+            // voice interaction service.
+            return (!ActivityManager.isLowRamDeviceStatic()
+                            && context.getPackageManager().hasSystemFeature(
+                                    PackageManager.FEATURE_VOICE_RECOGNIZERS)) ||
+                    getForceVoiceInteractionServicePackage(context.getResources()) != null;
         }
 
         private String getForceVoiceInteractionServicePackage(Resources res) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 735f09b..8e4f487 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -346,14 +346,29 @@
     public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
 
     /**
-     * Flag indicating whether we should downgrade/terminate VT calls and disable VT when
-     * data enabled changed (e.g. reach data limit or turn off data).
+     * When {@code true}, changes to the mobile data enabled switch will not cause the VT
+     * registration state to change.  That is, turning on or off mobile data will not cause VT to be
+     * enabled or disabled.
+     * When {@code false}, disabling mobile data will cause VT to be de-registered.
+     * <p>
+     * See also {@link #KEY_VILTE_DATA_IS_METERED_BOOL}.
      * @hide
      */
     public static final String KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS =
             "ignore_data_enabled_changed_for_video_calls";
 
     /**
+     * Flag indicating whether data used for a video call over LTE is metered or not.
+     * <p>
+     * When {@code true}, if the device hits the data limit or data is disabled during a ViLTE call,
+     * the call will be downgraded to audio-only (or paused if
+     * {@link #KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} is {@code true}).
+     *
+     * @hide
+     */
+    public static final String KEY_VILTE_DATA_IS_METERED_BOOL = "vilte_data_is_metered_bool";
+
+    /**
      * Flag specifying whether WFC over IMS should be available for carrier: independent of
      * carrier provisioning. If false: hard disabled. If true: then depends on carrier
      * provisioning, availability etc.
@@ -825,8 +840,6 @@
      * If user has explicitly disabled some packages in the list, won't re-enable.
      * Other carrier specific apps which are not in this list may be disabled for current carrier,
      * and only be re-enabled when this config for another carrier includes it.
-     *
-     * @hide
      */
     public static final String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
 
@@ -1569,7 +1582,8 @@
         sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
         sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
-        sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, false);
+        sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
+        sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index bf56ec0..b982d0d 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -43,6 +43,7 @@
     SDK_NOUGAT_MR1 = 25,
     SDK_O = 26,
     SDK_O_MR1 = 27,
+    SDK_P = 10000, // STOPSHIP Replace with the real version.
 };
 
 #endif // H_AAPT_SDK_CONSTANTS
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 5c32ed4..13584c0 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -53,6 +53,7 @@
   SDK_NOUGAT_MR1 = 25,
   SDK_O = 26,
   SDK_O_MR1 = 27,
+  SDK_P = 10000, // STOPSHIP Replace with the real version.
 };
 
 ApiVersion FindAttributeSdkLevel(const ResourceId& id);
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 84b7927..887803e 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -323,11 +323,13 @@
   std::vector<std::string> configs;
   std::vector<std::string> split_args;
   bool verbose = false;
+  bool print_only = false;
   Flags flags =
       Flags()
           .OptionalFlag("-o", "Path to the output APK.", &options.output_path)
           .OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir)
           .OptionalFlag("-x", "Path to XML configuration file.", &config_path)
+          .OptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only)
           .OptionalFlag(
               "--target-densities",
               "Comma separated list of the screen densities that the APK will be optimized for.\n"
@@ -372,12 +374,12 @@
   }
 
   context.SetVerbose(verbose);
+  IDiagnostics* diag = context.GetDiagnostics();
 
   if (target_densities) {
     // Parse the target screen densities.
     for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) {
-      Maybe<uint16_t> target_density =
-          ParseTargetDensityParameter(config_str, context.GetDiagnostics());
+      Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
       if (!target_density) {
         return 1;
       }
@@ -387,7 +389,7 @@
 
   std::unique_ptr<IConfigFilter> filter;
   if (!configs.empty()) {
-    filter = ParseConfigFilterParameters(configs, context.GetDiagnostics());
+    filter = ParseConfigFilterParameters(configs, diag);
     if (filter == nullptr) {
       return 1;
     }
@@ -398,26 +400,45 @@
   for (const std::string& split_arg : split_args) {
     options.split_paths.emplace_back();
     options.split_constraints.emplace_back();
-    if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options.split_paths.back(),
+    if (!ParseSplitParameter(split_arg, diag, &options.split_paths.back(),
                              &options.split_constraints.back())) {
       return 1;
     }
   }
 
   if (config_path) {
-    if (!options.output_dir) {
-      context.GetDiagnostics()->Error(
-          DiagMessage() << "Output directory is required when using a configuration file");
-      return 1;
-    }
     std::string& path = config_path.value();
     Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
     if (for_path) {
-      options.configuration = for_path.value().WithDiagnostics(context.GetDiagnostics()).Parse();
+      options.configuration = for_path.value().WithDiagnostics(diag).Parse();
     } else {
-      context.GetDiagnostics()->Error(DiagMessage() << "Could not parse config file " << path);
+      diag->Error(DiagMessage() << "Could not parse config file " << path);
       return 1;
     }
+
+    if (print_only) {
+      std::vector<std::string> names;
+      const PostProcessingConfiguration& config = options.configuration.value();
+      if (!config.AllArtifactNames(file::GetFilename(apk_path), &names, diag)) {
+        diag->Error(DiagMessage() << "Failed to generate output artifact list");
+        return 1;
+      }
+
+      for (const auto& name : names) {
+        std::cout << name << std::endl;
+      }
+      return 0;
+    }
+
+    // Since we know that we are going to process the APK (not just print targets), make sure we
+    // have somewhere to write them to.
+    if (!options.output_dir) {
+      diag->Error(DiagMessage() << "Output directory is required when using a configuration file");
+      return 1;
+    }
+  } else if (print_only) {
+    diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations");
+    return 1;
   }
 
   if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index 424e9be..1735a50 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -30,6 +30,7 @@
 #include "io/File.h"
 #include "io/FileSystem.h"
 #include "io/StringInputStream.h"
+#include "util/Files.h"
 #include "util/Maybe.h"
 #include "util/Util.h"
 #include "xml/XmlActionExecutor.h"
@@ -149,24 +150,49 @@
   return true;
 }
 
-Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, IDiagnostics* diag,
-                                            const StringPiece& base_name,
-                                            const StringPiece& ext) const {
-  std::string result = format.to_string();
+/**
+ * Returns the common artifact base name from a template string.
+ */
+Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
+  const StringPiece ext = file::GetExtension(apk_name);
+  size_t end_index = apk_name.to_string().rfind(ext.to_string());
+  const std::string base_name =
+      (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : "";
 
-  Maybe<StringPiece> maybe_base_name =
-      base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
-  if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
-    return {};
+  // Base name is optional.
+  if (result.find("${basename}") != std::string::npos) {
+    Maybe<StringPiece> maybe_base_name =
+        base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
+    if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
+      return {};
+    }
   }
 
   // Extension is optional.
   if (result.find("${ext}") != std::string::npos) {
-    if (!ReplacePlaceholder("${ext}", {ext}, &result, diag)) {
+    // Make sure we disregard the '.' in the extension when replacing the placeholder.
+    if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) {
       return {};
     }
+  } else {
+    // If no extension is specified, and the name template does not end in the current extension,
+    // add the existing extension.
+    if (!util::EndsWith(result, ext)) {
+      result.append(ext.to_string());
+    }
   }
 
+  return result;
+}
+
+Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, const StringPiece& apk_name,
+                                            IDiagnostics* diag) const {
+  Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
+  if (!base) {
+    return {};
+  }
+  std::string result = std::move(base.value());
+
   if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
     return {};
   }
@@ -194,29 +220,37 @@
   return result;
 }
 
-Maybe<std::string> Artifact::Name(const StringPiece& base_name, const StringPiece& ext,
-                                  IDiagnostics* diag) const {
+Maybe<std::string> Artifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
   if (!name) {
     return {};
   }
 
-  std::string result = name.value();
+  return ToBaseName(name.value(), apk_name, diag);
+}
 
-  // Base name is optional.
-  if (result.find("${basename}") != std::string::npos) {
-    if (!ReplacePlaceholder("${basename}", {base_name}, &result, diag)) {
-      return {};
+bool PostProcessingConfiguration::AllArtifactNames(const StringPiece& apk_name,
+                                                   std::vector<std::string>* artifact_names,
+                                                   IDiagnostics* diag) const {
+  for (const auto& artifact : artifacts) {
+    Maybe<std::string> name;
+    if (artifact.name) {
+      name = artifact.Name(apk_name, diag);
+    } else {
+      if (!artifact_format) {
+        diag->Error(DiagMessage() << "No global artifact template and an artifact name is missing");
+        return false;
+      }
+      name = artifact.ToArtifactName(artifact_format.value(), apk_name, diag);
     }
+
+    if (!name) {
+      return false;
+    }
+
+    artifact_names->push_back(std::move(name.value()));
   }
 
-  // Extension is optional.
-  if (result.find("${ext}") != std::string::npos) {
-    if (!ReplacePlaceholder("${ext}", {ext}, &result, diag)) {
-      return {};
-    }
-  }
-
-  return result;
+  return true;
 }
 
 }  // namespace configuration
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index 6259ce8..a58685e 100644
--- a/tools/aapt2/configuration/ConfigurationParser.h
+++ b/tools/aapt2/configuration/ConfigurationParser.h
@@ -51,13 +51,11 @@
   Maybe<std::string> gl_texture_group;
 
   /** Convert an artifact name template into a name string based on configuration contents. */
-  Maybe<std::string> ToArtifactName(const android::StringPiece& format, IDiagnostics* diag,
-                                    const android::StringPiece& base_name = "",
-                                    const android::StringPiece& ext = "apk") const;
+  Maybe<std::string> ToArtifactName(const android::StringPiece& format,
+                                    const android::StringPiece& apk_name, IDiagnostics* diag) const;
 
   /** Convert an artifact name template into a name string based on configuration contents. */
-  Maybe<std::string> Name(const android::StringPiece& base_name, const android::StringPiece& ext,
-                          IDiagnostics* diag) const;
+  Maybe<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
 };
 
 /** Enumeration of currently supported ABIs. */
@@ -139,6 +137,10 @@
   Group<AndroidSdk> android_sdk_groups;
   Group<DeviceFeature> device_feature_groups;
   Group<GlTexture> gl_texture_groups;
+
+  /** Helper method that generates a list of artifact names and returns true on success. */
+  bool AllArtifactNames(const android::StringPiece& apk_name,
+                        std::vector<std::string>* artifact_names, IDiagnostics* diag) const;
 };
 
 }  // namespace configuration
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index ece70a9..d3bfd33 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -414,16 +414,36 @@
   Artifact x86;
   x86.abi_group = {"x86"};
 
-  auto x86_result = x86.ToArtifactName("something.${abi}.apk", &diag);
+  auto x86_result = x86.ToArtifactName("something.${abi}.apk", "", &diag);
   ASSERT_TRUE(x86_result);
   EXPECT_EQ(x86_result.value(), "something.x86.apk");
 
   Artifact arm;
   arm.abi_group = {"armeabi-v7a"};
 
-  auto arm_result = arm.ToArtifactName("app.${abi}.apk", &diag);
-  ASSERT_TRUE(arm_result);
-  EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+  {
+    auto arm_result = arm.ToArtifactName("app.${abi}.apk", "", &diag);
+    ASSERT_TRUE(arm_result);
+    EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+  }
+
+  {
+    auto arm_result = arm.ToArtifactName("app.${abi}.apk", "different_name.apk", &diag);
+    ASSERT_TRUE(arm_result);
+    EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+  }
+
+  {
+    auto arm_result = arm.ToArtifactName("${basename}.${abi}.apk", "app.apk", &diag);
+    ASSERT_TRUE(arm_result);
+    EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+  }
+
+  {
+    auto arm_result = arm.ToArtifactName("app.${abi}.${ext}", "app.apk", &diag);
+    ASSERT_TRUE(arm_result);
+    EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+  }
 }
 
 TEST(ArtifactTest, Complex) {
@@ -436,10 +456,40 @@
   artifact.locale_group = {"en-AU"};
   artifact.android_sdk_group = {"26"};
 
-  auto result = artifact.ToArtifactName(
-      "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", &diag);
-  ASSERT_TRUE(result);
-  EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+  {
+    auto result = artifact.ToArtifactName(
+        "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", "", &diag);
+    ASSERT_TRUE(result);
+    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+  }
+
+  {
+    auto result = artifact.ToArtifactName(
+        "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", "app.apk", &diag);
+    ASSERT_TRUE(result);
+    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+  }
+
+  {
+    auto result = artifact.ToArtifactName(
+        "${basename}.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", "app.apk", &diag);
+    ASSERT_TRUE(result);
+    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+  }
+
+  {
+    auto result = artifact.ToArtifactName(
+        "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.${ext}", "app.apk", &diag);
+    ASSERT_TRUE(result);
+    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+  }
+
+  {
+    auto result = artifact.ToArtifactName(
+        "${basename}.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}", "app.apk", &diag);
+    ASSERT_TRUE(result);
+    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+  }
 }
 
 TEST(ArtifactTest, Missing) {
@@ -447,16 +497,20 @@
   Artifact x86;
   x86.abi_group = {"x86"};
 
-  EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", &diag));
-  EXPECT_FALSE(x86.ToArtifactName("something.apk", &diag));
+  EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "", &diag));
+  EXPECT_FALSE(x86.ToArtifactName("something.apk", "", &diag));
+  EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "something.apk", &diag));
+  EXPECT_FALSE(x86.ToArtifactName("something.apk", "something.apk", &diag));
 }
 
 TEST(ArtifactTest, Empty) {
   StdErrDiagnostics diag;
   Artifact artifact;
 
-  EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", &diag));
-  EXPECT_TRUE(artifact.ToArtifactName("something.apk", &diag));
+  EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "", &diag));
+  EXPECT_TRUE(artifact.ToArtifactName("something.apk", "", &diag));
+  EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "something.apk", &diag));
+  EXPECT_TRUE(artifact.ToArtifactName("something.apk", "something.apk", &diag));
 }
 
 TEST(ArtifactTest, Repeated) {
@@ -464,8 +518,9 @@
   Artifact artifact;
   artifact.screen_density_group = {"mdpi"};
 
-  ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", &diag));
-  EXPECT_FALSE(artifact.ToArtifactName("something.${density}.${density}.apk", &diag));
+  ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "", &diag));
+  EXPECT_FALSE(artifact.ToArtifactName("something.${density}.${density}.apk", "", &diag));
+  ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "something.apk", &diag));
 }
 
 TEST(ArtifactTest, Nesting) {
@@ -473,9 +528,9 @@
   Artifact x86;
   x86.abi_group = {"x86"};
 
-  EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", &diag));
+  EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", "", &diag));
 
-  const Maybe<std::string>& name = x86.ToArtifactName("something.${abi${abi}}.apk", &diag);
+  const Maybe<std::string>& name = x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag);
   ASSERT_TRUE(name);
   EXPECT_EQ(name.value(), "something.${abix86}.apk");
 }
@@ -486,12 +541,12 @@
   artifact.device_feature_group = {"${gl}"};
   artifact.gl_texture_group = {"glx1"};
 
-  EXPECT_FALSE(artifact.ToArtifactName("app.${feature}.${gl}.apk", &diag));
+  EXPECT_FALSE(artifact.ToArtifactName("app.${feature}.${gl}.apk", "", &diag));
 
   artifact.device_feature_group = {"df1"};
   artifact.gl_texture_group = {"${feature}"};
   {
-    const auto& result = artifact.ToArtifactName("app.${feature}.${gl}.apk", &diag);
+    const auto& result = artifact.ToArtifactName("app.${feature}.${gl}.apk", "", &diag);
     ASSERT_TRUE(result);
     EXPECT_EQ(result.value(), "app.df1.${feature}.apk");
   }
@@ -501,7 +556,7 @@
   artifact.device_feature_group = {"${gl}"};
   artifact.gl_texture_group = {"glx1"};
   {
-    const auto& result = artifact.ToArtifactName("app.${feature}.apk", &diag);
+    const auto& result = artifact.ToArtifactName("app.${feature}.apk", "", &diag);
     ASSERT_TRUE(result);
     EXPECT_EQ(result.value(), "app.glx1.apk");
   }
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index a0ffefa..6fb1793 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -293,6 +293,7 @@
   manifest_action["instrumentation"]["meta-data"] = meta_data_action;
 
   manifest_action["original-package"];
+  manifest_action["overlay"];
   manifest_action["protected-broadcast"];
   manifest_action["uses-permission"];
   manifest_action["uses-permission-sdk-23"];
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 8316264..e7a4f85 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -44,11 +44,11 @@
                                     const PostProcessingConfiguration& config,
                                     const TableFlattenerOptions& table_flattener_options) {
   // TODO(safarmer): Handle APK version codes for the generated APKs.
-  // TODO(safarmer): Handle explicit outputs/generating an output file list for other tools.
+  IDiagnostics* diag = context_->GetDiagnostics();
 
-  const std::string& apk_path = file::GetFilename(apk_->GetSource().path).to_string();
-  const StringPiece ext = file::GetExtension(apk_path);
-  const std::string base_name = apk_path.substr(0, apk_path.rfind(ext.to_string()));
+  const std::string& apk_name = file::GetFilename(apk_->GetSource().path).to_string();
+  const StringPiece ext = file::GetExtension(apk_name);
+  const std::string base_name = apk_name.substr(0, apk_name.rfind(ext.to_string()));
 
   // For now, just write out the stripped APK since ABI splitting doesn't modify anything else.
   for (const Artifact& artifact : config.artifacts) {
@@ -57,20 +57,17 @@
     AxisConfigFilter axis_filter;
 
     if (!artifact.name && !config.artifact_format) {
-      context_->GetDiagnostics()->Error(
+      diag->Error(
           DiagMessage() << "Artifact does not have a name and no global name template defined");
       return false;
     }
 
     Maybe<std::string> artifact_name =
-        (artifact.name)
-            ? artifact.Name(base_name, ext.substr(1), context_->GetDiagnostics())
-            : artifact.ToArtifactName(config.artifact_format.value(), context_->GetDiagnostics(),
-                                      base_name, ext.substr(1));
+        (artifact.name) ? artifact.Name(apk_name, diag)
+                        : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
 
     if (!artifact_name) {
-      context_->GetDiagnostics()->Error(DiagMessage()
-                                        << "Could not determine split APK artifact name");
+      diag->Error(DiagMessage() << "Could not determine split APK artifact name");
       return false;
     }
 
@@ -80,8 +77,7 @@
       auto group = config.abi_groups.find(group_name);
       // TODO: Remove validation when configuration parser ensures referential integrity.
       if (group == config.abi_groups.end()) {
-        context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced ABI group '"
-                                                        << group_name << "'");
+        diag->Error(DiagMessage() << "could not find referenced ABI group '" << group_name << "'");
         return false;
       }
       filters.AddFilter(AbiFilter::FromAbiList(group->second));
@@ -93,8 +89,7 @@
       auto group = config.screen_density_groups.find(group_name);
       // TODO: Remove validation when configuration parser ensures referential integrity.
       if (group == config.screen_density_groups.end()) {
-        context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
-                                                        << group_name << "'");
+        diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'");
         return false;
       }
 
@@ -109,8 +104,7 @@
       auto group = config.locale_groups.find(group_name);
       // TODO: Remove validation when configuration parser ensures referential integrity.
       if (group == config.locale_groups.end()) {
-        context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
-                                                        << group_name << "'");
+        diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'");
         return false;
       }
 
@@ -132,11 +126,10 @@
     }
     file::AppendPath(&out, artifact_name.value());
 
-    std::unique_ptr<IArchiveWriter> writer =
-        CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);
+    std::unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(diag, out);
 
     if (context_->IsVerbose()) {
-      context_->GetDiagnostics()->Note(DiagMessage() << "Writing output: " << out);
+      diag->Note(DiagMessage() << "Writing output: " << out);
     }
 
     if (!apk_->WriteToArchive(context_, table.get(), table_flattener_options, &filters,