Merge "Add composite string for battery summary in settings" into pi-dev
diff --git a/Android.mk b/Android.mk
index d7d9c90..88394d6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -196,7 +196,7 @@
     -since $(SRC_API_DIR)/25.txt 25 \
     -since $(SRC_API_DIR)/26.txt 26 \
     -since $(SRC_API_DIR)/27.txt 27 \
-    -since ./frameworks/base/api/current.txt P \
+    -since $(SRC_API_DIR)/28.txt 28 \
     -werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
     -overview $(LOCAL_PATH)/core/java/overview.html \
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 2ef2025..f7bfeae 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -204,6 +204,10 @@
 
 package android.app.usage {
 
+  public class NetworkStatsManager {
+    method public void setPollForce(boolean);
+  }
+
   public class StorageStatsManager {
     method public boolean isQuotaSupported(java.util.UUID);
     method public boolean isReservedSupported(java.util.UUID);
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 2bb7edc..09343f1 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -147,9 +147,21 @@
     }
 
     public void runSetForceAdoptable() throws RemoteException {
-        final boolean forceAdoptable = Boolean.parseBoolean(nextArg());
-        mSm.setDebugFlags(forceAdoptable ? StorageManager.DEBUG_FORCE_ADOPTABLE : 0,
-                StorageManager.DEBUG_FORCE_ADOPTABLE);
+        final int mask = StorageManager.DEBUG_ADOPTABLE_FORCE_ON
+                | StorageManager.DEBUG_ADOPTABLE_FORCE_OFF;
+        switch (nextArg()) {
+            case "on":
+            case "true":
+                mSm.setDebugFlags(StorageManager.DEBUG_ADOPTABLE_FORCE_ON, mask);
+                break;
+            case "off":
+                mSm.setDebugFlags(StorageManager.DEBUG_ADOPTABLE_FORCE_OFF, mask);
+                break;
+            case "default":
+            case "false":
+                mSm.setDebugFlags(0, mask);
+                break;
+        }
     }
 
     public void runSetSdcardfs() throws RemoteException {
@@ -289,7 +301,7 @@
         System.err.println("       sm list-volumes [public|private|emulated|all]");
         System.err.println("       sm has-adoptable");
         System.err.println("       sm get-primary-storage-uuid");
-        System.err.println("       sm set-force-adoptable [true|false]");
+        System.err.println("       sm set-force-adoptable [on|off|default]");
         System.err.println("       sm set-virtual-disk [true|false]");
         System.err.println("");
         System.err.println("       sm partition DISK [public|private|mixed] [ratio]");
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 11b5d9c..0213622 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2282,6 +2282,7 @@
 Landroid/view/Choreographer;->scheduleVsyncLocked()V
 Landroid/view/Choreographer;->USE_VSYNC:Z
 Landroid/view/ContextThemeWrapper;->getThemeResId()I
+Landroid/view/ContextThemeWrapper;->mInflater:Landroid/view/LayoutInflater;
 Landroid/view/ContextThemeWrapper;->mResources:Landroid/content/res/Resources;
 Landroid/view/ContextThemeWrapper;->mTheme:Landroid/content/res/Resources$Theme;
 Landroid/view/ContextThemeWrapper;->mThemeResource:I
@@ -2864,8 +2865,10 @@
 Landroid/widget/Switch;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
 Landroid/widget/TabHost$IntentContentStrategy;->getContentView()Landroid/view/View;
 Landroid/widget/TabHost$IntentContentStrategy;->tabClosed()V
+Landroid/widget/TabHost$TabSpec;->mIndicatorStrategy:Landroid/widget/TabHost$IndicatorStrategy;
 Landroid/widget/TabHost;->mTabSpecs:Ljava/util/List;
 Landroid/widget/TabHost$TabSpec;->mContentStrategy:Landroid/widget/TabHost$ContentStrategy;
+Landroid/widget/TabWidget;->mDrawBottomStrips:Z
 Landroid/widget/TabWidget;->mSelectedTab:I
 Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V
 Landroid/widget/TextView;->assumeLayout()V
@@ -3450,6 +3453,7 @@
 Ljava/lang/reflect/Executable;->artMethod:J
 Ljava/lang/reflect/Parameter;-><init>(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V
 Ljava/lang/reflect/Proxy;->invoke(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/ref/Reference;->getReferent()Ljava/lang/Object;
 Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V
 Ljava/lang/ref/Reference;->referent:Ljava/lang/Object;
 Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
@@ -3457,7 +3461,9 @@
 Ljava/lang/Runtime;->mLibPaths:[Ljava/lang/String;
 Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
 Ljava/lang/Short;->value:S
+Ljava/lang/String;->getCharsNoCheck(II[CI)V
 Ljava/lang/String;-><init>(II[C)V
+Ljava/lang/System;->arraycopy([CI[CII)V
 Ljava/lang/System;->arraycopy([II[III)V
 Ljava/lang/System;-><init>()V
 Ljava/lang/Thread;->contextClassLoader:Ljava/lang/ClassLoader;
@@ -3489,7 +3495,21 @@
 Ljava/lang/Void;-><init>()V
 Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator;
 Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl;
+Ljava/net/HttpCookie;->assignors:Ljava/util/Map;
+Ljava/net/HttpCookie;->comment:Ljava/lang/String;
+Ljava/net/HttpCookie;->commentURL:Ljava/lang/String;
+Ljava/net/HttpCookie;->domain:Ljava/lang/String;
+Ljava/net/HttpCookie;->header:Ljava/lang/String;
 Ljava/net/HttpCookie;->httpOnly:Z
+Ljava/net/HttpCookie;->maxAge:J
+Ljava/net/HttpCookie;->name:Ljava/lang/String;
+Ljava/net/HttpCookie;->path:Ljava/lang/String;
+Ljava/net/HttpCookie;->portlist:Ljava/lang/String;
+Ljava/net/HttpCookie;->secure:Z
+Ljava/net/HttpCookie;->toDiscard:Z
+Ljava/net/HttpCookie;->tspecials:Ljava/lang/String;
+Ljava/net/HttpCookie;->value:Ljava/lang/String;
+Ljava/net/HttpCookie;->version:I
 Ljava/net/HttpCookie;->whenCreated:J
 Ljava/net/Inet4Address;-><init>()V
 Ljava/net/Inet6Address;->holder6:Ljava/net/Inet6Address$Inet6AddressHolder;
@@ -3580,6 +3600,7 @@
 Ljava/util/PriorityQueue;->size:I
 Ljava/util/Random;->seedUniquifier()J
 Ljava/util/regex/Matcher;->appendPos:I
+Ljava/util/UUID;->leastSigBits:J
 Ljava/util/UUID;->mostSigBits:J
 Ljava/util/Vector;->elementData(I)Ljava/lang/Object;
 Ljava/util/zip/Deflater;->buf:[B
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 20149de..b456b72 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -759,6 +759,7 @@
     private static final int LOG_AM_ON_STOP_CALLED = 30049;
     private static final int LOG_AM_ON_RESTART_CALLED = 30058;
     private static final int LOG_AM_ON_DESTROY_CALLED = 30060;
+    private static final int LOG_AM_ON_ACTIVITY_RESULT_CALLED = 30062;
 
     private static class ManagedDialog {
         Dialog mDialog;
@@ -7438,8 +7439,8 @@
         }
     }
 
-    void dispatchActivityResult(String who, int requestCode,
-        int resultCode, Intent data) {
+    void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data,
+            String reason) {
         if (false) Log.v(
             TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
             + ", resCode=" + resultCode + ", data=" + data);
@@ -7475,6 +7476,7 @@
                 frag.onActivityResult(requestCode, resultCode, data);
             }
         }
+        writeEventLog(LOG_AM_ON_ACTIVITY_RESULT_CALLED, reason);
     }
 
     /**
diff --git a/core/java/android/app/ActivityGroup.java b/core/java/android/app/ActivityGroup.java
index 78a4dfd..228067c 100644
--- a/core/java/android/app/ActivityGroup.java
+++ b/core/java/android/app/ActivityGroup.java
@@ -16,11 +16,11 @@
 
 package android.app;
 
-import java.util.HashMap;
-
 import android.content.Intent;
 import android.os.Bundle;
 
+import java.util.HashMap;
+
 /**
  * A screen that contains and runs multiple embedded activities.
  *
@@ -109,7 +109,7 @@
 
     @Override
     void dispatchActivityResult(String who, int requestCode, int resultCode,
-            Intent data) {
+            Intent data, String reason) {
         if (who != null) {
             Activity act = mLocalActivityManager.getActivity(who);
             /*
@@ -123,7 +123,7 @@
                 return;
             }
         }
-        super.dispatchActivityResult(who, requestCode, resultCode, data);
+        super.dispatchActivityResult(who, requestCode, resultCode, data, reason);
     }
 }
 
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index db9d923..97c9fa5 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -377,6 +377,11 @@
     public abstract boolean isRecentsComponentHomeActivity(int userId);
 
     /**
+     * Cancels any currently running recents animation.
+     */
+    public abstract void cancelRecentsAnimation(boolean restoreHomeStackPosition);
+
+    /**
      * Whether an UID is active or idle.
      */
     public abstract boolean isUidActive(int uid);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a41da0e..037a87b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3730,7 +3730,7 @@
                 r.pendingIntents = null;
             }
             if (r.pendingResults != null) {
-                deliverResults(r, r.pendingResults);
+                deliverResults(r, r.pendingResults, reason);
                 r.pendingResults = null;
             }
             r.activity.performResume(r.startsNotResumed, reason);
@@ -4299,7 +4299,7 @@
         WindowManagerGlobal.getInstance().reportNewConfiguration(mConfiguration);
     }
 
-    private void deliverResults(ActivityClientRecord r, List<ResultInfo> results) {
+    private void deliverResults(ActivityClientRecord r, List<ResultInfo> results, String reason) {
         final int N = results.size();
         for (int i=0; i<N; i++) {
             ResultInfo ri = results.get(i);
@@ -4311,7 +4311,7 @@
                 if (DEBUG_RESULTS) Slog.v(TAG,
                         "Delivering result to activity " + r + " : " + ri);
                 r.activity.dispatchActivityResult(ri.mResultWho,
-                        ri.mRequestCode, ri.mResultCode, ri.mData);
+                        ri.mRequestCode, ri.mResultCode, ri.mData, reason);
             } catch (Exception e) {
                 if (!mInstrumentation.onException(r.activity, e)) {
                     throw new RuntimeException(
@@ -4324,7 +4324,7 @@
     }
 
     @Override
-    public void handleSendResult(IBinder token, List<ResultInfo> results) {
+    public void handleSendResult(IBinder token, List<ResultInfo> results, String reason) {
         ActivityClientRecord r = mActivities.get(token);
         if (DEBUG_RESULTS) Slog.v(TAG, "Handling send result to " + r);
         if (r != null) {
@@ -4359,9 +4359,9 @@
                 }
             }
             checkAndBlockForNetworkAccess();
-            deliverResults(r, results);
+            deliverResults(r, results, reason);
             if (resumed) {
-                r.activity.performResume(false, "handleSendResult");
+                r.activity.performResume(false, reason);
                 r.activity.mTemporaryPause = false;
             }
         }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1084b42..0e44833 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2831,7 +2831,7 @@
         synchronized (mLock) {
             if (mArtManager == null) {
                 try {
-                    mArtManager = new ArtManager(mPM.getArtManager());
+                    mArtManager = new ArtManager(mContext, mPM.getArtManager());
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index e26d989..ea0d703 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -121,7 +121,7 @@
             Configuration overrideConfig, int displayId);
 
     /** Deliver result from another activity. */
-    public abstract void handleSendResult(IBinder token, List<ResultInfo> results);
+    public abstract void handleSendResult(IBinder token, List<ResultInfo> results, String reason);
 
     /** Deliver multi-window mode change notification. */
     public abstract void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 919f714..569c2bd 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -575,6 +575,11 @@
     void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
             in Rect tempDockedTaskInsetBounds,
             in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
+    /**
+     * Sets whether we are currently in an interactive split screen resize operation where we
+     * are changing the docked stack size.
+     */
+    void setSplitScreenResizing(boolean resizing);
     int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
     // Gets the URI permissions granted to an arbitrary package (or all packages if null)
     // NOTE: this is different from getPersistedUriPermissions(), which returns the URIs the package
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 0f2a11a..cd12710 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -87,6 +87,7 @@
     boolean onlyHasDefaultChannel(String pkg, int uid);
     ParceledListSlice getRecentNotifyingAppsForUser(int userId);
     int getBlockedAppCount(int userId);
+    boolean areChannelsBypassingDnd();
 
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 22da924..4f88a03 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -91,6 +91,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * A class that represents how a persistent notification is to be presented to
@@ -2306,6 +2307,45 @@
     }
 
     /**
+     * Note all {@link Uri} that are referenced internally, with the expectation
+     * that Uri permission grants will need to be issued to ensure the recipient
+     * of this object is able to render its contents.
+     *
+     * @hide
+     */
+    public void visitUris(@NonNull Consumer<Uri> visitor) {
+        visitor.accept(sound);
+
+        if (tickerView != null) tickerView.visitUris(visitor);
+        if (contentView != null) contentView.visitUris(visitor);
+        if (bigContentView != null) bigContentView.visitUris(visitor);
+        if (headsUpContentView != null) headsUpContentView.visitUris(visitor);
+
+        if (extras != null) {
+            visitor.accept(extras.getParcelable(EXTRA_AUDIO_CONTENTS_URI));
+            visitor.accept(extras.getParcelable(EXTRA_BACKGROUND_IMAGE_URI));
+        }
+
+        if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) {
+            final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
+            if (!ArrayUtils.isEmpty(messages)) {
+                for (MessagingStyle.Message message : MessagingStyle.Message
+                        .getMessagesFromBundleArray(messages)) {
+                    visitor.accept(message.getDataUri());
+                }
+            }
+
+            final Parcelable[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
+            if (!ArrayUtils.isEmpty(historic)) {
+                for (MessagingStyle.Message message : MessagingStyle.Message
+                        .getMessagesFromBundleArray(historic)) {
+                    visitor.accept(message.getDataUri());
+                }
+            }
+        }
+    }
+
+    /**
      * Removes heavyweight parts of the Notification object for archival or for sending to
      * listeners when the full contents are not necessary.
      * @hide
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 757fc64..93be932 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1167,6 +1167,23 @@
         public final int suppressedVisualEffects;
 
         /**
+         * @hide
+         */
+        public static final int STATE_CHANNELS_BYPASSING_DND = 1 << 0;
+
+        /**
+         * @hide
+         */
+        public static final int STATE_UNSET = -1;
+
+        /**
+         * Notification state information that is necessary to determine Do Not Disturb behavior.
+         * Bitmask of STATE_* constants.
+         * @hide
+         */
+        public final int state;
+
+        /**
          * Constructs a policy for Do Not Disturb priority mode behavior.
          *
          * <p>
@@ -1181,7 +1198,7 @@
          */
         public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders) {
             this(priorityCategories, priorityCallSenders, priorityMessageSenders,
-                    SUPPRESSED_EFFECTS_UNSET);
+                    SUPPRESSED_EFFECTS_UNSET, STATE_UNSET);
         }
 
         /**
@@ -1219,11 +1236,23 @@
             this.priorityCallSenders = priorityCallSenders;
             this.priorityMessageSenders = priorityMessageSenders;
             this.suppressedVisualEffects = suppressedVisualEffects;
+            this.state = STATE_UNSET;
+        }
+
+        /** @hide */
+        public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
+                int suppressedVisualEffects, int state) {
+            this.priorityCategories = priorityCategories;
+            this.priorityCallSenders = priorityCallSenders;
+            this.priorityMessageSenders = priorityMessageSenders;
+            this.suppressedVisualEffects = suppressedVisualEffects;
+            this.state = state;
         }
 
         /** @hide */
         public Policy(Parcel source) {
-            this(source.readInt(), source.readInt(), source.readInt(), source.readInt());
+            this(source.readInt(), source.readInt(), source.readInt(), source.readInt(),
+                    source.readInt());
         }
 
         @Override
@@ -1232,6 +1261,7 @@
             dest.writeInt(priorityCallSenders);
             dest.writeInt(priorityMessageSenders);
             dest.writeInt(suppressedVisualEffects);
+            dest.writeInt(state);
         }
 
         @Override
@@ -1264,6 +1294,8 @@
                     + ",priorityMessageSenders=" + prioritySendersToString(priorityMessageSenders)
                     + ",suppressedVisualEffects="
                     + suppressedEffectsToString(suppressedVisualEffects)
+                    + ",areChannelsBypassingDnd=" + (((state & STATE_CHANNELS_BYPASSING_DND) != 0)
+                        ? "true" : "false")
                     + "]";
         }
 
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 545463c..e57f585 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -16,7 +16,6 @@
 
 package android.app.servertransaction;
 
-import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.app.ClientTransactionHandler;
@@ -37,16 +36,17 @@
 
     private List<ResultInfo> mResultInfoList;
 
+    /* TODO(b/78294732)
     @Override
     public int getPostExecutionState() {
         return ON_RESUME;
-    }
+    }*/
 
     @Override
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
-        client.handleSendResult(token, mResultInfoList);
+        client.handleSendResult(token, mResultInfoList, "ACTIVITY_RESULT");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 0b21196..9f46f20 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -20,6 +20,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.usage.NetworkStats.Bucket;
 import android.content.Context;
 import android.net.ConnectivityManager;
@@ -111,7 +112,9 @@
     /** @hide */
     public static final int FLAG_POLL_ON_OPEN = 1 << 0;
     /** @hide */
-    public static final int FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN = 1 << 1;
+    public static final int FLAG_POLL_FORCE = 1 << 1;
+    /** @hide */
+    public static final int FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN = 1 << 2;
 
     private int mFlags;
 
@@ -141,6 +144,16 @@
     }
 
     /** @hide */
+    @TestApi
+    public void setPollForce(boolean pollForce) {
+        if (pollForce) {
+            mFlags |= FLAG_POLL_FORCE;
+        } else {
+            mFlags &= ~FLAG_POLL_FORCE;
+        }
+    }
+
+    /** @hide */
     public void setAugmentWithSubscriptionPlan(boolean augmentWithSubscriptionPlan) {
         if (augmentWithSubscriptionPlan) {
             mFlags |= FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ee667c2..1b6b5a0 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -80,8 +80,7 @@
  * {@link #getBondedDevices()}; start device discovery with
  * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
  * listen for incoming RFComm connection requests with {@link
- * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
- * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for
+ * #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
  * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
  * </p>
  * <p>This class is thread safe.</p>
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index 7ae4cb7..3a05e70 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -57,13 +57,35 @@
         if (o instanceof BluetoothCodecStatus) {
             BluetoothCodecStatus other = (BluetoothCodecStatus) o;
             return (Objects.equals(other.mCodecConfig, mCodecConfig)
-                    && Objects.equals(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities)
-                    && Objects.equals(other.mCodecsSelectableCapabilities,
+                    && sameCapabilities(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities)
+                    && sameCapabilities(other.mCodecsSelectableCapabilities,
                     mCodecsSelectableCapabilities));
         }
         return false;
     }
 
+    /**
+     * Checks whether two arrays of capabilities contain same capabilities.
+     * The order of the capabilities in each array is ignored.
+     *
+     * @param c1 the first array of capabilities to compare
+     * @param c2 the second array of capabilities to compare
+     * @return true if both arrays contain same capabilities
+     */
+    private static boolean sameCapabilities(BluetoothCodecConfig[] c1,
+                                            BluetoothCodecConfig[] c2) {
+        if (c1 == null) {
+            return (c2 == null);
+        }
+        if (c2 == null) {
+            return false;
+        }
+        if (c1.length != c2.length) {
+            return false;
+        }
+        return Arrays.asList(c1).containsAll(Arrays.asList(c2));
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(mCodecConfig, mCodecsLocalCapabilities,
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 4ed2500..ef1b0bd 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -701,10 +701,14 @@
      * <p>If the local device has already exposed services when this function
      * is called, a service update notification will be sent to all clients.
      *
+     * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate
+     * whether this service has been added successfully. Do not add another service
+     * before this callback.
+     *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @param service Service to be added to the list of services provided by this device.
-     * @return true, if the service has been added successfully
+     * @return true, if the request to add service has been initiated
      */
     public boolean addService(BluetoothGattService service) {
         if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index ead6c25..fc58533 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -396,4 +396,14 @@
         mPackage = pkg;
         mClass = in.readString();
     }
+
+    /**
+     * Interface for classes associated with a component name.
+     * @hide
+     */
+    @FunctionalInterface
+    public interface WithComponentName {
+        /** Return the associated component name. */
+        ComponentName getComponentName();
+    }
 }
diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java
index ded11cfd..2d521e9 100644
--- a/core/java/android/content/SyncStatusInfo.java
+++ b/core/java/android/content/SyncStatusInfo.java
@@ -28,10 +28,15 @@
 public class SyncStatusInfo implements Parcelable {
     private static final String TAG = "Sync";
 
-    static final int VERSION = 5;
+    static final int VERSION = 6;
 
     private static final int MAX_EVENT_COUNT = 10;
 
+    /**
+     * Number of sync sources. KEEP THIS AND SyncStorageEngine.SOURCES IN SYNC.
+     */
+    private static final int SOURCE_COUNT = 6;
+
     public final int authorityId;
 
     /**
@@ -120,7 +125,10 @@
     public long initialFailureTime;
     public boolean pending;
     public boolean initialize;
-    
+
+    public final long[] perSourceLastSuccessTimes = new long[SOURCE_COUNT];
+    public final long[] perSourceLastFailureTimes = new long[SOURCE_COUNT];
+
   // Warning: It is up to the external caller to ensure there are
   // no race conditions when accessing this list
   private ArrayList<Long> periodicSyncTimes;
@@ -191,6 +199,10 @@
 
         todayStats.writeToParcel(parcel);
         yesterdayStats.writeToParcel(parcel);
+
+        // Version 6.
+        parcel.writeLongArray(perSourceLastSuccessTimes);
+        parcel.writeLongArray(perSourceLastFailureTimes);
     }
 
     public SyncStatusInfo(Parcel parcel) {
@@ -260,6 +272,10 @@
             todayStats.readFromParcel(parcel);
             yesterdayStats.readFromParcel(parcel);
         }
+        if (version >= 6) {
+            parcel.readLongArray(perSourceLastSuccessTimes);
+            parcel.readLongArray(perSourceLastFailureTimes);
+        }
     }
 
     public SyncStatusInfo(SyncStatusInfo other) {
@@ -284,6 +300,13 @@
         }
         mLastEventTimes.addAll(other.mLastEventTimes);
         mLastEvents.addAll(other.mLastEvents);
+
+        copy(perSourceLastSuccessTimes, other.perSourceLastSuccessTimes);
+        copy(perSourceLastFailureTimes, other.perSourceLastFailureTimes);
+    }
+
+    private static void copy(long[] to, long[] from) {
+        System.arraycopy(from, 0, to, 0, to.length);
     }
 
     public void setPeriodicSyncTime(int index, long when) {
@@ -332,6 +355,34 @@
         return mLastEvents.get(i);
     }
 
+    /** Call this when a sync has succeeded. */
+    public void setLastSuccess(int source, long lastSyncTime) {
+        lastSuccessTime = lastSyncTime;
+        lastSuccessSource = source;
+        lastFailureTime = 0;
+        lastFailureSource = -1;
+        lastFailureMesg = null;
+        initialFailureTime = 0;
+
+        if (0 <= source && source < perSourceLastSuccessTimes.length) {
+            perSourceLastSuccessTimes[source] = lastSyncTime;
+        }
+    }
+
+    /** Call this when a sync has failed. */
+    public void setLastFailure(int source, long lastSyncTime, String failureMessage) {
+        lastFailureTime = lastSyncTime;
+        lastFailureSource = source;
+        lastFailureMesg = failureMessage;
+        if (initialFailureTime == 0) {
+            initialFailureTime = lastSyncTime;
+        }
+
+        if (0 <= source && source < perSourceLastFailureTimes.length) {
+            perSourceLastFailureTimes[source] = lastSyncTime;
+        }
+    }
+
     public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
         public SyncStatusInfo createFromParcel(Parcel in) {
             return new SyncStatusInfo(in);
@@ -356,7 +407,7 @@
     }
 
     /**
-     * If the last reset was not not today, move today's stats to yesterday's and clear today's.
+     * If the last reset was not today, move today's stats to yesterday's and clear today's.
      */
     public void maybeResetTodayStats(boolean clockValid, boolean force) {
         final long now = System.currentTimeMillis();
@@ -391,4 +442,4 @@
         return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
                 && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java
index 4129398..b0970f4 100644
--- a/core/java/android/content/pm/dex/ArtManager.java
+++ b/core/java/android/content/pm/dex/ArtManager.java
@@ -16,12 +16,16 @@
 
 package android.content.pm.dex;
 
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.Manifest.permission.READ_RUNTIME_PROFILES;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.content.Context;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -62,13 +66,14 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProfileType {}
 
-
-    private IArtManager mArtManager;
+    private final Context mContext;
+    private final IArtManager mArtManager;
 
     /**
      * @hide
      */
-    public ArtManager(@NonNull IArtManager manager) {
+    public ArtManager(@NonNull Context context, @NonNull IArtManager manager) {
+        mContext = context;
         mArtManager = manager;
     }
 
@@ -99,7 +104,7 @@
      * @param callback the callback which should be used for the result
      * @param executor the executor which should be used to post the result
      */
-    @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+    @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
     public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
             @Nullable String codePath, @NonNull @CallbackExecutor Executor executor,
             @NonNull SnapshotRuntimeProfileCallback callback) {
@@ -108,9 +113,10 @@
         SnapshotRuntimeProfileCallbackDelegate delegate =
                 new SnapshotRuntimeProfileCallbackDelegate(callback, executor);
         try {
-            mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate);
+            mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate,
+                    mContext.getOpPackageName());
         } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -122,14 +128,13 @@
      * @param profileType can be either {@link ArtManager#PROFILE_APPS}
      *                    or {@link ArtManager#PROFILE_BOOT_IMAGE}
      */
-    @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+    @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
     public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
         try {
-            return mArtManager.isRuntimeProfilingEnabled(profileType);
+            return mArtManager.isRuntimeProfilingEnabled(profileType, mContext.getOpPackageName());
         } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
+            throw e.rethrowAsRuntimeException();
         }
-        return false;
     }
 
     /**
diff --git a/core/java/android/content/pm/dex/IArtManager.aidl b/core/java/android/content/pm/dex/IArtManager.aidl
index 6abfdba..7f0de7e 100644
--- a/core/java/android/content/pm/dex/IArtManager.aidl
+++ b/core/java/android/content/pm/dex/IArtManager.aidl
@@ -44,8 +44,8 @@
      * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
      * {@code profileType}.
      */
-    oneway void snapshotRuntimeProfile(int profileType, in String packageName,
-        in String codePath, in ISnapshotRuntimeProfileCallback callback);
+    void snapshotRuntimeProfile(int profileType, in String packageName,
+        in String codePath, in ISnapshotRuntimeProfileCallback callback, String callingPackage);
 
      /**
        * Returns true if runtime profiles are enabled for the given type, false otherwise.
@@ -54,5 +54,5 @@
        *
        * @param profileType
        */
-    boolean isRuntimeProfilingEnabled(int profileType);
+    boolean isRuntimeProfilingEnabled(int profileType, String callingPackage);
 }
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 435bcb0..90d407c 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -498,7 +498,7 @@
     @Override
     public int hashCode() {
         // secondaryIds and vendorIds are ignored for equality/hashing
-        return Objects.hash(mProgramType, mPrimaryId);
+        return mPrimaryId.hashCode();
     }
 
     @Override
@@ -507,7 +507,8 @@
         if (!(obj instanceof ProgramSelector)) return false;
         ProgramSelector other = (ProgramSelector) obj;
         // secondaryIds and vendorIds are ignored for equality/hashing
-        return other.getProgramType() == mProgramType && mPrimaryId.equals(other.getPrimaryId());
+        // programType can be inferred from primaryId, thus not checked
+        return mPrimaryId.equals(other.getPrimaryId());
     }
 
     private ProgramSelector(Parcel in) {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index f525b1f..300a78b 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -50,6 +50,7 @@
     private String mIfaceName;
     private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
     private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
+    private ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<InetAddress>();
     private boolean mUsePrivateDns;
     private String mPrivateDnsServerName;
     private String mDomains;
@@ -167,6 +168,9 @@
             mIfaceName = source.getInterfaceName();
             for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
             for (InetAddress i : source.getDnsServers()) mDnses.add(i);
+            for (InetAddress i : source.getValidatedPrivateDnsServers()) {
+                mValidatedPrivateDnses.add(i);
+            }
             mUsePrivateDns = source.mUsePrivateDns;
             mPrivateDnsServerName = source.mPrivateDnsServerName;
             mDomains = source.getDomains();
@@ -374,7 +378,7 @@
      * Replaces the DNS servers in this {@code LinkProperties} with
      * the given {@link Collection} of {@link InetAddress} objects.
      *
-     * @param addresses The {@link Collection} of DNS servers to set in this object.
+     * @param dnsServers The {@link Collection} of DNS servers to set in this object.
      * @hide
      */
     public void setDnsServers(Collection<InetAddress> dnsServers) {
@@ -448,6 +452,65 @@
     }
 
     /**
+     * Adds the given {@link InetAddress} to the list of validated private DNS servers,
+     * if not present. This is distinct from the server name in that these are actually
+     * resolved addresses.
+     *
+     * @param dnsServer The {@link InetAddress} to add to the list of validated private DNS servers.
+     * @return true if the DNS server was added, false if it was already present.
+     * @hide
+     */
+    public boolean addValidatedPrivateDnsServer(InetAddress dnsServer) {
+        if (dnsServer != null && !mValidatedPrivateDnses.contains(dnsServer)) {
+            mValidatedPrivateDnses.add(dnsServer);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Removes the given {@link InetAddress} from the list of validated private DNS servers.
+     *
+     * @param dnsServer The {@link InetAddress} to remove from the list of validated private DNS
+     *        servers.
+     * @return true if the DNS server was removed, false if it did not exist.
+     * @hide
+     */
+    public boolean removeValidatedPrivateDnsServer(InetAddress dnsServer) {
+        if (dnsServer != null) {
+            return mValidatedPrivateDnses.remove(dnsServer);
+        }
+        return false;
+    }
+
+    /**
+     * Replaces the validated private DNS servers in this {@code LinkProperties} with
+     * the given {@link Collection} of {@link InetAddress} objects.
+     *
+     * @param dnsServers The {@link Collection} of validated private DNS servers to set in this
+     *        object.
+     * @hide
+     */
+    public void setValidatedPrivateDnsServers(Collection<InetAddress> dnsServers) {
+        mValidatedPrivateDnses.clear();
+        for (InetAddress dnsServer: dnsServers) {
+            addValidatedPrivateDnsServer(dnsServer);
+        }
+    }
+
+    /**
+     * Returns all the {@link InetAddress} for validated private DNS servers on this link.
+     * These are resolved from the private DNS server name.
+     *
+     * @return An umodifiable {@link List} of {@link InetAddress} for validated private
+     *         DNS servers on this link.
+     * @hide
+     */
+    public List<InetAddress> getValidatedPrivateDnsServers() {
+        return Collections.unmodifiableList(mValidatedPrivateDnses);
+    }
+
+    /**
      * Sets the DNS domain search path used on this link.
      *
      * @param domains A {@link String} listing in priority order the comma separated
@@ -715,6 +778,15 @@
             privateDnsServerName = "PrivateDnsServerName: " + mPrivateDnsServerName + " ";
         }
 
+        String validatedPrivateDns = "";
+        if (!mValidatedPrivateDnses.isEmpty()) {
+            validatedPrivateDns = "ValidatedPrivateDnsAddresses: [";
+            for (InetAddress addr : mValidatedPrivateDnses) {
+                validatedPrivateDns += addr.getHostAddress() + ",";
+            }
+            validatedPrivateDns += "] ";
+        }
+
         String domainName = "Domains: " + mDomains;
 
         String mtu = " MTU: " + mMtu;
@@ -959,7 +1031,7 @@
             if (mDomains.equals(targetDomains) == false) return false;
         }
         return (mDnses.size() == targetDnses.size()) ?
-                    mDnses.containsAll(targetDnses) : false;
+                mDnses.containsAll(targetDnses) : false;
     }
 
     /**
@@ -977,6 +1049,20 @@
     }
 
     /**
+     * Compares this {@code LinkProperties} validated private DNS addresses against
+     * the target
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isIdenticalValidatedPrivateDnses(LinkProperties target) {
+        Collection<InetAddress> targetDnses = target.getValidatedPrivateDnsServers();
+        return (mValidatedPrivateDnses.size() == targetDnses.size())
+                ? mValidatedPrivateDnses.containsAll(targetDnses) : false;
+    }
+
+    /**
      * Compares this {@code LinkProperties} Routes against the target
      *
      * @param target LinkProperties to compare.
@@ -986,7 +1072,7 @@
     public boolean isIdenticalRoutes(LinkProperties target) {
         Collection<RouteInfo> targetRoutes = target.getRoutes();
         return (mRoutes.size() == targetRoutes.size()) ?
-                    mRoutes.containsAll(targetRoutes) : false;
+                mRoutes.containsAll(targetRoutes) : false;
     }
 
     /**
@@ -998,7 +1084,7 @@
      */
     public boolean isIdenticalHttpProxy(LinkProperties target) {
         return getHttpProxy() == null ? target.getHttpProxy() == null :
-                    getHttpProxy().equals(target.getHttpProxy());
+                getHttpProxy().equals(target.getHttpProxy());
     }
 
     /**
@@ -1074,6 +1160,7 @@
                 && isIdenticalAddresses(target)
                 && isIdenticalDnses(target)
                 && isIdenticalPrivateDns(target)
+                && isIdenticalValidatedPrivateDnses(target)
                 && isIdenticalRoutes(target)
                 && isIdenticalHttpProxy(target)
                 && isIdenticalStackedLinks(target)
@@ -1121,6 +1208,19 @@
     }
 
     /**
+     * Compares the validated private DNS addresses in this LinkProperties with another
+     * LinkProperties.
+     *
+     * @param target a LinkProperties with the new list of validated private dns addresses
+     * @return the differences between the DNS addresses.
+     * @hide
+     */
+    public CompareResult<InetAddress> compareValidatedPrivateDnses(LinkProperties target) {
+        return new CompareResult<>(mValidatedPrivateDnses,
+                target != null ? target.getValidatedPrivateDnsServers() : null);
+    }
+
+    /**
      * Compares all routes in this LinkProperties with another LinkProperties,
      * examining both the the base link and all stacked links.
      *
@@ -1168,6 +1268,7 @@
         return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
                 + mLinkAddresses.size() * 31
                 + mDnses.size() * 37
+                + mValidatedPrivateDnses.size() * 61
                 + ((null == mDomains) ? 0 : mDomains.hashCode())
                 + mRoutes.size() * 41
                 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
@@ -1184,12 +1285,16 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(getInterfaceName());
         dest.writeInt(mLinkAddresses.size());
-        for(LinkAddress linkAddress : mLinkAddresses) {
+        for (LinkAddress linkAddress : mLinkAddresses) {
             dest.writeParcelable(linkAddress, flags);
         }
 
         dest.writeInt(mDnses.size());
-        for(InetAddress d : mDnses) {
+        for (InetAddress d : mDnses) {
+            dest.writeByteArray(d.getAddress());
+        }
+        dest.writeInt(mValidatedPrivateDnses.size());
+        for (InetAddress d : mValidatedPrivateDnses) {
             dest.writeByteArray(d.getAddress());
         }
         dest.writeBoolean(mUsePrivateDns);
@@ -1198,7 +1303,7 @@
         dest.writeInt(mMtu);
         dest.writeString(mTcpBufferSizes);
         dest.writeInt(mRoutes.size());
-        for(RouteInfo route : mRoutes) {
+        for (RouteInfo route : mRoutes) {
             dest.writeParcelable(route, flags);
         }
 
@@ -1225,26 +1330,33 @@
                     netProp.setInterfaceName(iface);
                 }
                 int addressCount = in.readInt();
-                for (int i=0; i<addressCount; i++) {
-                    netProp.addLinkAddress((LinkAddress)in.readParcelable(null));
+                for (int i = 0; i < addressCount; i++) {
+                    netProp.addLinkAddress((LinkAddress) in.readParcelable(null));
                 }
                 addressCount = in.readInt();
-                for (int i=0; i<addressCount; i++) {
+                for (int i = 0; i < addressCount; i++) {
                     try {
                         netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray()));
                     } catch (UnknownHostException e) { }
                 }
+                addressCount = in.readInt();
+                for (int i = 0; i < addressCount; i++) {
+                    try {
+                        netProp.addValidatedPrivateDnsServer(
+                                InetAddress.getByAddress(in.createByteArray()));
+                    } catch (UnknownHostException e) { }
+                }
                 netProp.setUsePrivateDns(in.readBoolean());
                 netProp.setPrivateDnsServerName(in.readString());
                 netProp.setDomains(in.readString());
                 netProp.setMtu(in.readInt());
                 netProp.setTcpBufferSizes(in.readString());
                 addressCount = in.readInt();
-                for (int i=0; i<addressCount; i++) {
-                    netProp.addRoute((RouteInfo)in.readParcelable(null));
+                for (int i = 0; i < addressCount; i++) {
+                    netProp.addRoute((RouteInfo) in.readParcelable(null));
                 }
                 if (in.readByte() == 1) {
-                    netProp.setHttpProxy((ProxyInfo)in.readParcelable(null));
+                    netProp.setHttpProxy((ProxyInfo) in.readParcelable(null));
                 }
                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
@@ -1259,16 +1371,16 @@
             }
         };
 
-        /**
-         * Check the valid MTU range based on IPv4 or IPv6.
-         * @hide
-         */
-        public static boolean isValidMtu(int mtu, boolean ipv6) {
-            if (ipv6) {
-                if ((mtu >= MIN_MTU_V6 && mtu <= MAX_MTU)) return true;
-            } else {
-                if ((mtu >= MIN_MTU && mtu <= MAX_MTU)) return true;
-            }
-            return false;
+    /**
+     * Check the valid MTU range based on IPv4 or IPv6.
+     * @hide
+     */
+    public static boolean isValidMtu(int mtu, boolean ipv6) {
+        if (ipv6) {
+            if (mtu >= MIN_MTU_V6 && mtu <= MAX_MTU) return true;
+        } else {
+            if (mtu >= MIN_MTU && mtu <= MAX_MTU) return true;
         }
+        return false;
+    }
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1d232bf..0b4b921 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -4447,8 +4447,7 @@
             pw.println(sb.toString());
         }
 
-        final long dischargeScreenOnCount =
-                dischargeCount - dischargeScreenOffCount - dischargeScreenDozeCount;
+        final long dischargeScreenOnCount = dischargeCount - dischargeScreenOffCount;
         if (dischargeScreenOnCount >= 0) {
             sb.setLength(0);
             sb.append(prefix);
diff --git a/core/java/android/os/ISchedulingPolicyService.aidl b/core/java/android/os/ISchedulingPolicyService.aidl
index efcf59a..78d299a 100644
--- a/core/java/android/os/ISchedulingPolicyService.aidl
+++ b/core/java/android/os/ISchedulingPolicyService.aidl
@@ -31,4 +31,13 @@
      */
     int requestPriority(int pid, int tid, int prio, boolean isForApp);
 
+    /**
+     * Move media.codec process between SP_FOREGROUND and SP_TOP_APP.
+     * When 'enable' is 'true', server will attempt to move media.codec process
+     * from SP_FOREGROUND into SP_TOP_APP cpuset. A valid 'client' must be
+     * provided for the server to receive death notifications. When 'enable'
+     * is 'false', server will attempt to move media.codec process back to
+     * the original cpuset, and 'client' is ignored in this case.
+     */
+    int requestCpusetBoost(boolean enable, IBinder client);
 }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 59380fd..3eaecf9 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -811,6 +811,10 @@
 
             /**
              * Detect reflective usage of APIs that are not part of the public Android SDK.
+             *
+             * <p>Note that any non-SDK APIs that this processes accesses before this detection is
+             * enabled may not be detected. To ensure that all such API accesses are detected,
+             * you should apply this policy as early as possible after process creation.
              */
             public Builder detectNonSdkApiUsage() {
                 return enable(DETECT_VM_NON_SDK_API_USAGE);
@@ -1885,8 +1889,10 @@
 
             if ((sVmPolicy.mask & DETECT_VM_NON_SDK_API_USAGE) != 0) {
                 VMRuntime.setNonSdkApiUsageConsumer(sNonSdkApiUsageConsumer);
+                VMRuntime.setDedupeHiddenApiWarnings(false);
             } else {
                 VMRuntime.setNonSdkApiUsageConsumer(null);
+                VMRuntime.setDedupeHiddenApiWarnings(true);
             }
         }
     }
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 673da50..6994033 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -475,11 +475,14 @@
      * @param exemptions List of hidden API exemption prefixes. Any matching members are treated as
      *        whitelisted/public APIs (i.e. allowed, no logging of usage).
      */
-    public void setApiBlacklistExemptions(List<String> exemptions) {
+    public boolean setApiBlacklistExemptions(List<String> exemptions) {
         synchronized (mLock) {
             mApiBlacklistExemptions = exemptions;
-            maybeSetApiBlacklistExemptions(primaryZygoteState, true);
-            maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
+            boolean ok = maybeSetApiBlacklistExemptions(primaryZygoteState, true);
+            if (ok) {
+                ok = maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
+            }
+            return ok;
         }
     }
 
@@ -499,12 +502,13 @@
     }
 
     @GuardedBy("mLock")
-    private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
+    private boolean maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
         if (state == null || state.isClosed()) {
-            return;
+            Slog.e(LOG_TAG, "Can't set API blacklist exemptions: no zygote connection");
+            return false;
         }
         if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
-            return;
+            return true;
         }
         try {
             state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
@@ -520,8 +524,11 @@
             if (status != 0) {
                 Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
             }
+            return true;
         } catch (IOException ioe) {
             Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe);
+            mApiBlacklistExemptions = Collections.emptyList();
+            return false;
         }
     }
 
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8905ad1..2d1bb2f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -115,7 +115,7 @@
     /** {@hide} */
     public static final String PROP_HAS_RESERVED = "vold.has_reserved";
     /** {@hide} */
-    public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
+    public static final String PROP_ADOPTABLE = "persist.sys.adoptable";
     /** {@hide} */
     public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe";
     /** {@hide} */
@@ -197,15 +197,17 @@
     public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
 
     /** {@hide} */
-    public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
+    public static final int DEBUG_ADOPTABLE_FORCE_ON = 1 << 0;
     /** {@hide} */
-    public static final int DEBUG_EMULATE_FBE = 1 << 1;
+    public static final int DEBUG_ADOPTABLE_FORCE_OFF = 1 << 1;
     /** {@hide} */
-    public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2;
+    public static final int DEBUG_EMULATE_FBE = 1 << 2;
     /** {@hide} */
-    public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3;
+    public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 3;
     /** {@hide} */
-    public static final int DEBUG_VIRTUAL_DISK = 1 << 4;
+    public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 4;
+    /** {@hide} */
+    public static final int DEBUG_VIRTUAL_DISK = 1 << 5;
 
     // NOTE: keep in sync with installd
     /** {@hide} */
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 60537a4..6c18b45 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -496,9 +496,9 @@
  *
  * <p>Apps that use standard Android widgets support autofill out-of-the-box and need to do
  * very little to improve their user experience (annotating autofillable views and providing
- * autofill hints). However, some apps do their own rendering and the rendered content may
- * contain semantic structure that needs to be surfaced to the autofill framework. The platform
- * exposes APIs to achieve this, however it could take some time until these apps implement
+ * autofill hints). However, some apps (typically browsers) do their own rendering and the rendered
+ * content may contain semantic structure that needs to be surfaced to the autofill framework. The
+ * platform exposes APIs to achieve this, however it could take some time until these apps implement
  * autofill support.
  *
  * <p>To enable autofill for such apps the platform provides a compatibility mode in which the
@@ -521,15 +521,33 @@
  *     &lt;meta-data android:name="android.autofill" android:resource="@xml/autofillservice" /&gt;
  * &lt;/service&gt;</pre>
  *
- * <P>In the XML file you can specify one or more packages for which to enable compatibility
+ * <p>In the XML file you can specify one or more packages for which to enable compatibility
  * mode. Below is a sample meta-data declaration:
  *
  * <pre> &lt;autofill-service xmlns:android="http://schemas.android.com/apk/res/android"&gt;
  *     &lt;compatibility-package android:name="foo.bar.baz" android:maxLongVersionCode="1000000000"/&gt;
  * &lt;/autofill-service&gt;</pre>
  *
- * <p>When using compatibility mode, the {@link SaveInfo.Builder#setFlags(int) SaveInfo flags}
- * automatically include {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE}.
+ * <p>Notice that compatibility mode has limitations such as:
+ * <ul>
+ * <li>No manual autofill requests. Hence, the {@link FillRequest}
+ * {@link FillRequest#getFlags() flags} never have the {@link FillRequest#FLAG_MANUAL_REQUEST} flag.
+ * <li>The value of password fields are most likely masked&mdash;for example, {@code ****} instead
+ * of {@code 1234}. Hence, you must be careful when using these values to avoid updating the user
+ * data with invalid input. For example, when you parse the {@link FillRequest} and detect a
+ * password field, you could check if its
+ * {@link android.app.assist.AssistStructure.ViewNode#getInputType()
+ * input type} has password flags and if so, don't add it to the {@link SaveInfo} object.
+ * <li>The autofill context is not always {@link AutofillManager#commit() committed} when an HTML
+ * form is submitted. Hence, you must use other mechanisms to trigger save, such as setting the
+ * {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} flag on {@link SaveInfo.Builder#setFlags(int)}
+ * or using {@link SaveInfo.Builder#setTriggerId(AutofillId)}.
+ * <li>Browsers often provide their own autofill management system. When both the browser and
+ * the platform render an autofill dialog at the same time, the result can be confusing to the user.
+ * Such browsers typically offer an option for users to disable autofill, so your service should
+ * also allow users to disable compatiblity mode for specific apps. That way, it is up to the user
+ * to decide which autofill mechanism&mdash;the browser's or the platform's&mdash;should be used.
+ * </ul>
  */
 public abstract class AutofillService extends Service {
     private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 32737c5..a7d70d0 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -807,7 +807,8 @@
      * @return An array of active notifications, sorted in natural order.
      */
     public StatusBarNotification[] getActiveNotifications() {
-        return getActiveNotifications(null, TRIM_FULL);
+        StatusBarNotification[] activeNotifications = getActiveNotifications(null, TRIM_FULL);
+        return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
     }
 
     /**
@@ -842,7 +843,8 @@
      */
     @SystemApi
     public StatusBarNotification[] getActiveNotifications(int trim) {
-        return getActiveNotifications(null, trim);
+        StatusBarNotification[] activeNotifications = getActiveNotifications(null, trim);
+        return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
     }
 
     /**
@@ -858,7 +860,8 @@
      * same order as the key list.
      */
     public StatusBarNotification[] getActiveNotifications(String[] keys) {
-        return getActiveNotifications(keys, TRIM_FULL);
+        StatusBarNotification[] activeNotifications = getActiveNotifications(keys, TRIM_FULL);
+        return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
     }
 
     /**
@@ -890,6 +893,9 @@
 
     private StatusBarNotification[] cleanUpNotificationList(
             ParceledListSlice<StatusBarNotification> parceledList) {
+        if (parceledList == null || parceledList.getList() == null) {
+            return new StatusBarNotification[0];
+        }
         List<StatusBarNotification> list = parceledList.getList();
         ArrayList<StatusBarNotification> corruptNotifications = null;
         int N = list.size();
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index e3f4ad1..309fa4a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -95,6 +95,7 @@
     private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
     private static final boolean DEFAULT_ALLOW_SCREEN_OFF = false;
     private static final boolean DEFAULT_ALLOW_SCREEN_ON = false;
+    private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
     private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
             Policy.getAllSuppressedVisualEffects();
 
@@ -118,6 +119,8 @@
     private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
     private static final String DISALLOW_TAG = "disallow";
     private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
+    private static final String STATE_TAG = "state";
+    private static final String STATE_ATT_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd";
 
     private static final String CONDITION_ATT_ID = "id";
     private static final String CONDITION_ATT_SUMMARY = "summary";
@@ -154,6 +157,7 @@
     public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS;
     public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
     public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
+    public boolean areChannelsBypassingDnd = DEFAULT_CHANNELS_BYPASSING_DND;
     public int version;
 
     public ZenRule manualRule;
@@ -187,6 +191,7 @@
         allowMedia = source.readInt() == 1;
         allowSystem = source.readInt() == 1;
         suppressedVisualEffects = source.readInt();
+        areChannelsBypassingDnd = source.readInt() == 1;
     }
 
     @Override
@@ -220,6 +225,7 @@
         dest.writeInt(allowMedia ? 1 : 0);
         dest.writeInt(allowSystem ? 1 : 0);
         dest.writeInt(suppressedVisualEffects);
+        dest.writeInt(areChannelsBypassingDnd ? 1 : 0);
     }
 
     @Override
@@ -239,6 +245,7 @@
                 .append(",allowWhenScreenOff=").append(allowWhenScreenOff)
                 .append(",allowWhenScreenOn=").append(allowWhenScreenOn)
                 .append(",suppressedVisualEffects=").append(suppressedVisualEffects)
+                .append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd)
                 .append(",automaticRules=").append(automaticRules)
                 .append(",manualRule=").append(manualRule)
                 .append(']').toString();
@@ -303,6 +310,11 @@
             ZenRule.appendDiff(d, "automaticRule[" + rule + "]", fromRule, toRule);
         }
         ZenRule.appendDiff(d, "manualRule", manualRule, to.manualRule);
+
+        if (areChannelsBypassingDnd != to.areChannelsBypassingDnd) {
+            d.addLine("areChannelsBypassingDnd", areChannelsBypassingDnd,
+                    to.areChannelsBypassingDnd);
+        }
         return d;
     }
 
@@ -397,7 +409,8 @@
                 && other.user == user
                 && Objects.equals(other.automaticRules, automaticRules)
                 && Objects.equals(other.manualRule, manualRule)
-                && other.suppressedVisualEffects == suppressedVisualEffects;
+                && other.suppressedVisualEffects == suppressedVisualEffects
+                && other.areChannelsBypassingDnd == areChannelsBypassingDnd;
     }
 
     @Override
@@ -406,7 +419,7 @@
                 allowRepeatCallers, allowMessages,
                 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
                 allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule,
-                suppressedVisualEffects);
+                suppressedVisualEffects, areChannelsBypassingDnd);
     }
 
     private static String toDayList(int[] days) {
@@ -511,6 +524,9 @@
                         automaticRule.id = id;
                         rt.automaticRules.put(id, automaticRule);
                     }
+                } else if (STATE_TAG.equals(tag)) {
+                    rt.areChannelsBypassingDnd = safeBoolean(parser,
+                            STATE_ATT_CHANNELS_BYPASSING_DND, DEFAULT_CHANNELS_BYPASSING_DND);
                 }
             }
         }
@@ -561,6 +577,12 @@
             writeRuleXml(automaticRule, out);
             out.endTag(null, AUTOMATIC_TAG);
         }
+
+        out.startTag(null, STATE_TAG);
+        out.attribute(null, STATE_ATT_CHANNELS_BYPASSING_DND,
+                Boolean.toString(areChannelsBypassingDnd));
+        out.endTag(null, STATE_TAG);
+
         out.endTag(null, ZEN_TAG);
     }
 
@@ -743,7 +765,8 @@
         priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
         priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
         return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
-                suppressedVisualEffects);
+                suppressedVisualEffects, areChannelsBypassingDnd
+                ? Policy.STATE_CHANNELS_BYPASSING_DND : 0);
     }
 
     /**
@@ -795,6 +818,9 @@
         if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
             suppressedVisualEffects = policy.suppressedVisualEffects;
         }
+        if (policy.state != Policy.STATE_UNSET) {
+            areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
+        }
     }
 
     public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
@@ -1465,15 +1491,15 @@
                 & NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0;
         boolean allowRepeatCallers = (policy.priorityCategories
                 & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
+        boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
         return !allowReminders && !allowCalls && !allowMessages && !allowEvents
-                && !allowRepeatCallers;
+                && !allowRepeatCallers && !areChannelsBypassingDnd;
     }
 
     /**
      * Determines if DND is currently overriding the ringer
      */
     public static boolean isZenOverridingRinger(int zen, ZenModeConfig zenConfig) {
-        // TODO (beverlyt): check if apps can bypass dnd b/77729075
         return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
                 || zen == Global.ZEN_MODE_ALARMS
                 || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
@@ -1485,7 +1511,8 @@
      */
     public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(ZenModeConfig config) {
         return !config.allowReminders && !config.allowCalls && !config.allowMessages
-                && !config.allowEvents && !config.allowRepeatCallers;
+                && !config.allowEvents && !config.allowRepeatCallers
+                && !config.areChannelsBypassingDnd;
     }
 
     /**
diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java
index e60377b..549f8b3 100644
--- a/core/java/android/text/method/LinkMovementMethod.java
+++ b/core/java/android/text/method/LinkMovementMethod.java
@@ -25,6 +25,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.textclassifier.TextLinks.TextLinkSpan;
 import android.widget.TextView;
 
 /**
@@ -130,64 +131,70 @@
             selStart = selEnd = -1;
 
         switch (what) {
-        case CLICK:
-            if (selStart == selEnd) {
-                return false;
-            }
+            case CLICK:
+                if (selStart == selEnd) {
+                    return false;
+                }
 
-            ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
+                ClickableSpan[] links = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
 
-            if (link.length != 1)
-                return false;
+                if (links.length != 1) {
+                    return false;
+                }
 
-            link[0].onClick(widget);
-            break;
+                ClickableSpan link = links[0];
+                if (link instanceof TextLinkSpan) {
+                    ((TextLinkSpan) link).onClick(widget, TextLinkSpan.INVOCATION_METHOD_KEYBOARD);
+                } else {
+                    link.onClick(widget);
+                }
+                break;
 
-        case UP:
-            int bestStart, bestEnd;
+            case UP:
+                int bestStart, bestEnd;
 
-            bestStart = -1;
-            bestEnd = -1;
+                bestStart = -1;
+                bestEnd = -1;
 
-            for (int i = 0; i < candidates.length; i++) {
-                int end = buffer.getSpanEnd(candidates[i]);
+                for (int i = 0; i < candidates.length; i++) {
+                    int end = buffer.getSpanEnd(candidates[i]);
 
-                if (end < selEnd || selStart == selEnd) {
-                    if (end > bestEnd) {
-                        bestStart = buffer.getSpanStart(candidates[i]);
-                        bestEnd = end;
+                    if (end < selEnd || selStart == selEnd) {
+                        if (end > bestEnd) {
+                            bestStart = buffer.getSpanStart(candidates[i]);
+                            bestEnd = end;
+                        }
                     }
                 }
-            }
 
-            if (bestStart >= 0) {
-                Selection.setSelection(buffer, bestEnd, bestStart);
-                return true;
-            }
+                if (bestStart >= 0) {
+                    Selection.setSelection(buffer, bestEnd, bestStart);
+                    return true;
+                }
 
-            break;
+                break;
 
-        case DOWN:
-            bestStart = Integer.MAX_VALUE;
-            bestEnd = Integer.MAX_VALUE;
+            case DOWN:
+                bestStart = Integer.MAX_VALUE;
+                bestEnd = Integer.MAX_VALUE;
 
-            for (int i = 0; i < candidates.length; i++) {
-                int start = buffer.getSpanStart(candidates[i]);
+                for (int i = 0; i < candidates.length; i++) {
+                    int start = buffer.getSpanStart(candidates[i]);
 
-                if (start > selStart || selStart == selEnd) {
-                    if (start < bestStart) {
-                        bestStart = start;
-                        bestEnd = buffer.getSpanEnd(candidates[i]);
+                    if (start > selStart || selStart == selEnd) {
+                        if (start < bestStart) {
+                            bestStart = start;
+                            bestEnd = buffer.getSpanEnd(candidates[i]);
+                        }
                     }
                 }
-            }
 
-            if (bestEnd < Integer.MAX_VALUE) {
-                Selection.setSelection(buffer, bestStart, bestEnd);
-                return true;
-            }
+                if (bestEnd < Integer.MAX_VALUE) {
+                    Selection.setSelection(buffer, bestStart, bestEnd);
+                    return true;
+                }
 
-            break;
+                break;
         }
 
         return false;
@@ -215,8 +222,14 @@
             ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class);
 
             if (links.length != 0) {
+                ClickableSpan link = links[0];
                 if (action == MotionEvent.ACTION_UP) {
-                    links[0].onClick(widget);
+                    if (link instanceof TextLinkSpan) {
+                        ((TextLinkSpan) link).onClick(
+                                widget, TextLinkSpan.INVOCATION_METHOD_TOUCH);
+                    } else {
+                        link.onClick(widget);
+                    }
                 } else if (action == MotionEvent.ACTION_DOWN) {
                     if (widget.getContext().getApplicationInfo().targetSdkVersion
                             >= Build.VERSION_CODES.P) {
@@ -225,8 +238,8 @@
                         widget.hideFloatingToolbar(HIDE_FLOATING_TOOLBAR_DELAY_MS);
                     }
                     Selection.setSelection(buffer,
-                        buffer.getSpanStart(links[0]),
-                        buffer.getSpanEnd(links[0]));
+                            buffer.getSpanStart(link),
+                            buffer.getSpanEnd(link));
                 }
                 return true;
             } else {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 6486230..8395681 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -342,12 +342,6 @@
     int getDockedStackSide();
 
     /**
-     * Sets whether we are currently in a drag resize operation where we are changing the docked
-     * stack size.
-     */
-    void setDockedStackResizing(boolean resizing);
-
-    /**
      * Sets the region the user can touch the divider. This region will be excluded from the region
      * which is used to cause a focus switch when dispatching touch.
      */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 71b6084..6b16d42 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20700,7 +20700,7 @@
             if (canTakeFocus()) {
                 // We have a robust focus, so parents should no longer be wanting focus.
                 clearParentsWantFocus();
-            } else if (!getViewRootImpl().isInLayout()) {
+            } else if (getViewRootImpl() == null || !getViewRootImpl().isInLayout()) {
                 // This is a weird case. Most-likely the user, rather than ViewRootImpl, called
                 // layout. In this case, there's no guarantee that parent layouts will be evaluated
                 // and thus the safest action is to clear focus here.
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 96016b4..ad50dc0 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -277,12 +277,12 @@
      */
     @Nullable
     public static PendingIntent createPendingIntent(
-            @NonNull final Context context, @NonNull final Intent intent) {
+            @NonNull final Context context, @NonNull final Intent intent, int requestCode) {
         switch (getIntentType(intent, context)) {
             case IntentType.ACTIVITY:
-                return PendingIntent.getActivity(context, 0, intent, 0);
+                return PendingIntent.getActivity(context, requestCode, intent, 0);
             case IntentType.SERVICE:
-                return PendingIntent.getService(context, 0, intent, 0);
+                return PendingIntent.getService(context, requestCode, intent, 0);
             default:
                 return null;
         }
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 2213355..910fcaa 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -412,7 +412,7 @@
         boolean isPrimaryAction = true;
         for (LabeledIntent labeledIntent : IntentFactory.create(
                 mContext, referenceTime, highestScoringResult, classifiedText)) {
-            RemoteAction action = labeledIntent.asRemoteAction(mContext);
+            final RemoteAction action = labeledIntent.asRemoteAction(mContext);
             if (isPrimaryAction) {
                 // For O backwards compatibility, the first RemoteAction is also written to the
                 // legacy API fields.
@@ -421,7 +421,7 @@
                 builder.setIntent(labeledIntent.getIntent());
                 builder.setOnClickListener(TextClassification.createIntentOnClickListener(
                         TextClassification.createPendingIntent(mContext,
-                                labeledIntent.getIntent())));
+                                labeledIntent.getIntent(), labeledIntent.getRequestCode())));
                 isPrimaryAction = false;
             }
             builder.addAction(action);
@@ -559,14 +559,30 @@
      * Helper class to store the information from which RemoteActions are built.
      */
     private static final class LabeledIntent {
-        private String mTitle;
-        private String mDescription;
-        private Intent mIntent;
 
-        LabeledIntent(String title, String description, Intent intent) {
+        static final int DEFAULT_REQUEST_CODE = 0;
+
+        private final String mTitle;
+        private final String mDescription;
+        private final Intent mIntent;
+        private final int mRequestCode;
+
+        /**
+         * Initializes a LabeledIntent.
+         *
+         * <p>NOTE: {@code reqestCode} is required to not be {@link #DEFAULT_REQUEST_CODE}
+         * if distinguishing info (e.g. the classified text) is represented in intent extras only.
+         * In such circumstances, the request code should represent the distinguishing info
+         * (e.g. by generating a hashcode) so that the generated PendingIntent is (somewhat)
+         * unique. To be correct, the PendingIntent should be definitely unique but we try a
+         * best effort approach that avoids spamming the system with PendingIntents.
+         */
+        // TODO: Fix the issue mentioned above so the behaviour is correct.
+        LabeledIntent(String title, String description, Intent intent, int requestCode) {
             mTitle = title;
             mDescription = description;
             mIntent = intent;
+            mRequestCode = requestCode;
         }
 
         String getTitle() {
@@ -581,6 +597,10 @@
             return mIntent;
         }
 
+        int getRequestCode() {
+            return mRequestCode;
+        }
+
         RemoteAction asRemoteAction(Context context) {
             final PackageManager pm = context.getPackageManager();
             final ResolveInfo resolveInfo = pm.resolveActivity(mIntent, 0);
@@ -602,8 +622,8 @@
                 icon = Icon.createWithResource("android",
                         com.android.internal.R.drawable.ic_more_items);
             }
-            RemoteAction action = new RemoteAction(icon, mTitle, mDescription,
-                    TextClassification.createPendingIntent(context, mIntent));
+            final RemoteAction action = new RemoteAction(icon, mTitle, mDescription,
+                    TextClassification.createPendingIntent(context, mIntent, mRequestCode));
             action.setShouldShowIcon(shouldShowIcon);
             return action;
         }
@@ -659,13 +679,15 @@
                             context.getString(com.android.internal.R.string.email),
                             context.getString(com.android.internal.R.string.email_desc),
                             new Intent(Intent.ACTION_SENDTO)
-                                    .setData(Uri.parse(String.format("mailto:%s", text)))),
+                                    .setData(Uri.parse(String.format("mailto:%s", text))),
+                            LabeledIntent.DEFAULT_REQUEST_CODE),
                     new LabeledIntent(
                             context.getString(com.android.internal.R.string.add_contact),
                             context.getString(com.android.internal.R.string.add_contact_desc),
                             new Intent(Intent.ACTION_INSERT_OR_EDIT)
                                     .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                                    .putExtra(ContactsContract.Intents.Insert.EMAIL, text)));
+                                    .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
+                            text.hashCode()));
         }
 
         @NonNull
@@ -679,20 +701,23 @@
                         context.getString(com.android.internal.R.string.dial),
                         context.getString(com.android.internal.R.string.dial_desc),
                         new Intent(Intent.ACTION_DIAL).setData(
-                                Uri.parse(String.format("tel:%s", text)))));
+                                Uri.parse(String.format("tel:%s", text))),
+                        LabeledIntent.DEFAULT_REQUEST_CODE));
             }
             actions.add(new LabeledIntent(
                     context.getString(com.android.internal.R.string.add_contact),
                     context.getString(com.android.internal.R.string.add_contact_desc),
                     new Intent(Intent.ACTION_INSERT_OR_EDIT)
                             .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                            .putExtra(ContactsContract.Intents.Insert.PHONE, text)));
+                            .putExtra(ContactsContract.Intents.Insert.PHONE, text),
+                    text.hashCode()));
             if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
                 actions.add(new LabeledIntent(
                         context.getString(com.android.internal.R.string.sms),
                         context.getString(com.android.internal.R.string.sms_desc),
                         new Intent(Intent.ACTION_SENDTO)
-                                .setData(Uri.parse(String.format("smsto:%s", text)))));
+                                .setData(Uri.parse(String.format("smsto:%s", text))),
+                        LabeledIntent.DEFAULT_REQUEST_CODE));
             }
             return actions;
         }
@@ -706,7 +731,8 @@
                         context.getString(com.android.internal.R.string.map),
                         context.getString(com.android.internal.R.string.map_desc),
                         new Intent(Intent.ACTION_VIEW)
-                                .setData(Uri.parse(String.format("geo:0,0?q=%s", encText)))));
+                                .setData(Uri.parse(String.format("geo:0,0?q=%s", encText))),
+                        LabeledIntent.DEFAULT_REQUEST_CODE));
             } catch (UnsupportedEncodingException e) {
                 Log.e(LOG_TAG, "Could not encode address", e);
             }
@@ -728,7 +754,8 @@
                     context.getString(com.android.internal.R.string.browse),
                     context.getString(com.android.internal.R.string.browse_desc),
                     new Intent(Intent.ACTION_VIEW, Uri.parse(text))
-                            .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())));
+                            .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
+                    LabeledIntent.DEFAULT_REQUEST_CODE));
         }
 
         @NonNull
@@ -754,7 +781,8 @@
                     context.getString(com.android.internal.R.string.view_flight),
                     context.getString(com.android.internal.R.string.view_flight_desc),
                     new Intent(Intent.ACTION_WEB_SEARCH)
-                            .putExtra(SearchManager.QUERY, text)));
+                            .putExtra(SearchManager.QUERY, text),
+                    text.hashCode()));
         }
 
         @NonNull
@@ -765,7 +793,8 @@
             return new LabeledIntent(
                     context.getString(com.android.internal.R.string.view_calendar),
                     context.getString(com.android.internal.R.string.view_calendar_desc),
-                    new Intent(Intent.ACTION_VIEW).setData(builder.build()));
+                    new Intent(Intent.ACTION_VIEW).setData(builder.build()),
+                    LabeledIntent.DEFAULT_REQUEST_CODE);
         }
 
         @NonNull
@@ -781,7 +810,8 @@
                             .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
                                     parsedTime.toEpochMilli())
                             .putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
-                                    parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION));
+                                    parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
+                    parsedTime.hashCode());
         }
     }
 }
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 851b2c9..e7faf14 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -503,6 +503,22 @@
      */
     public static class TextLinkSpan extends ClickableSpan {
 
+        /**
+         * How the clickspan is triggered.
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({INVOCATION_METHOD_UNSPECIFIED, INVOCATION_METHOD_TOUCH,
+                INVOCATION_METHOD_KEYBOARD})
+        public @interface InvocationMethod {}
+
+        /** @hide */
+        public static final int INVOCATION_METHOD_UNSPECIFIED = -1;
+        /** @hide */
+        public static final int INVOCATION_METHOD_TOUCH = 0;
+        /** @hide */
+        public static final int INVOCATION_METHOD_KEYBOARD = 1;
+
         private final TextLink mTextLink;
 
         public TextLinkSpan(@NonNull TextLink textLink) {
@@ -511,16 +527,24 @@
 
         @Override
         public void onClick(View widget) {
+            onClick(widget, INVOCATION_METHOD_UNSPECIFIED);
+        }
+
+        /** @hide */
+        public final void onClick(View widget, @InvocationMethod int invocationMethod) {
             if (widget instanceof TextView) {
                 final TextView textView = (TextView) widget;
                 final Context context = textView.getContext();
                 if (TextClassificationManager.getSettings(context).isSmartLinkifyEnabled()) {
-                    if (textView.requestFocus()) {
-                        textView.requestActionMode(this);
-                    } else {
-                        // If textView can not take focus, then simply handle the click as it will
-                        // be difficult to get rid of the floating action mode.
-                        textView.handleClick(this);
+                    switch (invocationMethod) {
+                        case INVOCATION_METHOD_TOUCH:
+                            textView.requestActionMode(this);
+                            break;
+                        case INVOCATION_METHOD_KEYBOARD:// fall though
+                        case INVOCATION_METHOD_UNSPECIFIED:  // fall through
+                        default:
+                            textView.handleClick(this);
+                            break;
                     }
                 } else {
                     if (mTextLink.mUrlSpan != null) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index dac100a..f6ac1cc 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4063,7 +4063,8 @@
                 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
                 mAssistClickHandlers.put(item, TextClassification.createIntentOnClickListener(
                         TextClassification.createPendingIntent(mTextView.getContext(),
-                                textClassification.getIntent())));
+                                textClassification.getIntent(),
+                                createAssistMenuItemPendingIntentRequestCode())));
             }
             final int count = textClassification.getActions().size();
             for (int i = 1; i < count; i++) {
@@ -4121,7 +4122,9 @@
                 final Intent intent = assistMenuItem.getIntent();
                 if (intent != null) {
                     onClickListener = TextClassification.createIntentOnClickListener(
-                            TextClassification.createPendingIntent(mTextView.getContext(), intent));
+                            TextClassification.createPendingIntent(
+                                    mTextView.getContext(), intent,
+                                    createAssistMenuItemPendingIntentRequestCode()));
                 }
             }
             if (onClickListener != null) {
@@ -4132,6 +4135,14 @@
             return true;
         }
 
+        private int createAssistMenuItemPendingIntentRequestCode() {
+            return mTextView.hasSelection()
+                    ? mTextView.getText().subSequence(
+                            mTextView.getSelectionStart(), mTextView.getSelectionEnd())
+                            .hashCode()
+                    : 0;
+        }
+
         private boolean shouldEnableAssistMenuItems() {
             return mTextView.isDeviceProvisioned()
                 && TextClassificationManager.getSettings(mTextView.getContext())
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a34bd09..b6bd14e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -83,6 +83,7 @@
 import java.util.Objects;
 import java.util.Stack;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * A class that describes a view hierarchy that can be displayed in
@@ -444,6 +445,10 @@
             return true;
         }
 
+        public void visitUris(@NonNull Consumer<Uri> visitor) {
+            // Nothing to visit by default
+        }
+
         int viewId;
     }
 
@@ -517,6 +522,27 @@
         setBitmapCache(mBitmapCache);
     }
 
+    /**
+     * Note all {@link Uri} that are referenced internally, with the expectation
+     * that Uri permission grants will need to be issued to ensure the recipient
+     * of this object is able to render its contents.
+     *
+     * @hide
+     */
+    public void visitUris(@NonNull Consumer<Uri> visitor) {
+        if (mActions != null) {
+            for (int i = 0; i < mActions.size(); i++) {
+                mActions.get(i).visitUris(visitor);
+            }
+        }
+    }
+
+    private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
+        if (icon != null && icon.getType() == Icon.TYPE_URI) {
+            visitor.accept(icon.getUri());
+        }
+    }
+
     private static class RemoteViewsContextWrapper extends ContextWrapper {
         private final Context mContextForResources;
 
@@ -1485,6 +1511,20 @@
         public boolean prefersAsyncApply() {
             return this.type == URI || this.type == ICON;
         }
+
+        @Override
+        public void visitUris(@NonNull Consumer<Uri> visitor) {
+            switch (this.type) {
+                case URI:
+                    final Uri uri = (Uri) this.value;
+                    visitor.accept(uri);
+                    break;
+                case ICON:
+                    final Icon icon = (Icon) this.value;
+                    visitIconUri(icon, visitor);
+                    break;
+            }
+        }
     }
 
     /**
@@ -1849,6 +1889,16 @@
             return TEXT_VIEW_DRAWABLE_ACTION_TAG;
         }
 
+        @Override
+        public void visitUris(@NonNull Consumer<Uri> visitor) {
+            if (useIcons) {
+                visitIconUri(i1, visitor);
+                visitIconUri(i2, visitor);
+                visitIconUri(i3, visitor);
+                visitIconUri(i4, visitor);
+            }
+        }
+
         boolean isRelative = false;
         boolean useIcons = false;
         int d1, d2, d3, d4;
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 433d14f..083c0c9 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -29,6 +29,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import java.util.stream.Stream;
 
 /**
@@ -84,6 +85,17 @@
         return emptyIfNull(result);
     }
 
+    /** Add all elements matching {@code predicate} in {@code source} to {@code dest}. */
+    public static <T> void addIf(@Nullable List<T> source, @NonNull Collection<? super T> dest,
+            @Nullable Predicate<? super T> predicate) {
+        for (int i = 0; i < size(source); i++) {
+            final T item = source.get(i);
+            if (predicate.test(item)) {
+                dest.add(item);
+            }
+        }
+    }
+
     /**
      * Returns a list of items resulting from applying the given function to each element of the
      * provided list.
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index e85b782..7fd83bc 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -16,18 +16,25 @@
 
 package com.android.internal.util;
 
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Handler;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.Objects;
+import java.util.function.Predicate;
 
 /**
  * Helper functions for dumping the state of system services.
+ * Test:
+ atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
  */
 public final class DumpUtils {
     private static final String TAG = "DumpUtils";
@@ -153,4 +160,99 @@
             PrintWriter pw) {
         return checkDumpPermission(context, tag, pw) && checkUsageStatsPermission(context, tag, pw);
     }
+
+    /**
+     * Return whether a package name is considered to be part of the platform.
+     * @hide
+     */
+    public static boolean isPlatformPackage(@Nullable String packageName) {
+        return (packageName != null)
+                && (packageName.equals("android")
+                    || packageName.startsWith("android.")
+                    || packageName.startsWith("com.android."));
+    }
+
+    /**
+     * Return whether a package name is considered to be part of the platform.
+     * @hide
+     */
+    public static boolean isPlatformPackage(@Nullable ComponentName cname) {
+        return (cname != null) && isPlatformPackage(cname.getPackageName());
+    }
+
+    /**
+     * Return whether a package name is considered to be part of the platform.
+     * @hide
+     */
+    public static boolean isPlatformPackage(@Nullable ComponentName.WithComponentName wcn) {
+        return (wcn != null) && isPlatformPackage(wcn.getComponentName());
+    }
+
+    /**
+     * Return whether a package name is NOT considered to be part of the platform.
+     * @hide
+     */
+    public static boolean isNonPlatformPackage(@Nullable String packageName) {
+        return (packageName != null) && !isPlatformPackage(packageName);
+    }
+
+    /**
+     * Return whether a package name is NOT considered to be part of the platform.
+     * @hide
+     */
+    public static boolean isNonPlatformPackage(@Nullable ComponentName cname) {
+        return (cname != null) && isNonPlatformPackage(cname.getPackageName());
+    }
+
+    /**
+     * Return whether a package name is NOT considered to be part of the platform.
+     * @hide
+     */
+    public static boolean isNonPlatformPackage(@Nullable ComponentName.WithComponentName wcn) {
+        return (wcn != null) && !isPlatformPackage(wcn.getComponentName());
+    }
+
+    /**
+     * Used for dumping providers and services. Return a predicate for a given filter string.
+     * @hide
+     */
+    public static <TRec extends ComponentName.WithComponentName> Predicate<TRec> filterRecord(
+            @Nullable String filterString) {
+
+        if (TextUtils.isEmpty(filterString)) {
+            return rec -> false;
+        }
+
+        // Dump all?
+        if ("all".equals(filterString)) {
+            return Objects::nonNull;
+        }
+
+        // Dump all platform?
+        if ("all-platform".equals(filterString)) {
+            return DumpUtils::isPlatformPackage;
+        }
+
+        // Dump all non-platform?
+        if ("all-non-platform".equals(filterString)) {
+            return DumpUtils::isNonPlatformPackage;
+        }
+
+        // Is the filter a component name? If so, do an exact match.
+        final ComponentName filterCname = ComponentName.unflattenFromString(filterString);
+        if (filterCname != null) {
+            // Do exact component name check.
+            return rec -> (rec != null) && filterCname.equals(rec.getComponentName());
+        }
+
+        // Otherwise, do a partial match against the component name.
+        // Also if the filter is a hex-decimal string, do the object ID match too.
+        final int id = ParseUtils.parseIntWithBase(filterString, 16, -1);
+        return rec -> {
+            final ComponentName cn = rec.getComponentName();
+            return ((id != -1) && (System.identityHashCode(rec) == id))
+                    || cn.flattenToString().toLowerCase().contains(filterString.toLowerCase());
+        };
+    }
 }
+
diff --git a/core/java/com/android/internal/util/ParseUtils.java b/core/java/com/android/internal/util/ParseUtils.java
new file mode 100644
index 0000000..a591f4a
--- /dev/null
+++ b/core/java/com/android/internal/util/ParseUtils.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 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.internal.util;
+
+import android.annotation.Nullable;
+
+/**
+ * Various numeric -> strings conversion.
+ *
+ * Test:
+ atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
+ */
+public final class ParseUtils {
+    private ParseUtils() {
+    }
+
+    /** Parse a value as a base-10 integer. */
+    public static int parseInt(@Nullable String value, int defValue) {
+        return parseIntWithBase(value, 10, defValue);
+    }
+
+    /** Parse a value as an integer of a given base. */
+    public static int parseIntWithBase(@Nullable String value, int base, int defValue) {
+        if (value == null) {
+            return defValue;
+        }
+        try {
+            return Integer.parseInt(value, base);
+        } catch (NumberFormatException e) {
+            return defValue;
+        }
+    }
+
+    /** Parse a value as a base-10 long. */
+    public static long parseLong(@Nullable String value, long defValue) {
+        return parseLongWithBase(value, 10, defValue);
+    }
+
+    /** Parse a value as a long of a given base. */
+    public static long parseLongWithBase(@Nullable String value, int base, long defValue) {
+        if (value == null) {
+            return defValue;
+        }
+        try {
+            return Long.parseLong(value, base);
+        } catch (NumberFormatException e) {
+            return defValue;
+        }
+    }
+
+    /** Parse a value as a float. */
+    public static float parseFloat(@Nullable String value, float defValue) {
+        if (value == null) {
+            return defValue;
+        }
+        try {
+            return Float.parseFloat(value);
+        } catch (NumberFormatException e) {
+            return defValue;
+        }
+    }
+
+    /** Parse a value as a double. */
+    public static double parseDouble(@Nullable String value, double defValue) {
+        if (value == null) {
+            return defValue;
+        }
+        try {
+            return Double.parseDouble(value);
+        } catch (NumberFormatException e) {
+            return defValue;
+        }
+    }
+
+    /** Parse a value as a boolean. */
+    public static boolean parseBoolean(@Nullable String value, boolean defValue) {
+        if ("true".equals(value)) {
+            return true;
+        }
+        if ("false".equals(value)) {
+            return false;
+        }
+        return parseInt(value, defValue ? 1 : 0) != 0;
+    }
+}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 2ce5a0b..63c2e96 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1176,6 +1176,9 @@
                 final boolean showIcon = isFirstItem && menuItem.getItemId() == R.id.textAssist;
                 final View menuItemButton = createMenuItemButton(
                         mContext, menuItem, mIconTextSpacing, showIcon);
+                if (!showIcon && menuItemButton instanceof LinearLayout) {
+                    ((LinearLayout) menuItemButton).setGravity(Gravity.CENTER);
+                }
 
                 // Adding additional start padding for the first button to even out button spacing.
                 if (isFirstItem) {
@@ -1200,57 +1203,21 @@
                 final int menuItemButtonWidth = Math.min(
                         menuItemButton.getMeasuredWidth(), toolbarWidth);
 
-                final boolean isNewGroup = !isFirstItem && lastGroupId != menuItem.getGroupId();
-                final int extraPadding = isNewGroup ? menuItemButton.getPaddingEnd() * 2 : 0;
-
                 // Check if we can fit an item while reserving space for the overflowButton.
                 final boolean canFitWithOverflow =
                         menuItemButtonWidth <=
-                                availableWidth - mOverflowButtonSize.getWidth() - extraPadding;
+                                availableWidth - mOverflowButtonSize.getWidth();
                 final boolean canFitNoOverflow =
-                        isLastItem && menuItemButtonWidth <= availableWidth - extraPadding;
+                        isLastItem && menuItemButtonWidth <= availableWidth;
                 if (canFitWithOverflow || canFitNoOverflow) {
-                    if (isNewGroup) {
-                        final View divider = createDivider(mContext);
-                        final int dividerWidth = divider.getLayoutParams().width;
-
-                        // Add extra padding to the end of the previous button.
-                        // Half of the extra padding (less borderWidth) goes to the previous button.
-                        final View previousButton = mMainPanel.getChildAt(
-                                mMainPanel.getChildCount() - 1);
-                        final int prevPaddingEnd = previousButton.getPaddingEnd()
-                                + extraPadding / 2 - dividerWidth;
-                        previousButton.setPaddingRelative(
-                                previousButton.getPaddingStart(),
-                                previousButton.getPaddingTop(),
-                                prevPaddingEnd,
-                                previousButton.getPaddingBottom());
-                        final ViewGroup.LayoutParams prevParams = previousButton.getLayoutParams();
-                        prevParams.width += extraPadding / 2 - dividerWidth;
-                        previousButton.setLayoutParams(prevParams);
-
-                        // Add extra padding to the start of this button.
-                        // Other half of the extra padding goes to this button.
-                        final int paddingStart = menuItemButton.getPaddingStart()
-                                + extraPadding / 2;
-                        menuItemButton.setPaddingRelative(
-                                paddingStart,
-                                menuItemButton.getPaddingTop(),
-                                menuItemButton.getPaddingEnd(),
-                                menuItemButton.getPaddingBottom());
-
-                        // Include a divider.
-                        mMainPanel.addView(divider);
-                    }
-
                     setButtonTagAndClickListener(menuItemButton, menuItem);
                     // Set tooltips for main panel items, but not overflow items (b/35726766).
                     menuItemButton.setTooltipText(menuItem.getTooltipText());
                     mMainPanel.addView(menuItemButton);
                     final ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
-                    params.width = menuItemButtonWidth + extraPadding / 2;
+                    params.width = menuItemButtonWidth;
                     menuItemButton.setLayoutParams(params);
-                    availableWidth -= menuItemButtonWidth + extraPadding;
+                    availableWidth -= menuItemButtonWidth;
                     remainingMenuItems.pop();
                 } else {
                     break;
@@ -1726,30 +1693,6 @@
         return popupWindow;
     }
 
-    private static View createDivider(Context context) {
-        // TODO: Inflate this instead.
-        View divider = new View(context);
-
-        int _1dp = (int) TypedValue.applyDimension(
-                TypedValue.COMPLEX_UNIT_DIP, 1, context.getResources().getDisplayMetrics());
-        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
-                _1dp, ViewGroup.LayoutParams.MATCH_PARENT);
-        params.setMarginsRelative(0, _1dp * 10, 0, _1dp * 10);
-        divider.setLayoutParams(params);
-
-        TypedArray a = context.obtainStyledAttributes(
-                new TypedValue().data, new int[] { R.attr.floatingToolbarDividerColor });
-        divider.setBackgroundColor(a.getColor(0, 0));
-        a.recycle();
-
-        divider.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-        divider.setEnabled(false);
-        divider.setFocusable(false);
-        divider.setContentDescription(null);
-
-        return divider;
-    }
-
     /**
      * Creates an "appear" animation for the specified view.
      *
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc030ca..909efea 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -37,9 +37,6 @@
         <item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_tty</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item>
@@ -49,10 +46,13 @@
         <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2aa40fd..a135b28 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -536,7 +536,7 @@
     <dimen name="floating_toolbar_menu_image_width">24dp</dimen>
     <dimen name="floating_toolbar_menu_image_button_width">56dp</dimen>
     <dimen name="floating_toolbar_menu_image_button_vertical_padding">12dp</dimen>
-    <dimen name="floating_toolbar_menu_button_side_padding">11dp</dimen>
+    <dimen name="floating_toolbar_menu_button_side_padding">8dp</dimen>
     <dimen name="floating_toolbar_overflow_image_button_width">60dp</dimen>
     <dimen name="floating_toolbar_overflow_side_padding">18dp</dimen>
     <dimen name="floating_toolbar_text_size">14sp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0de2cb0..590f988 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1623,6 +1623,8 @@
   <java-symbol type="anim" name="task_open_enter_cross_profile_apps" />
   <java-symbol type="anim" name="activity_translucent_open_enter" />
   <java-symbol type="anim" name="activity_translucent_close_exit" />
+  <java-symbol type="anim" name="activity_open_enter" />
+  <java-symbol type="anim" name="activity_close_exit" />
 
   <java-symbol type="array" name="config_autoRotationTiltTolerance" />
   <java-symbol type="array" name="config_keyboardTapVibePattern" />
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index dce8a65..3a71851 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -19,8 +19,12 @@
 
 <!-- Default configuration for zen mode.  See android.service.notification.ZenModeConfig. -->
 <zen version="7">
-    <allow alarms="true" media="true" system="false" calls="false" messages="false" reminders="false"
-           events="false" />
+    <allow alarms="true" media="true" system="false" calls="false" messages="false"
+           reminders="false" events="false" />
+
     <!-- all visual effects that exist as of P -->
     <disallow suppressedVisualEffect="511" />
+
+    <!-- whether there are notification channels that can bypass dnd -->
+    <state areChannelsBypassingDnd="false" />
 </zen>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
new file mode 100644
index 0000000..59b4665
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test cases for {@link BluetoothCodecConfig}.
+ * <p>
+ * To run this test, use:
+ * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
+ */
+public class BluetoothCodecConfigTest extends TestCase {
+    private static final int[] kCodecTypeArray = new int[] {
+        BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+        BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+        BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+        BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+        BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+        BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX,
+        BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID,
+    };
+    private static final int[] kCodecPriorityArray = new int[] {
+        BluetoothCodecConfig.CODEC_PRIORITY_DISABLED,
+        BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+        BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
+    };
+    private static final int[] kSampleRateArray = new int[] {
+        BluetoothCodecConfig.SAMPLE_RATE_NONE,
+        BluetoothCodecConfig.SAMPLE_RATE_44100,
+        BluetoothCodecConfig.SAMPLE_RATE_48000,
+        BluetoothCodecConfig.SAMPLE_RATE_88200,
+        BluetoothCodecConfig.SAMPLE_RATE_96000,
+        BluetoothCodecConfig.SAMPLE_RATE_176400,
+        BluetoothCodecConfig.SAMPLE_RATE_192000,
+    };
+    private static final int[] kBitsPerSampleArray = new int[] {
+        BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
+        BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+        BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+        BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+    };
+    private static final int[] kChannelModeArray = new int[] {
+        BluetoothCodecConfig.CHANNEL_MODE_NONE,
+        BluetoothCodecConfig.CHANNEL_MODE_MONO,
+        BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+    };
+    private static final long[] kCodecSpecific1Array = new long[] { 1000, 1001, 1002, 1003, };
+    private static final long[] kCodecSpecific2Array = new long[] { 2000, 2001, 2002, 2003, };
+    private static final long[] kCodecSpecific3Array = new long[] { 3000, 3001, 3002, 3003, };
+    private static final long[] kCodecSpecific4Array = new long[] { 4000, 4001, 4002, 4003, };
+
+    private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length *
+        kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length *
+        kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length *
+        kCodecSpecific4Array.length;
+
+    private int selectCodecType(int configId) {
+        int left = kCodecTypeArray.length;
+        int right = kTotalConfigs / left;
+        int index = configId / right;
+        index = index % kCodecTypeArray.length;
+        return kCodecTypeArray[index];
+    }
+
+    private int selectCodecPriority(int configId) {
+        int left = kCodecTypeArray.length * kCodecPriorityArray.length;
+        int right = kTotalConfigs / left;
+        int index = configId / right;
+        index = index % kCodecPriorityArray.length;
+        return kCodecPriorityArray[index];
+    }
+
+    private int selectSampleRate(int configId) {
+        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length;
+        int right = kTotalConfigs / left;
+        int index = configId / right;
+        index = index % kSampleRateArray.length;
+        return kSampleRateArray[index];
+    }
+
+    private int selectBitsPerSample(int configId) {
+        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+            kBitsPerSampleArray.length;
+        int right = kTotalConfigs / left;
+        int index = configId / right;
+        index = index % kBitsPerSampleArray.length;
+        return kBitsPerSampleArray[index];
+    }
+
+    private int selectChannelMode(int configId) {
+        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+            kBitsPerSampleArray.length * kChannelModeArray.length;
+        int right = kTotalConfigs / left;
+        int index = configId / right;
+        index = index % kChannelModeArray.length;
+        return kChannelModeArray[index];
+    }
+
+    private long selectCodecSpecific1(int configId) {
+        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length;
+        int right = kTotalConfigs / left;
+        int index = configId / right;
+        index = index % kCodecSpecific1Array.length;
+        return kCodecSpecific1Array[index];
+    }
+
+    private long selectCodecSpecific2(int configId) {
+        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
+            kCodecSpecific2Array.length;
+        int right = kTotalConfigs / left;
+        int index = configId / right;
+        index = index % kCodecSpecific2Array.length;
+        return kCodecSpecific2Array[index];
+    }
+
+    private long selectCodecSpecific3(int configId) {
+        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
+            kCodecSpecific2Array.length * kCodecSpecific3Array.length;
+        int right = kTotalConfigs / left;
+        int index = configId / right;
+        index = index % kCodecSpecific3Array.length;
+        return kCodecSpecific3Array[index];
+    }
+
+    private long selectCodecSpecific4(int configId) {
+        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
+            kCodecSpecific2Array.length * kCodecSpecific3Array.length *
+            kCodecSpecific4Array.length;
+        int right = kTotalConfigs / left;
+        int index = configId / right;
+        index = index % kCodecSpecific4Array.length;
+        return kCodecSpecific4Array[index];
+    }
+
+    @SmallTest
+    public void testBluetoothCodecConfig_valid_get_methods() {
+
+        for (int config_id = 0; config_id < kTotalConfigs; config_id++) {
+            int codec_type = selectCodecType(config_id);
+            int codec_priority = selectCodecPriority(config_id);
+            int sample_rate = selectSampleRate(config_id);
+            int bits_per_sample = selectBitsPerSample(config_id);
+            int channel_mode = selectChannelMode(config_id);
+            long codec_specific1 = selectCodecSpecific1(config_id);
+            long codec_specific2 = selectCodecSpecific2(config_id);
+            long codec_specific3 = selectCodecSpecific3(config_id);
+            long codec_specific4 = selectCodecSpecific4(config_id);
+
+            BluetoothCodecConfig bcc = new BluetoothCodecConfig(codec_type, codec_priority,
+                                                                sample_rate, bits_per_sample,
+                                                                channel_mode, codec_specific1,
+                                                                codec_specific2, codec_specific3,
+                                                                codec_specific4);
+            if (sample_rate == BluetoothCodecConfig.SAMPLE_RATE_NONE) {
+                assertFalse(bcc.isValid());
+            } else if (bits_per_sample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
+                assertFalse(bcc.isValid());
+            } else if (channel_mode == BluetoothCodecConfig.CHANNEL_MODE_NONE) {
+                assertFalse(bcc.isValid());
+            } else {
+                assertTrue(bcc.isValid());
+            }
+
+            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) {
+                assertTrue(bcc.isMandatoryCodec());
+            } else {
+                assertFalse(bcc.isMandatoryCodec());
+            }
+
+            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) {
+                assertEquals("SBC", bcc.getCodecName());
+            }
+            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC) {
+                assertEquals("AAC", bcc.getCodecName());
+            }
+            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX) {
+                assertEquals("aptX", bcc.getCodecName());
+            }
+            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD) {
+                assertEquals("aptX HD", bcc.getCodecName());
+            }
+            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) {
+                assertEquals("LDAC", bcc.getCodecName());
+            }
+            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX) {
+                assertEquals("UNKNOWN CODEC(" + BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX + ")",
+                             bcc.getCodecName());
+            }
+            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
+                assertEquals("INVALID CODEC", bcc.getCodecName());
+            }
+
+            assertEquals(codec_type, bcc.getCodecType());
+            assertEquals(codec_priority, bcc.getCodecPriority());
+            assertEquals(sample_rate, bcc.getSampleRate());
+            assertEquals(bits_per_sample, bcc.getBitsPerSample());
+            assertEquals(channel_mode, bcc.getChannelMode());
+            assertEquals(codec_specific1, bcc.getCodecSpecific1());
+            assertEquals(codec_specific2, bcc.getCodecSpecific2());
+            assertEquals(codec_specific3, bcc.getCodecSpecific3());
+            assertEquals(codec_specific4, bcc.getCodecSpecific4());
+        }
+    }
+
+    @SmallTest
+    public void testBluetoothCodecConfig_equals() {
+        BluetoothCodecConfig bcc1 =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     1000, 2000, 3000, 4000);
+
+        BluetoothCodecConfig bcc2_same =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     1000, 2000, 3000, 4000);
+        assertTrue(bcc1.equals(bcc2_same));
+
+        BluetoothCodecConfig bcc3_codec_type =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     1000, 2000, 3000, 4000);
+        assertFalse(bcc1.equals(bcc3_codec_type));
+
+        BluetoothCodecConfig bcc4_codec_priority =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     1000, 2000, 3000, 4000);
+        assertFalse(bcc1.equals(bcc4_codec_priority));
+
+        BluetoothCodecConfig bcc5_sample_rate =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     1000, 2000, 3000, 4000);
+        assertFalse(bcc1.equals(bcc5_sample_rate));
+
+        BluetoothCodecConfig bcc6_bits_per_sample =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     1000, 2000, 3000, 4000);
+        assertFalse(bcc1.equals(bcc6_bits_per_sample));
+
+        BluetoothCodecConfig bcc7_channel_mode =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                     1000, 2000, 3000, 4000);
+        assertFalse(bcc1.equals(bcc7_channel_mode));
+
+        BluetoothCodecConfig bcc8_codec_specific1 =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     1001, 2000, 3000, 4000);
+        assertFalse(bcc1.equals(bcc8_codec_specific1));
+
+        BluetoothCodecConfig bcc9_codec_specific2 =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     1000, 2002, 3000, 4000);
+        assertFalse(bcc1.equals(bcc9_codec_specific2));
+
+        BluetoothCodecConfig bcc10_codec_specific3 =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     1000, 2000, 3003, 4000);
+        assertFalse(bcc1.equals(bcc10_codec_specific3));
+
+        BluetoothCodecConfig bcc11_codec_specific4 =
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     1000, 2000, 3000, 4004);
+        assertFalse(bcc1.equals(bcc11_codec_specific4));
+    }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
new file mode 100644
index 0000000..83bf2ed
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test cases for {@link BluetoothCodecStatus}.
+ * <p>
+ * To run this test, use:
+ * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
+ */
+public class BluetoothCodecStatusTest extends TestCase {
+
+    // Codec configs: A and B are same; C is different
+    private static final BluetoothCodecConfig config_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig config_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig config_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    // Local capabilities: A and B are same; C is different
+    private static final BluetoothCodecConfig local_capability1_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability1_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability1_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+
+    private static final BluetoothCodecConfig local_capability2_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability2_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability2_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability3_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability3_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability3_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability4_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability4_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability4_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability5_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability5_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig local_capability5_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+
+    // Selectable capabilities: A and B are same; C is different
+    private static final BluetoothCodecConfig selectable_capability1_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability1_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability1_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability2_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability2_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability2_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability3_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability3_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability3_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability4_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability4_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability4_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability5_A =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability5_B =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig selectable_capability5_C =
+        new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
+                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                 1000, 2000, 3000, 4000);
+
+    private static final BluetoothCodecConfig[] local_capability_A = {
+        local_capability1_A,
+        local_capability2_A,
+        local_capability3_A,
+        local_capability4_A,
+        local_capability5_A,
+    };
+
+    private static final BluetoothCodecConfig[] local_capability_B = {
+        local_capability1_B,
+        local_capability2_B,
+        local_capability3_B,
+        local_capability4_B,
+        local_capability5_B,
+    };
+
+    private static final BluetoothCodecConfig[] local_capability_B_reordered = {
+        local_capability5_B,
+        local_capability4_B,
+        local_capability2_B,
+        local_capability3_B,
+        local_capability1_B,
+    };
+
+    private static final BluetoothCodecConfig[] local_capability_C = {
+        local_capability1_C,
+        local_capability2_C,
+        local_capability3_C,
+        local_capability4_C,
+        local_capability5_C,
+    };
+
+    private static final BluetoothCodecConfig[] selectable_capability_A = {
+        selectable_capability1_A,
+        selectable_capability2_A,
+        selectable_capability3_A,
+        selectable_capability4_A,
+        selectable_capability5_A,
+    };
+
+    private static final BluetoothCodecConfig[] selectable_capability_B = {
+        selectable_capability1_B,
+        selectable_capability2_B,
+        selectable_capability3_B,
+        selectable_capability4_B,
+        selectable_capability5_B,
+    };
+
+    private static final BluetoothCodecConfig[] selectable_capability_B_reordered = {
+        selectable_capability5_B,
+        selectable_capability4_B,
+        selectable_capability2_B,
+        selectable_capability3_B,
+        selectable_capability1_B,
+    };
+
+    private static final BluetoothCodecConfig[] selectable_capability_C = {
+        selectable_capability1_C,
+        selectable_capability2_C,
+        selectable_capability3_C,
+        selectable_capability4_C,
+        selectable_capability5_C,
+    };
+
+    private static final BluetoothCodecStatus bcs_A =
+        new BluetoothCodecStatus(config_A, local_capability_A, selectable_capability_A);
+    private static final BluetoothCodecStatus bcs_B =
+        new BluetoothCodecStatus(config_B, local_capability_B, selectable_capability_B);
+    private static final BluetoothCodecStatus bcs_B_reordered =
+        new BluetoothCodecStatus(config_B, local_capability_B_reordered,
+                                 selectable_capability_B_reordered);
+    private static final BluetoothCodecStatus bcs_C =
+        new BluetoothCodecStatus(config_C, local_capability_C, selectable_capability_C);
+
+    @SmallTest
+    public void testBluetoothCodecStatus_get_methods() {
+
+        assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_A));
+        assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_B));
+        assertFalse(Objects.equals(bcs_A.getCodecConfig(), config_C));
+
+        assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_A));
+        assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_B));
+        assertFalse(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_C));
+
+        assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(),
+                                 selectable_capability_A));
+        assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(),
+                                  selectable_capability_B));
+        assertFalse(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(),
+                                  selectable_capability_C));
+    }
+
+    @SmallTest
+    public void testBluetoothCodecStatus_equals() {
+        assertTrue(bcs_A.equals(bcs_B));
+        assertTrue(bcs_B.equals(bcs_A));
+        assertTrue(bcs_A.equals(bcs_B_reordered));
+        assertTrue(bcs_B_reordered.equals(bcs_A));
+        assertFalse(bcs_A.equals(bcs_C));
+        assertFalse(bcs_C.equals(bcs_A));
+    }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 3eefc36..fe58116 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -40,7 +40,10 @@
 
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
+import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -50,7 +53,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.InOrder;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -231,12 +233,12 @@
 
     @Test
     public void testActivityResultRequiredStateResolution() {
-        ActivityResultItem activityResultItem = ActivityResultItem.obtain(new ArrayList<>());
+        PostExecItem postExecItem = new PostExecItem(ON_RESUME);
 
         IBinder token = mock(IBinder.class);
         ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
                 token /* activityToken */);
-        transaction.addCallback(activityResultItem);
+        transaction.addCallback(postExecItem);
 
         // Verify resolution that should get to onPause
         mClientRecord.setState(ON_RESUME);
@@ -395,4 +397,54 @@
         return mExecutorHelper.getLifecyclePath(mClientRecord.getLifecycleState(), finish,
                 true /* excludeLastState */).toArray();
     }
+
+    /** A transaction item that requires some specific post-execution state. */
+    private static class PostExecItem extends StubItem {
+
+        @LifecycleState
+        private int mPostExecutionState;
+
+        PostExecItem(@LifecycleState int state) {
+            mPostExecutionState = state;
+        }
+
+        @Override
+        public int getPostExecutionState() {
+            return mPostExecutionState;
+        }
+    }
+
+    /** Stub implementation of a transaction item that works as a base class for items in tests. */
+    private static class StubItem extends ClientTransactionItem  {
+
+        private StubItem() {
+        }
+
+        private StubItem(Parcel in) {
+        }
+
+        @Override
+        public void execute(ClientTransactionHandler client, IBinder token,
+                PendingTransactionActions pendingActions) {
+        }
+
+        @Override
+        public void recycle() {
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+        }
+
+        public static final Parcelable.Creator<StubItem> CREATOR =
+                new Parcelable.Creator<StubItem>() {
+                    public StubItem createFromParcel(Parcel in) {
+                        return new StubItem(in);
+                    }
+
+                    public StubItem[] newArray(int size) {
+                        return new StubItem[size];
+                    }
+                };
+    }
 }
diff --git a/core/tests/coretests/src/android/graphics/RectTest.java b/core/tests/coretests/src/android/graphics/RectTest.java
new file mode 100644
index 0000000..d31d7d5
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/RectTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 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.graphics;
+
+import static android.graphics.Rect.copyOrNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RectTest {
+
+    @Test
+    public void copyOrNull_passesThroughNull() {
+        assertNull(copyOrNull(null));
+    }
+
+    @Test
+    public void copyOrNull_copiesNonNull() {
+        final Rect orig = new Rect(1, 2, 3, 4);
+        final Rect copy = copyOrNull(orig);
+
+        assertEquals(orig, copy);
+        assertNotSame(orig, copy);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
new file mode 100644
index 0000000..45b19bc
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 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.internal.util;
+
+import static com.android.internal.util.DumpUtils.filterRecord;
+import static com.android.internal.util.DumpUtils.isNonPlatformPackage;
+import static com.android.internal.util.DumpUtils.isPlatformPackage;
+
+import android.content.ComponentName;
+
+import junit.framework.TestCase;
+
+/**
+ * Run with:
+ atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpTest.java
+ */
+public class DumpUtilsTest extends TestCase {
+
+    private static ComponentName cn(String componentName) {
+        if (componentName == null) {
+            return null;
+        }
+        return ComponentName.unflattenFromString(componentName);
+    }
+
+    private static ComponentName.WithComponentName wcn(String componentName) {
+        if (componentName == null) {
+            return null;
+        }
+        return () -> cn(componentName);
+    }
+
+    public void testIsPlatformPackage() {
+        assertTrue(isPlatformPackage("android"));
+        assertTrue(isPlatformPackage("android.abc"));
+        assertTrue(isPlatformPackage("com.android.abc"));
+
+        assertFalse(isPlatformPackage((String) null));
+        assertFalse(isPlatformPackage("com.google"));
+
+        assertTrue(isPlatformPackage(cn("android/abc")));
+        assertTrue(isPlatformPackage(cn("android.abc/abc")));
+        assertTrue(isPlatformPackage(cn("com.android.def/abc")));
+
+        assertFalse(isPlatformPackage(cn(null)));
+        assertFalse(isPlatformPackage(cn("com.google.def/abc")));
+
+        assertTrue(isPlatformPackage(wcn("android/abc")));
+        assertTrue(isPlatformPackage(wcn("android.abc/abc")));
+        assertTrue(isPlatformPackage(wcn("com.android.def/abc")));
+
+        assertFalse(isPlatformPackage(wcn(null)));
+        assertFalse(isPlatformPackage(wcn("com.google.def/abc")));
+    }
+
+    public void testIsNonPlatformPackage() {
+        assertFalse(isNonPlatformPackage("android"));
+        assertFalse(isNonPlatformPackage("android.abc"));
+        assertFalse(isNonPlatformPackage("com.android.abc"));
+
+        assertFalse(isNonPlatformPackage((String) null));
+        assertTrue(isNonPlatformPackage("com.google"));
+
+        assertFalse(isNonPlatformPackage(cn("android/abc")));
+        assertFalse(isNonPlatformPackage(cn("android.abc/abc")));
+        assertFalse(isNonPlatformPackage(cn("com.android.def/abc")));
+
+        assertFalse(isNonPlatformPackage(cn(null)));
+        assertTrue(isNonPlatformPackage(cn("com.google.def/abc")));
+
+        assertFalse(isNonPlatformPackage(wcn("android/abc")));
+        assertFalse(isNonPlatformPackage(wcn("android.abc/abc")));
+        assertFalse(isNonPlatformPackage(wcn("com.android.def/abc")));
+
+        assertFalse(isNonPlatformPackage(wcn(null)));
+        assertTrue(isNonPlatformPackage(wcn("com.google.def/abc")));
+    }
+
+    public void testFilterRecord() {
+        assertFalse(filterRecord(null).test(wcn("com.google.p/abc")));
+        assertFalse(filterRecord(null).test(wcn("com.android.p/abc")));
+
+        assertTrue(filterRecord("all").test(wcn("com.google.p/abc")));
+        assertTrue(filterRecord("all").test(wcn("com.android.p/abc")));
+        assertFalse(filterRecord("all").test(wcn(null)));
+
+        assertFalse(filterRecord("all-platform").test(wcn("com.google.p/abc")));
+        assertTrue(filterRecord("all-platform").test(wcn("com.android.p/abc")));
+        assertFalse(filterRecord("all-platform").test(wcn(null)));
+
+        assertTrue(filterRecord("all-non-platform").test(wcn("com.google.p/abc")));
+        assertFalse(filterRecord("all-non-platform").test(wcn("com.android.p/abc")));
+        assertFalse(filterRecord("all-non-platform").test(wcn(null)));
+
+        // Partial string match.
+        assertTrue(filterRecord("abc").test(wcn("com.google.p/.abc")));
+        assertFalse(filterRecord("abc").test(wcn("com.google.p/.def")));
+        assertTrue(filterRecord("com").test(wcn("com.google.p/.xyz")));
+
+        // Full component name match.
+        assertTrue(filterRecord("com.google/com.google.abc").test(wcn("com.google/.abc")));
+        assertFalse(filterRecord("com.google/com.google.abc").test(wcn("com.google/.abc.def")));
+
+
+        // Hex ID match
+        ComponentName.WithComponentName component = wcn("com.google/.abc");
+
+        assertTrue(filterRecord(
+                Integer.toHexString(System.identityHashCode(component))).test(component));
+        // Same component name, but different ID, no match.
+        assertFalse(filterRecord(
+                Integer.toHexString(System.identityHashCode(component))).test(
+                        wcn("com.google/.abc")));
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
new file mode 100644
index 0000000..f00c48c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.internal.util;
+
+import junit.framework.TestCase;
+
+/**
+ * Run with:
+ atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
+ */
+public class ParseUtilsTest extends TestCase {
+    public void testParseInt() {
+        assertEquals(1, ParseUtils.parseInt(null, 1));
+        assertEquals(1, ParseUtils.parseInt("", 1));
+        assertEquals(1, ParseUtils.parseInt("1x", 1));
+        assertEquals(2, ParseUtils.parseInt("2", 1));
+
+        assertEquals(2, ParseUtils.parseInt("+2", 1));
+        assertEquals(-2, ParseUtils.parseInt("-2", 1));
+    }
+
+    public void testParseIntWithBase() {
+        assertEquals(1, ParseUtils.parseIntWithBase(null, 10, 1));
+        assertEquals(1, ParseUtils.parseIntWithBase("", 10, 1));
+        assertEquals(1, ParseUtils.parseIntWithBase("1x", 10, 1));
+        assertEquals(2, ParseUtils.parseIntWithBase("2", 10, 1));
+        assertEquals(10, ParseUtils.parseIntWithBase("10", 10, 1));
+        assertEquals(3, ParseUtils.parseIntWithBase("10", 3, 1));
+
+        assertEquals(3, ParseUtils.parseIntWithBase("+10", 3, 1));
+        assertEquals(-3, ParseUtils.parseIntWithBase("-10", 3, 1));
+    }
+
+    public void testParseLong() {
+        assertEquals(1L, ParseUtils.parseLong(null, 1));
+        assertEquals(1L, ParseUtils.parseLong("", 1));
+        assertEquals(1L, ParseUtils.parseLong("1x", 1));
+        assertEquals(2L, ParseUtils.parseLong("2", 1));
+    }
+
+    public void testParseLongWithBase() {
+        assertEquals(1L, ParseUtils.parseLongWithBase(null, 10, 1));
+        assertEquals(1L, ParseUtils.parseLongWithBase("", 10, 1));
+        assertEquals(1L, ParseUtils.parseLongWithBase("1x", 10, 1));
+        assertEquals(2L, ParseUtils.parseLongWithBase("2", 10, 1));
+        assertEquals(10L, ParseUtils.parseLongWithBase("10", 10, 1));
+        assertEquals(3L, ParseUtils.parseLongWithBase("10", 3, 1));
+
+        assertEquals(3L, ParseUtils.parseLongWithBase("+10", 3, 1));
+        assertEquals(-3L, ParseUtils.parseLongWithBase("-10", 3, 1));
+
+        assertEquals(10_000_000_000L, ParseUtils.parseLongWithBase("+10000000000", 10, 1));
+        assertEquals(-10_000_000_000L, ParseUtils.parseLongWithBase("-10000000000", 10, 1));
+
+        assertEquals(10_000_000_000L, ParseUtils.parseLongWithBase(null, 10, 10_000_000_000L));
+    }
+
+    public void testParseFloat() {
+        assertEquals(0.5f, ParseUtils.parseFloat(null, 0.5f));
+        assertEquals(0.5f, ParseUtils.parseFloat("", 0.5f));
+        assertEquals(0.5f, ParseUtils.parseFloat("1x", 0.5f));
+        assertEquals(1.5f, ParseUtils.parseFloat("1.5", 0.5f));
+    }
+
+    public void testParseDouble() {
+        assertEquals(0.5, ParseUtils.parseDouble(null, 0.5));
+        assertEquals(0.5, ParseUtils.parseDouble("", 0.5));
+        assertEquals(0.5, ParseUtils.parseDouble("1x", 0.5));
+        assertEquals(1.5, ParseUtils.parseDouble("1.5", 0.5));
+    }
+
+    public void testParseBoolean() {
+        assertEquals(false, ParseUtils.parseBoolean(null, false));
+        assertEquals(true, ParseUtils.parseBoolean(null, true));
+
+        assertEquals(false, ParseUtils.parseBoolean("", false));
+        assertEquals(true, ParseUtils.parseBoolean("", true));
+
+        assertEquals(true, ParseUtils.parseBoolean("true", false));
+        assertEquals(true, ParseUtils.parseBoolean("true", true));
+
+        assertEquals(false, ParseUtils.parseBoolean("false", false));
+        assertEquals(false, ParseUtils.parseBoolean("false", true));
+
+        assertEquals(true, ParseUtils.parseBoolean("1", false));
+        assertEquals(true, ParseUtils.parseBoolean("1", true));
+
+        assertEquals(false, ParseUtils.parseBoolean("0", false));
+        assertEquals(false, ParseUtils.parseBoolean("0", true));
+    }
+}
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index aff942d..3843cb9 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.CheckResult;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -99,6 +100,16 @@
         }
     }
 
+    /**
+     * Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise.
+     *
+     * @hide
+     */
+    @Nullable
+    public static Rect copyOrNull(@Nullable Rect r) {
+        return r == null ? null : new Rect(r);
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 3118009..f1cc569 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -386,6 +386,38 @@
   EXPECT_EQ(basic::R::array::integerArray1, last_ref);
 }
 
+TEST_F(AssetManager2Test, ResolveDeepIdReference) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  // Set up the resource ids
+  const uint32_t high_ref = assetmanager
+      .GetResourceId("@id/high_ref", "values", "com.android.basic");
+  ASSERT_NE(high_ref, 0u);
+  const uint32_t middle_ref = assetmanager
+      .GetResourceId("@id/middle_ref", "values", "com.android.basic");
+  ASSERT_NE(middle_ref, 0u);
+  const uint32_t low_ref = assetmanager
+      .GetResourceId("@id/low_ref", "values", "com.android.basic");
+  ASSERT_NE(low_ref, 0u);
+
+  // Retrieve the most shallow resource
+  Res_value value;
+  ResTable_config config;
+  uint32_t flags;
+  ApkAssetsCookie cookie = assetmanager.GetResource(high_ref, false /*may_be_bag*/,
+                                                    0 /*density_override*/,
+                                                    &value, &config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  EXPECT_EQ(middle_ref, value.data);
+
+  // Check that resolving the reference resolves to the deepest id
+  uint32_t last_ref = high_ref;
+  assetmanager.ResolveReference(cookie, &value, &config, &flags, &last_ref);
+  EXPECT_EQ(last_ref, low_ref);
+}
+
 TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({basic_assets_.get()});
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
index 1733b6a..b721ebf 100644
--- a/libs/androidfw/tests/data/basic/basic.apk
+++ b/libs/androidfw/tests/data/basic/basic.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index b343562..d4b2683 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -78,4 +78,8 @@
         <item type="string" name="test2" />
         <item type="array" name="integerArray1" />
     </overlayable>
+
+    <item name="high_ref" type="id">@id/middle_ref</item>
+    <item name="middle_ref" type="id">@id/low_ref</item>
+    <item name="low_ref" type="id"/>
 </resources>
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 7888436..c486e68 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1222,7 +1222,7 @@
             TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
             TAG_GPS_TIMESTAMP));
     // Mappings from tag number to IFD type for pointer tags.
-    private static final HashMap sExifPointerTagMap = new HashMap();
+    private static final HashMap<Integer, Integer> sExifPointerTagMap = new HashMap();
 
     // See JPEG File Interchange Format Version 1.02.
     // The following values are defined for handling JPEG streams. In this implementation, we are
@@ -1299,6 +1299,7 @@
     private final boolean mIsInputStream;
     private int mMimeType;
     private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
+    private Set<Integer> mAttributesOffsets = new HashSet<>(EXIF_TAGS.length);
     private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
     private boolean mHasThumbnail;
     // The following values used for indicating a thumbnail position.
@@ -2957,8 +2958,9 @@
         }
         // See TIFF 6.0 Section 2: TIFF Structure, Figure 1.
         short numberOfDirectoryEntry = dataInputStream.readShort();
-        if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength) {
-            // Return if the size of entries is too big.
+        if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength
+                || numberOfDirectoryEntry <= 0) {
+            // Return if the size of entries is either too big or negative.
             return;
         }
 
@@ -3049,7 +3051,7 @@
             }
 
             // Recursively parse IFD when a IFD pointer tag appears.
-            Object nextIfdType = sExifPointerTagMap.get(tagNumber);
+            Integer nextIfdType = sExifPointerTagMap.get(tagNumber);
             if (DEBUG) {
                 Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount);
             }
@@ -3083,9 +3085,20 @@
                 if (DEBUG) {
                     Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name));
                 }
+
+                // Check if the next IFD offset
+                // 1. Exists within the boundaries of the input stream
+                // 2. Does not point to a previously read IFD.
                 if (offset > 0L && offset < dataInputStream.mLength) {
-                    dataInputStream.seek(offset);
-                    readImageFileDirectory(dataInputStream, (int) nextIfdType);
+                    if (!mAttributesOffsets.contains((int) offset)) {
+                        // Save offset of current IFD to prevent reading an IFD that is already read
+                        mAttributesOffsets.add(dataInputStream.mPosition);
+                        dataInputStream.seek(offset);
+                        readImageFileDirectory(dataInputStream, nextIfdType);
+                    } else {
+                        Log.w(TAG, "Skip jump into the IFD since it has already been read: "
+                                + "IfdType " + nextIfdType + " (at " + offset + ")");
+                    }
                 } else {
                     Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
                 }
@@ -3127,16 +3140,27 @@
             if (DEBUG) {
                 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
             }
-            // The next IFD offset needs to be bigger than 8
-            // since the first IFD offset is at least 8.
-            if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) {
-                dataInputStream.seek(nextIfdOffset);
-                if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
+            // Check if the next IFD offset
+            // 1. Exists within the boundaries of the input stream
+            // 2. Does not point to a previously read IFD.
+            if (nextIfdOffset > 0L && nextIfdOffset < dataInputStream.mLength) {
+                if (!mAttributesOffsets.contains(nextIfdOffset)) {
+                    // Save offset of current IFD to prevent reading an IFD that is already read.
+                    mAttributesOffsets.add(dataInputStream.mPosition);
+                    dataInputStream.seek(nextIfdOffset);
                     // Do not overwrite thumbnail IFD data if it alreay exists.
-                    readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL);
-                } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) {
-                    readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
+                    if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
+                        readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL);
+                    } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) {
+                        readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
+                    }
+                } else {
+                    Log.w(TAG, "Stop reading file since re-reading an IFD may cause an "
+                            + "infinite loop: " + nextIfdOffset);
                 }
+            } else {
+                Log.w(TAG, "Stop reading file since a wrong offset may cause an infinite loop: "
+                        + nextIfdOffset);
             }
         }
     }
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index a5f0f24..1ca3c1d 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -65,9 +65,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -219,7 +217,7 @@
         stopScan();
         mDevicesFound.clear();
         mSelectedDevice = null;
-        mDevicesAdapter.notifyDataSetChanged();
+        notifyDataSetChanged();
     }
 
     @Override
@@ -265,7 +263,12 @@
             onReadyToShowUI();
         }
         mDevicesFound.add(device);
-        mDevicesAdapter.notifyDataSetChanged();
+        notifyDataSetChanged();
+    }
+
+    private void notifyDataSetChanged() {
+        Handler.getMain().sendMessage(obtainMessage(
+                DevicesAdapter::notifyDataSetChanged, mDevicesAdapter));
     }
 
     //TODO also, on timeout -> call onFailure
@@ -283,7 +286,7 @@
 
     private void onDeviceLost(@Nullable DeviceFilterPair device) {
         mDevicesFound.remove(device);
-        mDevicesAdapter.notifyDataSetChanged();
+        notifyDataSetChanged();
         if (DEBUG) Log.i(LOG_TAG, "Lost device " + device.getDisplayName());
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
index 9554e81..95a8f1d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
@@ -28,6 +28,7 @@
 public class CustomDialogPreference extends DialogPreference {
 
     private CustomPreferenceDialogFragment mFragment;
+    private DialogInterface.OnShowListener mOnShowListener;
 
     public CustomDialogPreference(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
@@ -54,6 +55,10 @@
         return mFragment != null ? mFragment.getDialog() : null;
     }
 
+    public void setOnShowListener(DialogInterface.OnShowListener listner) {
+        mOnShowListener = listner;
+    }
+
     protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
             DialogInterface.OnClickListener listener) {
     }
@@ -71,6 +76,10 @@
         mFragment = fragment;
     }
 
+    private DialogInterface.OnShowListener getOnShowListener() {
+        return mOnShowListener;
+    }
+
     public static class CustomPreferenceDialogFragment extends PreferenceDialogFragment {
 
         public static CustomPreferenceDialogFragment newInstance(String key) {
@@ -104,6 +113,13 @@
         }
 
         @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Dialog dialog = super.onCreateDialog(savedInstanceState);
+            dialog.setOnShowListener(getCustomizablePreference().getOnShowListener());
+            return dialog;
+        }
+
+        @Override
         public void onClick(DialogInterface dialog, int which) {
             super.onClick(dialog, which);
             getCustomizablePreference().onClick(dialog, which);
diff --git a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
new file mode 100644
index 0000000..e92b36a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 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.settingslib;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+
+/**
+ * Utility class that allows Settings to use SystemUI to relay broadcasts related to pinned slices.
+ */
+public class SliceBroadcastRelay {
+
+    public static final String ACTION_REGISTER
+            = "com.android.settingslib.action.REGISTER_SLICE_RECEIVER";
+    public static final String ACTION_UNREGISTER
+            = "com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER";
+    public static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+
+    public static final String EXTRA_URI = "uri";
+    public static final String EXTRA_RECEIVER = "receiver";
+    public static final String EXTRA_FILTER = "filter";
+
+    public static void registerReceiver(Context context, Uri registerKey,
+            Class<? extends BroadcastReceiver> receiver, IntentFilter filter) {
+        Intent registerBroadcast = new Intent(ACTION_REGISTER);
+        registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
+        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+                Process.myUserHandle().getIdentifier()));
+        registerBroadcast.putExtra(EXTRA_RECEIVER,
+                new ComponentName(context.getPackageName(), receiver.getName()));
+        registerBroadcast.putExtra(EXTRA_FILTER, filter);
+
+        context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
+    }
+
+    public static void unregisterReceivers(Context context, Uri registerKey) {
+        Intent registerBroadcast = new Intent(ACTION_UNREGISTER);
+        registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
+        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+                Process.myUserHandle().getIdentifier()));
+
+        context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 547cd9a..fe0b35b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -103,10 +103,9 @@
     public void handleBroadcast(Intent intent) {
         String action = intent.getAction();
         if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-            state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                    WifiManager.WIFI_STATE_UNKNOWN);
-            enabled = state == WifiManager.WIFI_STATE_ENABLED;
+            updateWifiState();
         } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+            updateWifiState();
             final NetworkInfo networkInfo =
                     intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
             connected = networkInfo != null && networkInfo.isConnected();
@@ -128,6 +127,11 @@
         }
     }
 
+    private void updateWifiState() {
+        state = mWifiManager.getWifiState();
+        enabled = state == WifiManager.WIFI_STATE_ENABLED;
+    }
+
     private void updateRssi(int newRssi) {
         rssi = newRssi;
         level = WifiManager.calculateSignalLevel(rssi, WifiManager.RSSI_LEVELS);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index ad300f4..53f7e44 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -50,6 +50,4 @@
     public abstract void onStateChanged(State state);
 
     public abstract int getDetailY();
-
-    public void setExpansion(float expansion) {}
 }
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 8253083..ed63089 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -382,9 +382,9 @@
 
     <!-- Instructions telling the user remaining times when enter SIM PIN view.  -->
     <plurals name="kg_password_default_pin_message">
-        <item quantity="one">Enter SIM PIN, you have <xliff:g id="number">%d</xliff:g> remaining
+        <item quantity="one">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining
 attempt before you must contact your carrier to unlock your device.</item>
-        <item quantity="other">Enter SIM PIN, you have <xliff:g id="number">%d</xliff:g> remaining
+        <item quantity="other">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining
 attempts.</item>
     </plurals>
 
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 74c22b0..49d142a 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -76,7 +76,7 @@
         android:layout_below="@id/label_group"
         android:clickable="false"
         android:ellipsize="marquee"
-        android:maxLines="1"
+        android:singleLine="true"
         android:padding="0dp"
         android:visibility="gone"
         android:gravity="center"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f49d3de4..7eb08c4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -350,6 +350,7 @@
         <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
         <item>com.android.systemui.ScreenDecorations</item>
         <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
+        <item>com.android.systemui.SliceBroadcastRelayHandler</item>
     </string-array>
 
     <!-- SystemUI vender service, used in config_systemUIServiceComponents. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b220091..a1e5b0e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -138,7 +138,7 @@
 
     <!-- Vertical translation of the shelf during animation that happens after the
     notification panel collapses -->
-    <dimen name="shelf_appear_translation">9dp</dimen>
+    <dimen name="shelf_appear_translation">42dp</dimen>
 
     <!-- The amount the content shifts upwards when transforming into the icon -->
     <dimen name="notification_icon_transform_content_shift">32dp</dimen>
@@ -497,9 +497,6 @@
          device. -->
     <dimen name="unlock_move_distance">75dp</dimen>
 
-    <!-- Distance after which the scrim starts fading in when dragging down the quick settings -->
-    <dimen name="notification_scrim_wait_distance">100dp</dimen>
-
     <!-- Move distance for the unlock hint animation on the lockscreen -->
     <dimen name="hint_move_distance">75dp</dimen>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index d45c427..458e133d7 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -24,6 +24,8 @@
     <item type="id" name="scale_y_animator_tag"/>
     <item type="id" name="top_inset_animator_tag"/>
     <item type="id" name="height_animator_tag"/>
+    <item type="id" name="x_animator_tag"/>
+    <item type="id" name="y_animator_tag"/>
     <item type="id" name="shadow_alpha_animator_tag"/>
     <item type="id" name="translation_x_animator_end_value_tag"/>
     <item type="id" name="translation_y_animator_end_value_tag"/>
@@ -34,6 +36,8 @@
     <item type="id" name="top_inset_animator_end_value_tag"/>
     <item type="id" name="height_animator_end_value_tag"/>
     <item type="id" name="shadow_alpha_animator_end_value_tag"/>
+    <item type="id" name="x_animator_tag_end_value"/>
+    <item type="id" name="y_animator_tag_end_value"/>
     <item type="id" name="translation_x_animator_start_value_tag"/>
     <item type="id" name="translation_y_animator_start_value_tag"/>
     <item type="id" name="translation_z_animator_start_value_tag"/>
@@ -43,6 +47,8 @@
     <item type="id" name="top_inset_animator_start_value_tag"/>
     <item type="id" name="height_animator_start_value_tag"/>
     <item type="id" name="shadow_alpha_animator_start_value_tag"/>
+    <item type="id" name="x_animator_tag_start_value"/>
+    <item type="id" name="y_animator_tag_start_value"/>
     <item type="id" name="doze_saved_filter_tag"/>
     <item type="id" name="qs_icon_tag"/>
     <item type="id" name="qs_slash_tag"/>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index d22aab5..3ecf89c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -49,4 +49,9 @@
      * Notifies SystemUI that Overview is shown.
      */
     void onOverviewShown(boolean fromHome) = 6;
+
+    /**
+     * Get the secondary split screen app's rectangle when not minimized.
+     */
+    Rect getNonMinimizedSplitScreenSecondaryBounds() = 7;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index e1540ea..282a8f1 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -22,7 +22,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Handler;
@@ -42,6 +41,7 @@
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.GraphicBufferCompat;
+import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.CallbackController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -147,6 +147,19 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        public Rect getNonMinimizedSplitScreenSecondaryBounds() {
+            long token = Binder.clearCallingIdentity();
+            try {
+                Divider divider = ((SystemUIApplication) mContext).getComponent(Divider.class);
+                if (divider != null) {
+                    return divider.getView().getNonMinimizedSplitScreenSecondaryBounds();
+                }
+                return null;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     };
 
     private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
new file mode 100644
index 0000000..68f5836
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.SliceBroadcastRelay;
+
+/**
+ * Allows settings to register certain broadcasts to launch the settings app for pinned slices.
+ * @see SliceBroadcastRelay
+ */
+public class SliceBroadcastRelayHandler extends SystemUI {
+    private static final String TAG = "SliceBroadcastRelay";
+    private static final boolean DEBUG = false;
+
+    private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>();
+
+    @Override
+    public void start() {
+        if (DEBUG) Log.d(TAG, "Start");
+        IntentFilter filter = new IntentFilter(SliceBroadcastRelay.ACTION_REGISTER);
+        filter.addAction(SliceBroadcastRelay.ACTION_UNREGISTER);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    @VisibleForTesting
+    void handleIntent(Intent intent) {
+        if (SliceBroadcastRelay.ACTION_REGISTER.equals(intent.getAction())) {
+            Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
+            ComponentName receiverClass =
+                    intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER);
+            IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER);
+            if (DEBUG) Log.d(TAG, "Register " + uri + " " + receiverClass + " " + filter);
+            getOrCreateRelay(uri).register(mContext, receiverClass, filter);
+        } else if (SliceBroadcastRelay.ACTION_UNREGISTER.equals(intent.getAction())) {
+            Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
+            if (DEBUG) Log.d(TAG, "Unregister " + uri);
+            getAndRemoveRelay(uri).unregister(mContext);
+        }
+    }
+
+    private BroadcastRelay getOrCreateRelay(Uri uri) {
+        BroadcastRelay ret = mRelays.get(uri);
+        if (ret == null) {
+            ret = new BroadcastRelay(uri);
+            mRelays.put(uri, ret);
+        }
+        return ret;
+    }
+
+    private BroadcastRelay getAndRemoveRelay(Uri uri) {
+        return mRelays.remove(uri);
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            handleIntent(intent);
+        }
+    };
+
+    private static class BroadcastRelay extends BroadcastReceiver {
+
+        private final ArraySet<ComponentName> mReceivers = new ArraySet<>();
+        private final UserHandle mUserId;
+
+        public BroadcastRelay(Uri uri) {
+            mUserId = new UserHandle(ContentProvider.getUserIdFromUri(uri));
+        }
+
+        public void register(Context context, ComponentName receiver, IntentFilter filter) {
+            mReceivers.add(receiver);
+            context.registerReceiver(this, filter);
+        }
+
+        public void unregister(Context context) {
+            context.unregisterReceiver(this);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            for (ComponentName receiver : mReceivers) {
+                intent.setComponent(receiver);
+                if (DEBUG) Log.d(TAG, "Forwarding " + receiver + " " + intent + " " + mUserId);
+                context.sendBroadcastAsUser(intent, mUserId);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index a61ce8c..4e7c3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -69,8 +69,8 @@
         SystemUIFactory.createFromConfig(this);
 
         if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
-            IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
-            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+            IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+            bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
             registerReceiver(new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
@@ -86,11 +86,21 @@
                         }
                     }
 
-                    IntentFilter localeChangedFilter = new IntentFilter(
-                            Intent.ACTION_LOCALE_CHANGED);
-                    registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
+
                 }
-            }, filter);
+            }, bootCompletedFilter);
+
+            IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+            registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+                        if (!mBootCompleted) return;
+                        // Update names of SystemUi notification channels
+                        NotificationChannels.createAll(context);
+                    }
+                }
+            }, localeChangedFilter);
         } else {
             // We don't need to startServices for sub-process that is doing some tasks.
             // (screenshots, sweetsweetdesserts or tuner ..)
@@ -239,14 +249,4 @@
     public SystemUI[] getServices() {
         return mServices;
     }
-
-    private final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
-                // Update names of SystemUi notification channels
-                NotificationChannels.createAll(context);
-            }
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index c390764..b7ff984 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -113,7 +113,9 @@
                     // The display buffers will be empty and need to be filled.
                     mHost.dozeTimeTick();
                     // The first frame may arrive when the display isn't ready yet.
-                    mHandler.postDelayed(mHost::dozeTimeTick, 100);
+                    mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 100);
+                    // The the delayed frame may arrive when the display isn't ready yet either.
+                    mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 1000);
                 }
                 scheduleTimeTick();
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index d8d07c0..1fd6023 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -56,6 +56,7 @@
 
     private AnimatorSet mBounceAnimatorSet;
     private int mAnimatingToPage = -1;
+    private float mLastExpansion;
 
     public PagedTileLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -172,8 +173,19 @@
 
     @Override
     public void setExpansion(float expansion) {
-        for (TileRecord tr : mTiles) {
-            tr.tileView.setExpansion(expansion);
+        mLastExpansion = expansion;
+        updateSelected();
+    }
+
+    private void updateSelected() {
+        // Start the marquee when fully expanded and stop when fully collapsed. Leave as is for
+        // other expansion ratios since there is no way way to pause the marquee.
+        if (mLastExpansion > 0f && mLastExpansion < 1f) {
+            return;
+        }
+        boolean selected = mLastExpansion == 1f;
+        for (int i = 0; i < mPages.size(); i++) {
+            mPages.get(i).setSelected(i == getCurrentItem() ? selected : false);
         }
     }
 
@@ -323,6 +335,7 @@
             new ViewPager.SimpleOnPageChangeListener() {
                 @Override
                 public void onPageSelected(int position) {
+                    updateSelected();
                     if (mPageIndicator == null) return;
                     if (mPageListener != null) {
                         mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 6c7eda7..5ae43c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -65,10 +65,9 @@
     void setKeyguardShowing(boolean keyguardShowing);
 
     /**
-     * Returns the {@link View} that should expand the quick settings when clicked.
+     * Sets the {@link android.view.View.OnClickListener to be used on elements that expend QS.
      */
-    @Nullable
-    View getExpandView();
+    void setExpandClickListener(View.OnClickListener onClickListener);
 
     default void disable(int state1, int state2, boolean animate) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index abe819b..fd9ddb0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs;
 
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +26,7 @@
 import android.graphics.PorterDuff.Mode;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.RippleDrawable;
+import android.os.Bundle;
 import android.os.UserManager;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
@@ -34,6 +34,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -96,6 +97,7 @@
     private ImageView mMobileRoaming;
     private final int mColorForeground;
     private final CellSignalState mInfo = new CellSignalState();
+    private OnClickListener mExpandClickListener;
 
     public QSFooterImpl(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -140,6 +142,7 @@
         mActivityStarter = Dependency.get(ActivityStarter.class);
         addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
                 oldBottom) -> updateAnimator(right - left));
+        setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
     }
 
     private void updateAnimator(int width) {
@@ -205,6 +208,11 @@
     }
 
     @Override
+    public void setExpandClickListener(OnClickListener onClickListener) {
+        mExpandClickListener = onClickListener;
+    }
+
+    @Override
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
@@ -238,8 +246,20 @@
     }
 
     @Override
-    public View getExpandView() {
-        return findViewById(R.id.expand_indicator);
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (action == AccessibilityNodeInfo.ACTION_EXPAND) {
+            if (mExpandClickListener != null) {
+                mExpandClickListener.onClick(null);
+                return true;
+            }
+        }
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index b82e355..cbd1ca1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -240,11 +240,6 @@
     @Override
     public void setHeaderClickable(boolean clickable) {
         if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable);
-
-        View expandView = mFooter.getExpandView();
-        if (expandView != null) {
-            expandView.setClickable(clickable);
-        }
     }
 
     @Override
@@ -369,11 +364,7 @@
 
     @Override
     public void setExpandClickListener(OnClickListener onClickListener) {
-        View expandView = mFooter.getExpandView();
-
-        if (expandView != null) {
-            expandView.setOnClickListener(onClickListener);
-        }
+        mFooter.setExpandClickListener(onClickListener);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
index 24b5a34..2ea21c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
@@ -114,11 +114,9 @@
         }
     }
 
-    @Nullable
     @Override
-    public View getExpandView() {
+    public void setExpandClickListener(OnClickListener onClickListener) {
         // No view that should expand/collapse the quick settings.
-        return null;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index d21b06f..5649f7f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -107,15 +107,6 @@
     }
 
     @Override
-    public void setExpansion(float expansion) {
-        // Start the marquee when fully expanded and stop when fully collapsed. Leave as is for
-        // other expansion ratios since there is no way way to pause the marquee.
-        boolean selected = expansion == 1f ? true : expansion == 0f ? false : mLabel.isSelected();
-        mLabel.setSelected(selected);
-        mSecondLine.setSelected(selected);
-    }
-
-    @Override
     protected void handleStateChanged(QSTile.State state) {
         super.handleStateChanged(state);
         if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
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 0c7919d..a25c466 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -51,6 +51,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 /** Quick settings tile: Bluetooth **/
 public class BluetoothTile extends QSTileImpl<BooleanState> {
@@ -137,7 +138,9 @@
         if (enabled) {
             if (connected) {
                 state.icon = new BluetoothConnectedTileIcon();
-                state.label = mController.getLastDeviceName();
+                if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) {
+                    state.label = mController.getConnectedDeviceName();
+                }
                 state.contentDescription =
                         mContext.getString(R.string.accessibility_bluetooth_name, state.label)
                                 + ", " + state.secondaryLabel;
@@ -177,9 +180,18 @@
         if (isTransient) {
             return mContext.getString(R.string.quick_settings_bluetooth_secondary_label_transient);
         }
-        final CachedBluetoothDevice lastDevice = mController.getLastDevice();
 
-        if (enabled && connected && lastDevice != null) {
+        List<CachedBluetoothDevice> connectedDevices = mController.getConnectedDevices();
+        if (enabled && connected && !connectedDevices.isEmpty()) {
+            if (connectedDevices.size() > 1) {
+                // TODO(b/76102598): add a new string for "X connected devices" after P
+                return mContext.getResources().getQuantityString(
+                        R.plurals.quick_settings_hotspot_secondary_label_num_devices,
+                        connectedDevices.size(),
+                        connectedDevices.size());
+            }
+
+            CachedBluetoothDevice lastDevice = connectedDevices.get(0);
             final int batteryLevel = lastDevice.getBatteryLevel();
 
             if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3d8e037..1149ad1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -378,6 +378,23 @@
         return mWindowManagerProxy;
     }
 
+    public Rect getNonMinimizedSplitScreenSecondaryBounds() {
+        calculateBoundsForPosition(mSnapTargetBeforeMinimized.position,
+                DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
+        switch (mDockSide) {
+            case WindowManager.DOCKED_LEFT:
+                mOtherTaskRect.right -= mStableInsets.right;
+                break;
+            case WindowManager.DOCKED_RIGHT:
+                mOtherTaskRect.left -= mStableInsets.left;
+                break;
+            case WindowManager.DOCKED_TOP:
+                mOtherTaskRect.bottom -= mStableInsets.bottom;
+                break;
+        }
+        return mOtherTaskRect;
+    }
+
     public boolean startDragging(boolean animate, boolean touching) {
         cancelFlingAnimation();
         if (touching) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 85a6062..1e5b37c 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -180,7 +180,7 @@
             @Override
             public void run() {
                 try {
-                    WindowManagerGlobal.getWindowManagerService().setDockedStackResizing(resizing);
+                    ActivityManager.getService().setSplitScreenResizing(resizing);
                 } catch (RemoteException e) {
                     Log.w(TAG, "Error calling setDockedStackResizing: " + e);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 4388b41..011be88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -62,6 +62,10 @@
         mEmptyText.setText(mText);
     }
 
+    public int getTextResource() {
+        return mText;
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 6364f5b..44136c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -58,7 +58,7 @@
             = SystemProperties.getBoolean("debug.icon_scroll_animations", true);
     private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
     private static final String TAG = "NotificationShelf";
-    private static final long SHELF_IN_TRANSLATION_DURATION = 220;
+    private static final long SHELF_IN_TRANSLATION_DURATION = 200;
 
     private ViewInvertHelper mViewInvertHelper;
     private boolean mDark;
@@ -157,14 +157,18 @@
 
     public void fadeInTranslating() {
         float translation = mShelfIcons.getTranslationY();
-        mShelfIcons.setTranslationY(translation + mShelfAppearTranslation);
+        mShelfIcons.setTranslationY(translation - mShelfAppearTranslation);
         mShelfIcons.setAlpha(0);
         mShelfIcons.animate()
-                .alpha(1)
-                .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+                .setInterpolator(Interpolators.DECELERATE_QUINT)
                 .translationY(translation)
                 .setDuration(SHELF_IN_TRANSLATION_DURATION)
                 .start();
+        mShelfIcons.animate()
+                .alpha(1)
+                .setInterpolator(Interpolators.LINEAR)
+                .setDuration(SHELF_IN_TRANSLATION_DURATION)
+                .start();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
index 14a6c42..b2eb18e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.util.AttributeSet;
@@ -35,6 +36,7 @@
     private boolean mIsVisible;
     private boolean mIsSecondaryVisible;
     private boolean mAnimating;
+    private boolean mSecondaryAnimating;
     private int mDuration = 260;
 
     public StackScrollerDecorView(Context context, AttributeSet attrs) {
@@ -61,13 +63,26 @@
     }
 
     public void performVisibilityAnimation(boolean nowVisible) {
-        animateText(mContent, nowVisible, null /* onFinishedRunnable */);
-        mIsVisible = nowVisible;
+        performVisibilityAnimation(nowVisible, null /* onFinishedRunnable */);
     }
 
     public void performVisibilityAnimation(boolean nowVisible, Runnable onFinishedRunnable) {
-        animateText(mContent, nowVisible, onFinishedRunnable);
-        mIsVisible = nowVisible;
+        boolean oldVisible = isVisible();
+        animateText(mContent, nowVisible, oldVisible, new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    mAnimating = true;
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mAnimating = false;
+                    mIsVisible = nowVisible;
+                    if (onFinishedRunnable != null) {
+                        onFinishedRunnable.run();
+                    }
+                }
+            });
     }
 
     public void performSecondaryVisibilityAnimation(boolean nowVisible) {
@@ -76,16 +91,43 @@
 
     public void performSecondaryVisibilityAnimation(boolean nowVisible,
             Runnable onFinishedRunnable) {
-        animateText(mSecondaryView, nowVisible, onFinishedRunnable);
-        mIsSecondaryVisible = nowVisible;
+        boolean oldVisible = isSecondaryVisible();
+        animateText(mSecondaryView, nowVisible, oldVisible, new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    mSecondaryAnimating = true;
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mSecondaryAnimating = false;
+                    mIsSecondaryVisible = nowVisible;
+                    if (onFinishedRunnable != null) {
+                        onFinishedRunnable.run();
+                    }
+                }
+            });
     }
 
+    /**
+     * Check whether the secondary view is visible or not.<p/>
+     *
+     * @see #isVisible()
+     */
     public boolean isSecondaryVisible() {
-        return mSecondaryView != null && (mIsSecondaryVisible || mAnimating);
+        return mSecondaryView != null && (mIsSecondaryVisible ^ mSecondaryAnimating);
     }
 
+    /**
+     * Check whether the whole view is visible or not.<p/>
+     * The view is considered visible if it matches one of following:
+     * <ul>
+     *   <li> It's visible and there is no ongoing animation. </li>
+     *   <li> It's not visible but is animating, thus being eventually visible. </li>
+     * </ul>
+     */
     public boolean isVisible() {
-        return mIsVisible || mAnimating;
+        return mIsVisible ^ mAnimating;
     }
 
     void setDuration(int duration) {
@@ -95,15 +137,18 @@
     /**
      * Animate the text to a new visibility.
      *
-     * @param nowVisible should it now be visible
-     * @param onFinishedRunnable A runnable which should be run when the animation is
-     *        finished.
+     * @param view Target view, maybe content view or dissmiss view
+     * @param nowVisible Should it now be visible
+     * @param oldVisible Is it visible currently
+     * @param listener A listener that doing flag settings or other actions
      */
-    private void animateText(View view, boolean nowVisible, final Runnable onFinishedRunnable) {
+    private void animateText(View view, boolean nowVisible, boolean oldVisible,
+        AnimatorListenerAdapter listener) {
         if (view == null) {
             return;
         }
-        if (nowVisible != mIsVisible) {
+
+        if (nowVisible != oldVisible) {
             // Animate text
             float endValue = nowVisible ? 1.0f : 0.0f;
             Interpolator interpolator;
@@ -112,24 +157,11 @@
             } else {
                 interpolator = Interpolators.ALPHA_OUT;
             }
-            mAnimating = true;
             view.animate()
                     .alpha(endValue)
                     .setInterpolator(interpolator)
                     .setDuration(mDuration)
-                    .withEndAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            mAnimating = false;
-                            if (onFinishedRunnable != null) {
-                                onFinishedRunnable.run();
-                            }
-                        }
-                    });
-        } else {
-            if (onFinishedRunnable != null) {
-                onFinishedRunnable.run();
-            }
+                    .setListener(listener);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index e7c8c94..8160f90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -75,7 +75,8 @@
         int displayId = getDisplayId();
         for (ActivityManager.StackInfo stackInfo :stackInfoList) {
             // if the display id is known and does not match the stack we skip
-            if (displayId != -1 && displayId != stackInfo.displayId) {
+            if (displayId != -1 && displayId != stackInfo.displayId ||
+                    stackInfo.topActivity == null) {
                 continue;
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index dbd1cd4..f1e2302 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -20,7 +20,9 @@
 import android.content.pm.UserInfo;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.os.AsyncTask;
+import android.os.UserHandle;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -31,6 +33,7 @@
 
 import androidx.car.widget.PagedListView;
 
+import com.android.internal.util.UserIcons;
 import com.android.settingslib.users.UserManagerHelper;
 import com.android.systemui.R;
 
@@ -180,13 +183,7 @@
         @Override
         public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
             UserRecord userRecord = mUsers.get(position);
-            if (!userRecord.mIsAddUser) {
-                holder.mUserAvatarImageView.setImageBitmap(mUserManagerHelper
-                        .getUserIcon(userRecord.mInfo));
-            } else {
-                holder.mUserAvatarImageView.setImageDrawable(mContext
-                        .getDrawable(R.drawable.car_add_circle_round));
-            }
+            holder.mUserAvatarImageView.setImageBitmap(getUserRecordIcon(userRecord));
             holder.mUserNameTextView.setText(userRecord.mInfo.name);
             holder.mView.setOnClickListener(v -> {
                 if (userRecord == null) {
@@ -219,6 +216,20 @@
 
         }
 
+        private Bitmap getUserRecordIcon(UserRecord userRecord) {
+            if (userRecord.mIsStartGuestSession) {
+                return UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
+                                mContext.getResources(), UserHandle.USER_NULL, false));
+            }
+
+            if (userRecord.mIsAddUser) {
+                return UserIcons.convertToBitmap(mContext
+                        .getDrawable(R.drawable.car_add_circle_round));
+            }
+
+            return mUserManagerHelper.getUserIcon(userRecord.mInfo);
+        }
+
         private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> {
 
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 7d283d9..81d6191 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -28,8 +28,10 @@
 import android.os.IBinder;
 import android.util.Log;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
@@ -46,7 +48,7 @@
     private Handler mHandler;
     private Car mCar;
     private CarHvacManager mHvacManager;
-    private HashMap<HvacKey, TemperatureView> mTempComponents = new HashMap<>();
+    private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
 
     public HvacController(Context context) {
         mContext = context;
@@ -114,18 +116,24 @@
      * @param temperatureView
      */
     public void addHvacTextView(TemperatureView temperatureView) {
-        mTempComponents.put(
-                new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId()),
-                temperatureView);
+
+        HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
+        if (!mTempComponents.containsKey(hvacKey)) {
+            mTempComponents.put(hvacKey, new ArrayList<>());
+        }
+        mTempComponents.get(hvacKey).add(temperatureView);
         initComponent(temperatureView);
     }
 
     private void initComponents() {
-        Iterator<Map.Entry<HvacKey, TemperatureView>> iterator =
+        Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
                 mTempComponents.entrySet().iterator();
         while (iterator.hasNext()) {
-            Map.Entry<HvacKey, TemperatureView> next = iterator.next();
-            initComponent(next.getValue());
+            Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
+            List<TemperatureView> temperatureViews = next.getValue();
+            for (TemperatureView view : temperatureViews) {
+                initComponent(view);
+            }
         }
     }
 
@@ -155,11 +163,13 @@
             try {
                 int areaId = val.getAreaId();
                 int propertyId = val.getPropertyId();
-                TemperatureView temperatureView = mTempComponents.get(
+                List<TemperatureView> temperatureViews = mTempComponents.get(
                         new HvacKey(propertyId, areaId));
-                if (temperatureView != null) {
+                if (temperatureViews != null && !temperatureViews.isEmpty()) {
                     float value = (float) val.getValue();
-                    temperatureView.setTemp(value);
+                    for (TemperatureView tempView : temperatureViews) {
+                        tempView.setTemp(value);
+                    }
                 } // else the data is not of interest
             } catch (Exception e) {
                 // catch all so we don't take down the sysui if a new data type is
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
index d7b211f..75b41ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
@@ -20,25 +20,30 @@
 import android.util.Property;
 import android.view.View;
 
-import com.android.systemui.statusbar.stack.AnimationProperties;
+import com.android.systemui.R;
 
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 import java.util.function.Function;
 
 /**
  * An animatable property of a view. Used with {@link PropertyAnimator}
  */
-public interface AnimatableProperty {
-    int getAnimationStartTag();
+public abstract class AnimatableProperty {
 
-    int getAnimationEndTag();
+    public static final AnimatableProperty X = AnimatableProperty.from(View.X,
+            R.id.x_animator_tag, R.id.x_animator_tag_start_value, R.id.x_animator_tag_end_value);
+    public static final AnimatableProperty Y = AnimatableProperty.from(View.Y,
+            R.id.y_animator_tag, R.id.y_animator_tag_start_value, R.id.y_animator_tag_end_value);
 
-    int getAnimatorTag();
+    public abstract int getAnimationStartTag();
 
-    Property getProperty();
+    public abstract int getAnimationEndTag();
 
-    static <T extends View> AnimatableProperty from(String name, BiConsumer<T, Float> setter,
+    public abstract int getAnimatorTag();
+
+    public abstract Property getProperty();
+
+    public static <T extends View> AnimatableProperty from(String name, BiConsumer<T, Float> setter,
             Function<T, Float> getter, int animatorTag, int startValueTag, int endValueTag) {
         Property<T, Float> property = new FloatProperty<T>(name) {
 
@@ -74,4 +79,29 @@
             }
         };
     }
+
+    public static <T extends View> AnimatableProperty from(Property<T, Float> property,
+            int animatorTag, int startValueTag, int endValueTag) {
+        return new AnimatableProperty() {
+            @Override
+            public int getAnimationStartTag() {
+                return startValueTag;
+            }
+
+            @Override
+            public int getAnimationEndTag() {
+                return endValueTag;
+            }
+
+            @Override
+            public int getAnimatorTag() {
+                return animatorTag;
+            }
+
+            @Override
+            public Property getProperty() {
+                return property;
+            }
+        };
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index 92dcc9e..2efcd16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -24,6 +24,7 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.statusbar.stack.AnimationFilter;
 import com.android.systemui.statusbar.stack.AnimationProperties;
@@ -115,4 +116,7 @@
         view.setTag(animationEndTag, newEndValue);
     }
 
+    public static <T extends View> boolean isAnimating(T view, AnimatableProperty property) {
+        return  view.getTag(property.getAnimatorTag()) != null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index d9a55c5..042e4ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -20,8 +20,8 @@
 
 import android.content.res.Resources;
 import android.util.MathUtils;
-import com.android.keyguard.KeyguardStatusView;
 
+import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
@@ -47,12 +47,6 @@
     private int mClockNotificationsMargin;
 
     /**
-     * Current height of {@link NotificationPanelView}, considering how much the
-     * user collapsed it.
-     */
-    private float mExpandedHeight;
-
-    /**
      * Height of the parent view - display size in px.
      */
     private int mHeight;
@@ -84,9 +78,9 @@
     private int mContainerTopPadding;
 
     /**
-     * @see NotificationPanelView#getMaxPanelHeight()
+     * @see NotificationPanelView#getExpandedFraction()
      */
-    private float mMaxPanelHeight;
+    private float mPanelExpansion;
 
     /**
      * Burn-in prevention x translation.
@@ -140,13 +134,13 @@
     }
 
     public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
-            float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight,
-            float dark, boolean secure, boolean pulsing, int bouncerTop) {
+            float panelExpansion, int parentHeight,
+            int keyguardStatusHeight, float dark, boolean secure, boolean pulsing,
+            int bouncerTop) {
         mMinTopMargin = minTopMargin + mContainerTopPadding;
         mMaxShadeBottom = maxShadeBottom;
         mNotificationStackHeight = notificationStackHeight;
-        mExpandedHeight = expandedHeight;
-        mMaxPanelHeight = maxPanelHeight;
+        mPanelExpansion = panelExpansion;
         mHeight = parentHeight;
         mKeyguardStatusHeight = keyguardStatusHeight;
         mDarkAmount = dark;
@@ -171,16 +165,12 @@
         return mHeight / 2 - mKeyguardStatusHeight - mClockNotificationsMargin;
     }
 
-    public int getExpandedClockBottom() {
-        return getExpandedClockPosition() + mKeyguardStatusHeight;
-    }
-
     /**
      * Vertically align the clock and the shade in the available space considering only
      * a percentage of the clock height defined by {@code CLOCK_HEIGHT_WEIGHT}.
      * @return Clock Y in pixels.
      */
-    private int getExpandedClockPosition() {
+    public int getExpandedClockPosition() {
         final int availableHeight = mMaxShadeBottom - mMinTopMargin;
         final int containerCenter = mMinTopMargin + availableHeight / 2;
 
@@ -212,8 +202,7 @@
                 mMinTopMargin : -mKeyguardStatusHeight;
 
         // Move clock up while collapsing the shade
-        float shadeExpansion = mExpandedHeight / mMaxPanelHeight;
-        shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(shadeExpansion);
+        float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(mPanelExpansion);
         final float clockY = MathUtils.lerp(clockYTarget, clockYRegular, shadeExpansion);
 
         return (int) MathUtils.lerp(clockY, clockYDark, mDarkAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 04bfcdd..66176b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -544,7 +544,12 @@
 
             // Set visibility, may fail if a11y service is active.
             // If invisible, call will stop animation.
-            mNavigationBarView.setRotateButtonVisibility(true);
+            int appliedVisibility = mNavigationBarView.setRotateButtonVisibility(true);
+            if (appliedVisibility == View.VISIBLE) {
+                // If the button will actually become visible and the navbar is about to hide,
+                // tell the statusbar to keep it around for longer
+                mStatusBar.touchAutoHide();
+            }
 
         } else { // Hide
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index c2053b6..6dbe9f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -766,13 +766,13 @@
         if (setIcon) getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
     }
 
-    public void setRotateButtonVisibility(final boolean visible) {
+    public int setRotateButtonVisibility(final boolean visible) {
         // Never show if a11y is visible
         final boolean adjVisible = visible && !mShowAccessibilityButton;
         final int vis = adjVisible ? View.VISIBLE : View.INVISIBLE;
 
         // No need to do anything if the request matches the current state
-        if (vis == getRotateSuggestionButton().getVisibility()) return;
+        if (vis == getRotateSuggestionButton().getVisibility()) return vis;
 
         getRotateSuggestionButton().setVisibility(vis);
         mShowRotateButton = visible;
@@ -789,6 +789,9 @@
 
         // Hide/restore other button visibility, if necessary
         updateNavButtonIcons();
+
+        // Return applied visibility
+        return vis;
     }
 
     public boolean isRotateButtonVisible() { return mShowRotateButton; }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 351633b..a0a97c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -20,7 +20,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
@@ -44,7 +43,6 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
@@ -69,8 +67,11 @@
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.stack.AnimationProperties;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
@@ -101,6 +102,8 @@
 
     public static final long DOZE_ANIMATION_DURATION = 700;
 
+    private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
+            .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
     private static final FloatProperty<NotificationPanelView> SET_DARK_AMOUNT_PROPERTY =
             new FloatProperty<NotificationPanelView>("mDarkAmount") {
                 @Override
@@ -122,11 +125,10 @@
     private QS mQs;
     private FrameLayout mQsFrame;
     private KeyguardStatusView mKeyguardStatusView;
-    private View mReserveNotificationSpace;
     private View mQsNavbarScrim;
     protected NotificationsQuickSettingsContainer mNotificationContainerParent;
     protected NotificationStackScrollLayout mNotificationStackScroller;
-    private boolean mAnimateNextTopPaddingChange;
+    private boolean mAnimateNextPositionUpdate;
 
     private int mTrackingPointer;
     private VelocityTracker mQsVelocityTracker;
@@ -173,9 +175,6 @@
     private int mUnlockMoveDistance;
     private float mEmptyDragAmount;
 
-    private Animator mClockAnimator;
-    private int mClockAnimationTargetX = Integer.MIN_VALUE;
-    private int mClockAnimationTargetY = Integer.MIN_VALUE;
     private KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
             new KeyguardClockPositionAlgorithm();
     private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
@@ -183,7 +182,6 @@
     private boolean mIsExpanding;
 
     private boolean mBlockTouches;
-    private int mNotificationScrimWaitDistance;
     // Used for two finger gesture as well as accessibility shortcut to QS.
     private boolean mQsExpandImmediate;
     private boolean mTwoFingerQsExpandPossible;
@@ -310,8 +308,6 @@
                 getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance);
         mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance);
         mClockPositionAlgorithm.loadDimens(getResources());
-        mNotificationScrimWaitDistance =
-                getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance);
         mQsFalsingThreshold = getResources().getDimensionPixelSize(
                 R.dimen.qs_falsing_threshold);
         mPositionMinSideMargin = getResources().getDimensionPixelSize(
@@ -461,19 +457,19 @@
      */
     private void positionClockAndNotifications() {
         boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
+        boolean animateClock = animate || mAnimateNextPositionUpdate;
         int stackScrollerPadding;
         if (mStatusBarState != StatusBarState.KEYGUARD) {
             stackScrollerPadding = (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
             +  mQsNotificationTopPadding;
         } else {
-            final int totalHeight = getHeight();
-            final int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
+            int totalHeight = getHeight();
+            int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
             mClockPositionAlgorithm.setup(
                     mStatusBarMinHeight,
                     totalHeight - bottomPadding,
-                    calculatePanelHeightShade() - mNotificationStackScroller.getTopPadding(),
-                    getExpandedHeight(),
-                    getMaxPanelHeight(),
+                    mNotificationStackScroller.getIntrinsicContentHeight(),
+                    getExpandedFraction(),
                     totalHeight,
                     mKeyguardStatusView.getHeight(),
                     mDarkAmount,
@@ -481,12 +477,10 @@
                     mPulsing,
                     mBouncerTop);
             mClockPositionAlgorithm.run(mClockPositionResult);
-            if (animate || mClockAnimator != null) {
-                startClockAnimation(mClockPositionResult.clockX, mClockPositionResult.clockY);
-            } else {
-                mKeyguardStatusView.setX(mClockPositionResult.clockX);
-                mKeyguardStatusView.setY(mClockPositionResult.clockY);
-            }
+            PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
+                    mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
+            PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
+                    mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
             updateClock();
             stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
         }
@@ -497,6 +491,7 @@
         mStackScrollerMeasuringPass++;
         requestScrollerTopPaddingUpdate(animate);
         mStackScrollerMeasuringPass = 0;
+        mAnimateNextPositionUpdate = false;
     }
 
     /**
@@ -558,42 +553,6 @@
         positionClockAndNotifications();
     }
 
-    private void startClockAnimation(int x, int y) {
-        if (mClockAnimationTargetX == x && mClockAnimationTargetY == y) {
-            return;
-        }
-        mClockAnimationTargetX = x;
-        mClockAnimationTargetY = y;
-        getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
-            @Override
-            public boolean onPreDraw() {
-                getViewTreeObserver().removeOnPreDrawListener(this);
-                if (mClockAnimator != null) {
-                    mClockAnimator.removeAllListeners();
-                    mClockAnimator.cancel();
-                }
-                AnimatorSet set = new AnimatorSet();
-                set.play(ObjectAnimator.ofFloat(
-                        mKeyguardStatusView, View.Y, mClockAnimationTargetY))
-                        .with(ObjectAnimator.ofFloat(
-                                mKeyguardStatusView, View.X, mClockAnimationTargetX));
-                mClockAnimator = set;
-                mClockAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-                mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                mClockAnimator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mClockAnimator = null;
-                        mClockAnimationTargetX = Integer.MIN_VALUE;
-                        mClockAnimationTargetY = Integer.MIN_VALUE;
-                    }
-                });
-                mClockAnimator.start();
-                return true;
-            }
-        });
-    }
-
     private void updateClock() {
         if (!mKeyguardStatusViewAnimating) {
             mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
@@ -601,9 +560,9 @@
     }
 
     public void animateToFullShade(long delay) {
-        mAnimateNextTopPaddingChange = true;
         mNotificationStackScroller.goToFullShade(delay);
         requestLayout();
+        mAnimateNextPositionUpdate = true;
     }
 
     public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
@@ -1411,10 +1370,8 @@
 
     protected void requestScrollerTopPaddingUpdate(boolean animate) {
         mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(),
-                mAnimateNextTopPaddingChange || animate,
-                mKeyguardShowing
+                animate, mKeyguardShowing
                         && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted));
-        mAnimateNextTopPaddingChange = false;
     }
 
     private void trackMovement(MotionEvent event) {
@@ -1535,7 +1492,7 @@
         if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
             maxHeight = calculatePanelHeightQsExpanded();
         } else {
-            maxHeight = Math.max(calculatePanelHeightShade(), calculatePanelHeightShadeExpanded());
+            maxHeight = calculatePanelHeightShade();
         }
         maxHeight = Math.max(maxHeight, min);
         return maxHeight;
@@ -1606,14 +1563,15 @@
         int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
         int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
         maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
-        return maxHeight;
-    }
 
-    private int calculatePanelHeightShadeExpanded() {
-        return mNotificationStackScroller.getHeight()
-                - mNotificationStackScroller.getEmptyBottomMargin()
-                - mNotificationStackScroller.getTopPadding()
-                + mClockPositionAlgorithm.getExpandedClockBottom();
+        if (mStatusBarState == StatusBarState.KEYGUARD) {
+            int minKeyguardPanelBottom = mClockPositionAlgorithm.getExpandedClockPosition()
+                    + mKeyguardStatusView.getHeight()
+                    + mNotificationStackScroller.getIntrinsicContentHeight();
+            return Math.max(maxHeight, minKeyguardPanelBottom);
+        } else {
+            return maxHeight;
+        }
     }
 
     private int calculatePanelHeightQsExpanded() {
@@ -1652,7 +1610,8 @@
 
     private void updateNotificationTranslucency() {
         float alpha = 1f;
-        if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp && !mHeadsUpManager.hasPinnedHeadsUp()) {
+        if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp &&
+                !mHeadsUpManager.hasPinnedHeadsUp()) {
             alpha = getFadeoutAlpha();
         }
         mNotificationStackScroller.setAlpha(alpha);
@@ -1907,13 +1866,16 @@
         if (view == null && mQsExpanded) {
             return;
         }
+        if (needsAnimation) {
+            mAnimateNextPositionUpdate = true;
+        }
         ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
         ExpandableNotificationRow firstRow = firstChildNotGone instanceof ExpandableNotificationRow
                 ? (ExpandableNotificationRow) firstChildNotGone
                 : null;
         if (firstRow != null
                 && (view == firstRow || (firstRow.getNotificationParent() == firstRow))) {
-            requestScrollerTopPaddingUpdate(false);
+            requestScrollerTopPaddingUpdate(false /* animate */);
         }
         requestPanelHeightUpdate();
     }
@@ -1965,14 +1927,12 @@
 
     @Override
     public void onClick(View v) {
-        if (v.getId() == R.id.expand_indicator) {
-            onQsExpansionStarted();
-            if (mQsExpanded) {
-                flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */);
-            } else if (mQsExpansionEnabled) {
-                mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
-                flingSettings(0 /* vel */, true /* expand */, null, true /* isClick */);
-            }
+        onQsExpansionStarted();
+        if (mQsExpanded) {
+            flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */);
+        } else if (mQsExpansionEnabled) {
+            mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
+            flingSettings(0 /* vel */, true /* expand */, null, true /* isClick */);
         }
     }
 
@@ -2339,15 +2299,15 @@
             p.setColor(Color.YELLOW);
             canvas.drawLine(0, calculatePanelHeightShade(), getWidth(),
                     calculatePanelHeightShade(), p);
-            p.setColor(Color.GRAY);
-            canvas.drawLine(0, calculatePanelHeightShadeExpanded(), getWidth(),
-                    calculatePanelHeightShadeExpanded(), p);
             p.setColor(Color.MAGENTA);
             canvas.drawLine(0, calculateQsTopPadding(), getWidth(),
                     calculateQsTopPadding(), p);
             p.setColor(Color.CYAN);
-            canvas.drawLine(0, mNotificationStackScroller.getTopPadding(), getWidth(),
+            canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, getWidth(),
                     mNotificationStackScroller.getTopPadding(), p);
+            p.setColor(Color.GRAY);
+            canvas.drawLine(0, mClockPositionResult.clockY, getWidth(),
+                    mClockPositionResult.clockY, p);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 304a499..347a4b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -828,7 +828,7 @@
     }
 
     @Override
-    protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
         mStatusBar.onPanelLaidOut();
         requestPanelHeightUpdate();
@@ -1088,13 +1088,10 @@
         }
         cancelPeek();
         notifyExpandingStarted();
-        startUnlockHintAnimationPhase1(new Runnable() {
-            @Override
-            public void run() {
-                notifyExpandingFinished();
-                onUnlockHintFinished();
-                mHintAnimationRunning = false;
-            }
+        startUnlockHintAnimationPhase1(() -> {
+            notifyExpandingFinished();
+            onUnlockHintFinished();
+            mHintAnimationRunning = false;
         });
         onUnlockHintStarted();
         mHintAnimationRunning = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 17cdf4d..1d64088 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -784,6 +784,12 @@
         // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
         mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
         mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
+        mZenController.addCallback(new ZenModeController.Callback() {
+            @Override
+            public void onZenChanged(int zen) {
+                updateEmptyShadeView();
+            }
+        });
         mActivityLaunchAnimator = new ActivityLaunchAnimator(mStatusBarWindow,
                 this,
                 mNotificationPanel,
@@ -4683,7 +4689,8 @@
             // tapping on a notification, editing QS or being dismissed by
             // FLAG_DISMISS_KEYGUARD_ACTIVITY.
             ScrimState state = mIsOccluded || mNotificationPanel.needsScrimming()
-                    || mStatusBarKeyguardViewManager.willDismissWithAction() ?
+                    || mStatusBarKeyguardViewManager.willDismissWithAction()
+                    || mStatusBarKeyguardViewManager.isFullscreenBouncer() ?
                     ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
             mScrimController.transitionTo(state);
         } else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e207eb0..e63a2e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -165,14 +165,15 @@
         // • The user quickly taps on the display and we show "swipe up to unlock."
         // • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
         // • Full-screen user switcher is displayed.
-        if (mOccluded || mNotificationPanelView.isUnlockHintRunning()
-                || mBouncer.willDismissWithAction()
+        if (mNotificationPanelView.isUnlockHintRunning()) {
+            mBouncer.setExpansion(1);
+        } else if (mOccluded || mBouncer.willDismissWithAction()
                 || mStatusBar.isFullScreenUserSwitcherState()) {
             mBouncer.setExpansion(0);
-        } else if (mShowing && mStatusBar.isKeyguardCurrentlySecure() && !mDozing) {
+        } else if (mShowing && !mDozing) {
             mBouncer.setExpansion(expansion);
-            if (expansion != 1 && tracking && !mBouncer.isShowing()
-                    && !mBouncer.isAnimatingAway()) {
+            if (expansion != 1 && tracking && mStatusBar.isKeyguardCurrentlySecure()
+                    && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
                 mBouncer.show(false /* resetSecuritySelection */, false /* animated */);
             }
         }
@@ -542,6 +543,10 @@
         return mBouncer.isShowing();
     }
 
+    public boolean isFullscreenBouncer() {
+        return mBouncer.isFullscreenBouncer();
+    }
+
     private long getNavBarShowDelay() {
         if (mStatusBar.isKeyguardFadingAway()) {
             return mStatusBar.getKeyguardFadingAwayDelay();
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 b693ebb..42e02d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -21,6 +21,7 @@
 import com.android.systemui.statusbar.policy.BluetoothController.Callback;
 
 import java.util.Collection;
+import java.util.List;
 
 public interface BluetoothController extends CallbackController<Callback>, Dumpable {
     boolean isBluetoothSupported();
@@ -30,7 +31,7 @@
 
     boolean isBluetoothConnected();
     boolean isBluetoothConnecting();
-    String getLastDeviceName();
+    String getConnectedDeviceName();
     void setBluetoothEnabled(boolean enabled);
     Collection<CachedBluetoothDevice> getDevices();
     void connect(CachedBluetoothDevice device);
@@ -39,7 +40,7 @@
 
     int getMaxConnectionState(CachedBluetoothDevice device);
     int getBondState(CachedBluetoothDevice device);
-    CachedBluetoothDevice getLastDevice();
+    List<CachedBluetoothDevice> getConnectedDevices();
 
     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 cd17cfc..44e87ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -31,6 +31,7 @@
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.systemui.Dependency;
 
 import java.io.FileDescriptor;
@@ -38,10 +39,11 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.WeakHashMap;
 
 public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
-        CachedBluetoothDevice.Callback {
+        CachedBluetoothDevice.Callback, LocalBluetoothProfileManager.ServiceListener {
     private static final String TAG = "BluetoothController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -51,10 +53,10 @@
     private final WeakHashMap<CachedBluetoothDevice, ActuallyCachedState> mCachedState =
             new WeakHashMap<>();
     private final Handler mBgHandler;
+    private final List<CachedBluetoothDevice> mConnectedDevices = new ArrayList<>();
 
     private boolean mEnabled;
     private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
-    private CachedBluetoothDevice mLastDevice;
 
     private final H mHandler = new H(Looper.getMainLooper());
     private int mState;
@@ -65,6 +67,7 @@
         if (mLocalBluetoothManager != null) {
             mLocalBluetoothManager.getEventManager().setReceiverHandler(mBgHandler);
             mLocalBluetoothManager.getEventManager().registerCallback(this);
+            mLocalBluetoothManager.getProfileManager().addServiceListener(this);
             onBluetoothStateChanged(
                     mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState());
         }
@@ -88,11 +91,10 @@
         }
         pw.print("  mEnabled="); pw.println(mEnabled);
         pw.print("  mConnectionState="); pw.println(stateToString(mConnectionState));
-        pw.print("  mLastDevice="); pw.println(mLastDevice);
+        pw.print("  mConnectedDevices="); pw.println(mConnectedDevices);
         pw.print("  mCallbacks.size="); pw.println(mHandler.mCallbacks.size());
         pw.println("  Bluetooth Devices:");
-        for (CachedBluetoothDevice device :
-                mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) {
+        for (CachedBluetoothDevice device : getDevices()) {
             pw.println("    " + getDeviceString(device));
         }
     }
@@ -121,8 +123,8 @@
     }
 
     @Override
-    public CachedBluetoothDevice getLastDevice() {
-        return mLastDevice;
+    public List<CachedBluetoothDevice> getConnectedDevices() {
+        return mConnectedDevices;
     }
 
     @Override
@@ -186,8 +188,11 @@
     }
 
     @Override
-    public String getLastDeviceName() {
-        return mLastDevice != null ? mLastDevice.getName() : null;
+    public String getConnectedDeviceName() {
+        if (mConnectedDevices.size() == 1) {
+            return mConnectedDevices.get(0).getName();
+        }
+        return null;
     }
 
     @Override
@@ -200,10 +205,7 @@
     private void updateConnected() {
         // Make sure our connection state is up to date.
         int state = mLocalBluetoothManager.getBluetoothAdapter().getConnectionState();
-        if (mLastDevice != null && !mLastDevice.isConnected()) {
-            // Clear out last device if no longer connected.
-            mLastDevice = null;
-        }
+        mConnectedDevices.clear();
         // If any of the devices are in a higher state than the adapter, move the adapter into
         // that state.
         for (CachedBluetoothDevice device : getDevices()) {
@@ -211,13 +213,12 @@
             if (maxDeviceState > state) {
                 state = maxDeviceState;
             }
-            if (mLastDevice == null && device.isConnected()) {
-                // Set as last connected device only if we don't have one.
-                mLastDevice = device;
+            if (device.isConnected()) {
+                mConnectedDevices.add(device);
             }
         }
 
-        if (mLastDevice == null && state == BluetoothAdapter.STATE_CONNECTED) {
+        if (mConnectedDevices.isEmpty() && state == BluetoothAdapter.STATE_CONNECTED) {
             // If somehow we think we are connected, but have no connected devices, we aren't
             // connected.
             state = BluetoothAdapter.STATE_DISCONNECTED;
@@ -271,7 +272,6 @@
     @Override
     public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
         mCachedState.remove(cachedDevice);
-        mLastDevice = cachedDevice;
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
     }
@@ -293,6 +293,15 @@
         return state;
     }
 
+    @Override
+    public void onServiceConnected() {
+        updateConnected();
+        mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
+    }
+
+    @Override
+    public void onServiceDisconnected() {}
+
     private static class ActuallyCachedState implements Runnable {
 
         private final WeakReference<CachedBluetoothDevice> mDevice;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 1b02e15..22a48f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -269,13 +269,6 @@
                 }
                 if (mCode != 0) {
                     if (doIt) {
-                        // If there was a pending remote recents animation, then we need to
-                        // cancel the animation now before we handle the button itself. In the case
-                        // where we are going home and the recents animation has already started,
-                        // just cancel the recents animation, leaving the home stack in place
-                        boolean isHomeKey = mCode == KEYCODE_HOME;
-                        ActivityManagerWrapper.getInstance().cancelRecentsAnimation(!isHomeKey);
-
                         sendEvent(KeyEvent.ACTION_UP, 0);
                         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
                     } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index bc5a848..b1c0a96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -111,6 +111,7 @@
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.BiConsumer;
 
 /**
@@ -163,6 +164,7 @@
 
     private Paint mDebugPaint;
     private int mContentHeight;
+    private int mIntrinsicContentHeight;
     private int mCollapsedSize;
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
@@ -537,15 +539,16 @@
                 canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
             }
         } else {
-            float animProgress = Interpolators.FAST_OUT_SLOW_IN
-                    .getInterpolation(1f - mDarkAmount);
-            float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN
-                    .getInterpolation((1f - mDarkAmount) * 2);
+            float inverseDark = 1 - mDarkAmount;
+            float yProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(inverseDark);
+            float xProgress = Interpolators.FAST_OUT_SLOW_IN
+                    .getInterpolation(inverseDark * 2f);
+
             mBackgroundAnimationRect.set(
-                    (int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
-                    (int) MathUtils.lerp(darkTop, lockScreenTop, animProgress),
-                    (int) MathUtils.lerp(darkRight, lockScreenRight, sidePaddingsProgress),
-                    (int) MathUtils.lerp(darkBottom, lockScreenBottom, animProgress));
+                    (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
+                    (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
+                    (int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
+                    (int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
             if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
                 canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
                         mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
@@ -627,8 +630,12 @@
     }
 
     private void notifyHeightChangeListener(ExpandableView view) {
+        notifyHeightChangeListener(view, false /* needsAnimation */);
+    }
+
+    private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
         if (mOnHeightChangedListener != null) {
-            mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
+            mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
         }
     }
 
@@ -849,7 +856,7 @@
                 mNeedsAnimation =  true;
             }
             requestChildrenUpdate();
-            notifyHeightChangeListener(null);
+            notifyHeightChangeListener(null, animate);
         }
     }
 
@@ -914,6 +921,13 @@
         updateClipping();
     }
 
+    /**
+     * Return the height of the content ignoring the footer.
+     */
+    public int getIntrinsicContentHeight() {
+        return mIntrinsicContentHeight;
+    }
+
     public void updateClipping() {
         boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1;
         boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
@@ -2129,8 +2143,9 @@
 
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView expandableView = (ExpandableView) getChildAt(i);
+            boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
             if (expandableView.getVisibility() != View.GONE
-                    && !expandableView.hasNoContentHeight()) {
+                    && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
                 boolean limitReached = maxDisplayedNotifications != -1
                         && numShownItems >= maxDisplayedNotifications;
                 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
@@ -2178,6 +2193,7 @@
                 }
             }
         }
+        mIntrinsicContentHeight = height;
         mContentHeight = height + mTopPadding + mBottomMargin;
         updateScrollability();
         clampScrollPosition();
@@ -3655,7 +3671,7 @@
         updateContentHeight();
         updateScrollPositionOnExpandInBottom(view);
         clampScrollPosition();
-        notifyHeightChangeListener(view);
+        notifyHeightChangeListener(view, needsAnimation);
         ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
                 ? (ExpandableNotificationRow) view
                 : null;
@@ -3969,6 +3985,7 @@
                 mShelf.fadeInTranslating();
             }
         }
+        updateAlgorithmHeightAndPadding();
         updateBackgroundDimming();
         updateAntiBurnInTranslation();
         requestChildrenUpdate();
@@ -4039,14 +4056,21 @@
     public void updateEmptyShadeView(boolean visible) {
         int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
         int newVisibility = visible ? VISIBLE : GONE;
-        if (oldVisibility != newVisibility) {
+
+        boolean changedVisibility = oldVisibility != newVisibility;
+        if (changedVisibility || newVisibility != GONE) {
             if (newVisibility != GONE) {
+                int oldText = mEmptyShadeView.getTextResource();
+                int newText;
                 if (mStatusBar.areNotificationsHidden()) {
-                    mEmptyShadeView.setText(R.string.dnd_suppressing_shade_text);
+                    newText = R.string.dnd_suppressing_shade_text;
                 } else {
-                    mEmptyShadeView.setText(R.string.empty_shade_text);
+                    newText = R.string.empty_shade_text;
                 }
-                showFooterView(mEmptyShadeView);
+                if (changedVisibility || !Objects.equals(oldText, newText)) {
+                    mEmptyShadeView.setText(newText);
+                    showFooterView(mEmptyShadeView);
+                }
             } else {
                 hideFooterView(mEmptyShadeView, true);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 4f5ff60..5c7ce59 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -765,9 +765,9 @@
         if (max != row.slider.getMax()) {
             row.slider.setMax(max);
         }
-        // update A11y slider min
+        // update slider min
         final int min = ss.levelMin * 100;
-        if (isA11yStream && min != row.slider.getMin()) {
+        if (min != row.slider.getMin()) {
             row.slider.setMin(min);
         }
 
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 767a24a..1be8322 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -57,6 +57,12 @@
         <service
             android:name="com.android.systemui.qs.external.TileLifecycleManagerTests$FakeTileService"
             android:process=":killable" />
+
+        <receiver android:name="com.android.systemui.SliceBroadcastRelayHandlerTest$Receiver">
+            <intent-filter>
+                <action android:name="com.android.systemui.action.TEST_ACTION" />
+            </intent-filter>
+        </receiver>
     </application>
 
     <instrumentation android:name="android.testing.TestableInstrumentation"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
new file mode 100644
index 0000000..4abac56
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.settingslib.SliceBroadcastRelay;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
+
+    private static final String TEST_ACTION = "com.android.systemui.action.TEST_ACTION";
+
+    @Test
+    public void testRegister() {
+        Uri testUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority("something")
+                .path("test")
+                .build();
+        SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
+        relayHandler.mContext = spy(mContext);
+
+        Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
+        intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+        intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
+                new ComponentName(mContext.getPackageName(), Receiver.class.getName()));
+        IntentFilter value = new IntentFilter(TEST_ACTION);
+        intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
+
+        relayHandler.handleIntent(intent);
+        verify(relayHandler.mContext).registerReceiver(any(), eq(value));
+    }
+
+    @Test
+    public void testUnregister() {
+        Uri testUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority("something")
+                .path("test")
+                .build();
+        SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
+        relayHandler.mContext = spy(mContext);
+
+        Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
+        intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+        intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
+                new ComponentName(mContext.getPackageName(), Receiver.class.getName()));
+        IntentFilter value = new IntentFilter(TEST_ACTION);
+        intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
+
+        relayHandler.handleIntent(intent);
+        ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value));
+
+        intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
+        intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+        relayHandler.handleIntent(intent);
+        verify(relayHandler.mContext).unregisterReceiver(eq(relay.getValue()));
+    }
+
+    @Test
+    public void testRelay() {
+        Receiver.sReceiver = mock(BroadcastReceiver.class);
+        Uri testUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority("something")
+                .path("test")
+                .build();
+        SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
+        relayHandler.mContext = spy(mContext);
+
+        Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
+        intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+        intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
+                new ComponentName(mContext.getPackageName(), Receiver.class.getName()));
+        IntentFilter value = new IntentFilter(TEST_ACTION);
+        intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
+
+        relayHandler.handleIntent(intent);
+        ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value));
+        relay.getValue().onReceive(relayHandler.mContext, new Intent(TEST_ACTION));
+
+        verify(Receiver.sReceiver, timeout(2000)).onReceive(any(), any());
+    }
+
+    public static class Receiver extends BroadcastReceiver {
+        private static BroadcastReceiver sReceiver;
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (sReceiver != null) sReceiver.onReceive(context, intent);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index a153140..f0ca3ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.stack.AnimationProperties;
 import com.android.systemui.statusbar.stack.ViewState;
 
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -227,4 +228,13 @@
         assertNotNull(animator);
         assertTrue(animator.getListeners().contains(mFinishListener));
     }
+
+    @Test
+    public void testIsAnimating() {
+        mAnimationFilter.reset();
+        mAnimationFilter.animate(mProperty.getProperty());
+        assertFalse(PropertyAnimator.isAnimating(mView, mProperty));
+        PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties);
+        assertTrue(PropertyAnimator.isAnimating(mView, mProperty));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 4e7550b..54153a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -35,6 +35,7 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
@@ -68,6 +69,8 @@
         mMockAdapter = mock(LocalBluetoothAdapter.class);
         when(mMockBluetoothManager.getBluetoothAdapter()).thenReturn(mMockAdapter);
         when(mMockBluetoothManager.getEventManager()).thenReturn(mock(BluetoothEventManager.class));
+        when(mMockBluetoothManager.getProfileManager())
+                .thenReturn(mock(LocalBluetoothProfileManager.class));
 
         mBluetoothControllerImpl = new BluetoothControllerImpl(mContext,
                 mTestableLooper.getLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index d30e777..6591715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -136,10 +136,9 @@
     }
 
     protected void setWifiEnabled(boolean enabled) {
-        Intent i = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        i.putExtra(WifiManager.EXTRA_WIFI_STATE,
+        when(mMockWm.getWifiState()).thenReturn(
                 enabled ? WifiManager.WIFI_STATE_ENABLED : WifiManager.WIFI_STATE_DISABLED);
-        mNetworkController.onReceive(mContext, i);
+        mNetworkController.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
     }
 
     protected void setWifiState(boolean connected, String ssid) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index dd2b581..eeb4209 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -141,6 +141,19 @@
     }
 
     @Test
+    public void updateEmptyView_noNotificationsToDndSuppressing() {
+        mStackScroller.setEmptyShadeView(mEmptyShadeView);
+        when(mEmptyShadeView.willBeGone()).thenReturn(true);
+        when(mBar.areNotificationsHidden()).thenReturn(false);
+        mStackScroller.updateEmptyShadeView(true);
+        verify(mEmptyShadeView).setText(R.string.empty_shade_text);
+
+        when(mBar.areNotificationsHidden()).thenReturn(true);
+        mStackScroller.updateEmptyShadeView(true);
+        verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
+    }
+
+    @Test
     @UiThreadTest
     public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
         mStackScroller.setExpandedHeight(0f);
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 44c4983..cac6bf7 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
@@ -21,6 +21,8 @@
 import com.android.systemui.statusbar.policy.BluetoothController.Callback;
 
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
 public class FakeBluetoothController extends BaseLeakChecker<Callback> implements
         BluetoothController {
@@ -55,7 +57,7 @@
     }
 
     @Override
-    public String getLastDeviceName() {
+    public String getConnectedDeviceName() {
         return null;
     }
 
@@ -95,7 +97,7 @@
     }
 
     @Override
-    public CachedBluetoothDevice getLastDevice() {
-        return null;
+    public List<CachedBluetoothDevice> getConnectedDevices() {
+        return Collections.emptyList();
     }
 }
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 1f1ed59..cd2e2a4 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -178,6 +178,16 @@
     TEXT_SELECTION_INVOCATION_LINK = 2;
   }
 
+  // Access method for hidden API events. Type of data tagged with
+  // FIELD_HIDDEN_API_ACCESS_METHOD.
+  // This must be kept in sync with enum AccessMethod in art/runtime/hidden_api.h
+  enum HiddenApiAccessMethod {
+    ACCESS_METHOD_NONE = 0; // never logged, included for completeness
+    ACCESS_METHOD_REFLECTION = 1;
+    ACCESS_METHOD_JNI = 2;
+    ACCESS_METHOD_LINKING = 3; // never logged, included for completeness
+  }
+
   // Known visual elements: views or controls.
   enum View {
     // Unknown view
@@ -5664,6 +5674,25 @@
     // OS: P
     BLUETOOTH_FRAGMENT = 1390;
 
+    // Enclosing category for group of FIELD_HIDDEN_API_FOO events, logged when
+    // an app uses a hidden API.
+    ACTION_HIDDEN_API_ACCESSED = 1391;
+
+    // Tagged data for ACTION_HIDDEN_API_ACCESSED. The metod of the hidden API
+    // access; see enum HiddenApiAccessMethod
+    // OS: P
+    FIELD_HIDDEN_API_ACCESS_METHOD = 1392;
+
+    // Tagged data for ACTION_HIDDEN_API_ACCESSED. Indicates that access was
+    // denied to the API.
+    // OS: P
+    FIELD_HIDDEN_API_ACCESS_DENIED = 1393;
+
+    // Tagged data for ACTION_HIDDEN_API_ACCESSED. The signature of the hidden
+    // API that was accessed.
+    // OS: P
+    FIELD_HIDDEN_API_SIGNATURE = 1394;
+
     // This value should never appear in log outputs - it is reserved for
     // internal platform metrics use.
     NOTIFICATION_SHADE_COUNT = 1395;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 06707da..e6b2a35 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -271,7 +271,8 @@
 
                 // Sanitize structure before it's sent to service.
                 final ComponentName componentNameFromApp = structure.getActivityComponent();
-                if (!mComponentName.equals(componentNameFromApp)) {
+                if (componentNameFromApp == null || !mComponentName.getPackageName()
+                        .equals(componentNameFromApp.getPackageName())) {
                     Slog.w(TAG, "Activity " + mComponentName + " forged different component on "
                             + "AssistStructure: " + componentNameFromApp);
                     structure.setActivityComponent(mComponentName);
@@ -2150,11 +2151,8 @@
             if (saveTriggerId != null) {
                 writeLog(MetricsEvent.AUTOFILL_EXPLICIT_SAVE_TRIGGER_DEFINITION);
             }
-            int flags = saveInfo.getFlags();
-            if (mCompatMode) {
-                flags |= SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE;
-            }
-            mSaveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
+            mSaveOnAllViewsInvisible =
+                    (saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
 
             // We only need to track views if we want to save once they become invisible.
             if (mSaveOnAllViewsInvisible) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index f678eed..5b446ca 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3950,6 +3950,7 @@
         if (mSwitchingDialog != null) {
             mSwitchingDialog.dismiss();
             mSwitchingDialog = null;
+            mSwitchingDialogTitleView = null;
         }
 
         updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 99e0459..6c35bda 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -25,11 +25,9 @@
 import static android.os.storage.OnObbStateChangeListener.MOUNTED;
 import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
 
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
 import static com.android.internal.util.XmlUtils.readStringAttribute;
-import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -249,7 +247,6 @@
     private static final String TAG_VOLUMES = "volumes";
     private static final String ATTR_VERSION = "version";
     private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
-    private static final String ATTR_FORCE_ADOPTABLE = "forceAdoptable";
     private static final String TAG_VOLUME = "volume";
     private static final String ATTR_TYPE = "type";
     private static final String ATTR_FS_UUID = "fsUuid";
@@ -287,8 +284,6 @@
     private ArrayMap<String, VolumeRecord> mRecords = new ArrayMap<>();
     @GuardedBy("mLock")
     private String mPrimaryStorageUuid;
-    @GuardedBy("mLock")
-    private boolean mForceAdoptable;
 
     /** Map from disk ID to latches */
     @GuardedBy("mLock")
@@ -1011,9 +1006,14 @@
         @Override
         public void onDiskCreated(String diskId, int flags) {
             synchronized (mLock) {
-                if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
-                        || mForceAdoptable) {
-                    flags |= DiskInfo.FLAG_ADOPTABLE;
+                final String value = SystemProperties.get(StorageManager.PROP_ADOPTABLE);
+                switch (value) {
+                    case "force_on":
+                        flags |= DiskInfo.FLAG_ADOPTABLE;
+                        break;
+                    case "force_off":
+                        flags &= ~DiskInfo.FLAG_ADOPTABLE;
+                        break;
                 }
                 mDisks.put(diskId, new DiskInfo(diskId, flags));
             }
@@ -1530,7 +1530,6 @@
     private void readSettingsLocked() {
         mRecords.clear();
         mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
-        mForceAdoptable = false;
 
         FileInputStream fis = null;
         try {
@@ -1552,7 +1551,6 @@
                             mPrimaryStorageUuid = readStringAttribute(in,
                                     ATTR_PRIMARY_STORAGE_UUID);
                         }
-                        mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false);
 
                     } else if (TAG_VOLUME.equals(tag)) {
                         final VolumeRecord rec = readVolumeRecord(in);
@@ -1583,7 +1581,6 @@
             out.startTag(null, TAG_VOLUMES);
             writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
             writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
-            writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable);
             final int size = mRecords.size();
             for (int i = 0; i < size; i++) {
                 final VolumeRecord rec = mRecords.valueAt(i);
@@ -1980,12 +1977,25 @@
             }
         }
 
-        if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
-            synchronized (mLock) {
-                mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
+        if ((mask & (StorageManager.DEBUG_ADOPTABLE_FORCE_ON
+                | StorageManager.DEBUG_ADOPTABLE_FORCE_OFF)) != 0) {
+            final String value;
+            if ((flags & StorageManager.DEBUG_ADOPTABLE_FORCE_ON) != 0) {
+                value = "force_on";
+            } else if ((flags & StorageManager.DEBUG_ADOPTABLE_FORCE_OFF) != 0) {
+                value = "force_off";
+            } else {
+                value = "";
+            }
 
-                writeSettingsLocked();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                SystemProperties.set(StorageManager.PROP_ADOPTABLE, value);
+
+                // Reset storage to kick new setting into place
                 mHandler.obtainMessage(H_RESET).sendToTarget();
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
 
@@ -3564,8 +3574,6 @@
                 pw.print(DataUnit.MEBIBYTES.toBytes(pair.second));
                 pw.println(" MiB)");
             }
-            pw.println("Force adoptable: " + mForceAdoptable);
-            pw.println();
             pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers));
             pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
         }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index fc047bc..228171f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -24,15 +24,19 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Predicate;
 
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.app.ServiceStartArgs;
+import android.content.ComponentName.WithComponentName;
 import android.content.IIntentSender;
 import android.content.IntentSender;
 import android.content.pm.ParceledListSlice;
@@ -55,6 +59,8 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.TransferPipe;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.server.AppStateTracker;
 import com.android.server.LocalServices;
@@ -4063,57 +4069,26 @@
      *  - the first arg isn't the flattened component name of an existing service:
      *    dump all services whose component contains the first arg as a substring
      */
-    protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, String[] args,
-            int opti, boolean dumpAll) {
-        ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+    protected boolean dumpService(FileDescriptor fd, PrintWriter pw, final String name,
+            String[] args, int opti, boolean dumpAll) {
+        final ArrayList<ServiceRecord> services = new ArrayList<>();
+
+        final Predicate<ServiceRecord> filter = DumpUtils.filterRecord(name);
 
         synchronized (mAm) {
             int[] users = mAm.mUserController.getUsers();
-            if ("all".equals(name)) {
-                for (int user : users) {
-                    ServiceMap smap = mServiceMap.get(user);
-                    if (smap == null) {
-                        continue;
-                    }
-                    ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
-                    for (int i=0; i<alls.size(); i++) {
-                        ServiceRecord r1 = alls.valueAt(i);
-                        services.add(r1);
-                    }
-                }
-            } else {
-                ComponentName componentName = name != null
-                        ? ComponentName.unflattenFromString(name) : null;
-                int objectId = 0;
-                if (componentName == null) {
-                    // Not a '/' separated full component name; maybe an object ID?
-                    try {
-                        objectId = Integer.parseInt(name, 16);
-                        name = null;
-                        componentName = null;
-                    } catch (RuntimeException e) {
-                    }
-                }
 
-                for (int user : users) {
-                    ServiceMap smap = mServiceMap.get(user);
-                    if (smap == null) {
-                        continue;
-                    }
-                    ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
-                    for (int i=0; i<alls.size(); i++) {
-                        ServiceRecord r1 = alls.valueAt(i);
-                        if (componentName != null) {
-                            if (r1.name.equals(componentName)) {
-                                services.add(r1);
-                            }
-                        } else if (name != null) {
-                            if (r1.name.flattenToString().contains(name)) {
-                                services.add(r1);
-                            }
-                        } else if (System.identityHashCode(r1) == objectId) {
-                            services.add(r1);
-                        }
+            for (int user : users) {
+                ServiceMap smap = mServiceMap.get(user);
+                if (smap == null) {
+                    continue;
+                }
+                ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
+                for (int i=0; i<alls.size(); i++) {
+                    ServiceRecord r1 = alls.valueAt(i);
+
+                    if (filter.test(r1)) {
+                        services.add(r1);
                     }
                 }
             }
@@ -4123,6 +4098,9 @@
             return false;
         }
 
+        // Sort by component name.
+        services.sort(Comparator.comparing(WithComponentName::getComponentName));
+
         boolean needSep = false;
         for (int i=0; i<services.size(); i++) {
             if (needSep) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e85351b..62a055d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2946,7 +2946,11 @@
                             ? Collections.emptyList()
                             : Arrays.asList(exemptions.split(","));
                 }
-                zygoteProcess.setApiBlacklistExemptions(mExemptions);
+                if (!zygoteProcess.setApiBlacklistExemptions(mExemptions)) {
+                  Slog.e(TAG, "Failed to set API blacklist exemptions!");
+                  // leave mExemptionsStr as is, so we don't try to send the same list again.
+                  mExemptions = Collections.emptyList();
+                }
             }
             int logSampleRate = Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, -1);
@@ -11447,6 +11451,19 @@
     }
 
     @Override
+    public void setSplitScreenResizing(boolean resizing) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                mStackSupervisor.setSplitScreenResizing(resizing);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
     public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
         final long ident = Binder.clearCallingIdentity();
@@ -26629,6 +26646,11 @@
         }
 
         @Override
+        public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+            ActivityManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition);
+        }
+
+        @Override
         public boolean isUidActive(int uid) {
             synchronized (ActivityManagerService.this) {
                 final UidRecord uidRec = mActiveUids.get(uid);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e20356f..95bae2e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1743,6 +1743,11 @@
         return getDisplay().isTopStack(this);
     }
 
+    boolean isTopActivityVisible() {
+        final ActivityRecord topActivity = getTopActivity();
+        return topActivity != null && topActivity.visible;
+    }
+
     /**
      * Returns true if the stack should be visible.
      *
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6a3587c..0dc2445 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -43,6 +43,7 @@
 import static android.app.WindowConfiguration.windowingModeToString;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.graphics.Rect.copyOrNull;
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
@@ -245,6 +246,20 @@
     // the activity callback indicating that it has completed pausing
     static final boolean PAUSE_IMMEDIATELY = true;
 
+    /** True if the docked stack is currently being resized. */
+    private boolean mDockedStackResizing;
+
+    /**
+     * True if there are pending docked bounds that need to be applied after
+     * {@link #mDockedStackResizing} is reset to false.
+     */
+    private boolean mHasPendingDockedBounds;
+    private Rect mPendingDockedBounds;
+    private Rect mPendingTempDockedTaskBounds;
+    private Rect mPendingTempDockedTaskInsetBounds;
+    private Rect mPendingTempOtherTaskBounds;
+    private Rect mPendingTempOtherTaskInsetBounds;
+
     /**
      * The modes which affect which tasks are returned when calling
      * {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
@@ -2708,6 +2723,28 @@
                 moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop));
     }
 
+    void setSplitScreenResizing(boolean resizing) {
+        if (resizing == mDockedStackResizing) {
+            return;
+        }
+
+        mDockedStackResizing = resizing;
+        mWindowManager.setDockedStackResizing(resizing);
+
+        if (!resizing && mHasPendingDockedBounds) {
+            resizeDockedStackLocked(mPendingDockedBounds, mPendingTempDockedTaskBounds,
+                    mPendingTempDockedTaskInsetBounds, mPendingTempOtherTaskBounds,
+                    mPendingTempOtherTaskInsetBounds, PRESERVE_WINDOWS);
+
+            mHasPendingDockedBounds = false;
+            mPendingDockedBounds = null;
+            mPendingTempDockedTaskBounds = null;
+            mPendingTempDockedTaskInsetBounds = null;
+            mPendingTempOtherTaskBounds = null;
+            mPendingTempOtherTaskInsetBounds = null;
+        }
+    }
+
     void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
             Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
             boolean preserveWindows) {
@@ -2731,6 +2768,15 @@
             return;
         }
 
+        if (mDockedStackResizing) {
+            mHasPendingDockedBounds = true;
+            mPendingDockedBounds = copyOrNull(dockedBounds);
+            mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds);
+            mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds);
+            mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds);
+            mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
+        }
+
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
         mWindowManager.deferSurfaceLayout();
         try {
@@ -2765,6 +2811,11 @@
                     if (!current.affectedBySplitScreenResize()) {
                         continue;
                     }
+                    if (mDockedStackResizing && !current.isTopActivityVisible()) {
+                        // Non-visible stacks get resized once we're done with the resize
+                        // interaction.
+                        continue;
+                    }
                     // Need to set windowing mode here before we try to get the dock bounds.
                     current.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
                     current.getStackDockedModeBounds(
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 7b9b659..cd39bcd 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -32,7 +32,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 
-final class ContentProviderRecord {
+final class ContentProviderRecord implements ComponentName.WithComponentName {
     final ActivityManagerService service;
     public final ProviderInfo info;
     final int uid;
@@ -260,4 +260,8 @@
             }
         }
     }
+
+    public ComponentName getComponentName() {
+        return name;
+    }
 }
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 40b9e4f..ed891df 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -134,6 +134,8 @@
 30059 am_on_start_called (User|1|5),(Component Name|3),(Reason|3)
 # The activity's onDestroy has been called.
 30060 am_on_destroy_called (User|1|5),(Component Name|3),(Reason|3)
+# The activity's onActivityResult has been called.
+30062 am_on_activity_result_called (User|1|5),(Component Name|3),(Reason|3)
 
 # The task is being removed from its parent stack
 30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 8a905f8..2f52002 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -17,22 +17,28 @@
 package com.android.server.am;
 
 import android.content.ComponentName;
+import android.content.ComponentName.WithComponentName;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Slog;
 import android.util.SparseArray;
 import com.android.internal.os.TransferPipe;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DumpUtils;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * Keeps track of content providers by authority (name) and class. It separates the mapping by
@@ -325,7 +331,9 @@
 
     private ArrayList<ContentProviderRecord> getProvidersForName(String name) {
         ArrayList<ContentProviderRecord> allProviders = new ArrayList<ContentProviderRecord>();
-        ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
+        final ArrayList<ContentProviderRecord> ret = new ArrayList<>();
+
+        final Predicate<ContentProviderRecord> filter = DumpUtils.filterRecord(name);
 
         synchronized (mAm) {
             allProviders.addAll(mSingletonByClass.values());
@@ -333,39 +341,11 @@
                 allProviders.addAll(mProvidersByClassPerUser.valueAt(i).values());
             }
 
-            if ("all".equals(name)) {
-                providers.addAll(allProviders);
-            } else {
-                ComponentName componentName = name != null
-                        ? ComponentName.unflattenFromString(name) : null;
-                int objectId = 0;
-                if (componentName == null) {
-                    // Not a '/' separated full component name; maybe an object ID?
-                    try {
-                        objectId = Integer.parseInt(name, 16);
-                        name = null;
-                        componentName = null;
-                    } catch (RuntimeException e) {
-                    }
-                }
-
-                for (int i=0; i<allProviders.size(); i++) {
-                    ContentProviderRecord r1 = allProviders.get(i);
-                    if (componentName != null) {
-                        if (r1.name.equals(componentName)) {
-                            providers.add(r1);
-                        }
-                    } else if (name != null) {
-                        if (r1.name.flattenToString().contains(name)) {
-                            providers.add(r1);
-                        }
-                    } else if (System.identityHashCode(r1) == objectId) {
-                        providers.add(r1);
-                    }
-                }
-            }
+            CollectionUtils.addIf(allProviders, ret, filter);
         }
-        return providers;
+        // Sort by component name.
+        ret.sort(Comparator.comparing(WithComponentName::getComponentName));
+        return ret;
     }
 
     protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8a174ed..32887e4 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -56,7 +56,7 @@
 /**
  * A running application service.
  */
-final class ServiceRecord extends Binder {
+final class ServiceRecord extends Binder implements ComponentName.WithComponentName {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ServiceRecord" : TAG_AM;
 
     // Maximum number of delivery attempts before giving up.
@@ -757,4 +757,8 @@
             .append(' ').append(shortName).append('}');
         return stringName = sb.toString();
     }
+
+    public ComponentName getComponentName() {
+        return name;
+    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 8212463..cc3a489 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5971,6 +5971,8 @@
                 mConnectedDevices.remove(deviceKey);
                 return true;
             }
+            Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey + ", deviceSpec="
+                       + deviceSpec + ", connect=" + connect);
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 2845383..c81624a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -822,9 +822,9 @@
         private void resolveStrictModeHostname() {
             try {
                 // Do a blocking DNS resolution using the network-assigned nameservers.
-                mPrivateDnsConfig = new PrivateDnsConfig(
-                        mPrivateDnsProviderHostname,
-                        mNetwork.getAllByName(mPrivateDnsProviderHostname));
+                final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(
+                        mNetwork, mPrivateDnsProviderHostname);
+                mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips);
             } catch (UnknownHostException uhe) {
                 mPrivateDnsConfig = null;
             }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index decae18..a55870f 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1980,6 +1980,9 @@
     }
 
     static String formatTime(long time) {
+        if (time == 0) {
+            return "N/A";
+        }
         Time tobj = new Time();
         tobj.set(time);
         return tobj.format("%Y-%m-%d %H:%M:%S");
@@ -2334,13 +2337,28 @@
             pw.print("]");
             pw.println();
 
+            pw.println("    Per source last syncs:");
+            for (int j = 0; j < SyncStorageEngine.SOURCES.length; j++) {
+                pw.print("      ");
+                pw.print(String.format("%8s", SyncStorageEngine.SOURCES[j]));
+                pw.print("  Success: ");
+                pw.print(formatTime(event.second.perSourceLastSuccessTimes[j]));
+
+                pw.print("  Failure: ");
+                pw.println(formatTime(event.second.perSourceLastFailureTimes[j]));
+            }
+
+            pw.println("    Last syncs:");
             for (int j = 0; j < event.second.getEventCount(); j++) {
-                pw.print("    ");
+                pw.print("      ");
                 pw.print(formatTime(event.second.getEventTime(j)));
                 pw.print(' ');
                 pw.print(event.second.getEvent(j));
                 pw.println();
             }
+            if (event.second.getEventCount() == 0) {
+                pw.println("      N/A");
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index f54a9a0..6a343f8 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -128,8 +128,13 @@
 
     public static final long NOT_IN_BACKOFF_MODE = -1;
 
-    /** String names for the sync source types. */
-    public static final String[] SOURCES = { "OTHER",
+    /**
+     * String names for the sync source types.
+     *
+     * KEEP THIS AND {@link SyncStatusInfo#SOURCE_COUNT} IN SYNC.
+     */
+    public static final String[] SOURCES = {
+            "OTHER",
             "LOCAL",
             "POLL",
             "USER",
@@ -1231,12 +1236,7 @@
                 if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
                     writeStatusNow = true;
                 }
-                status.lastSuccessTime = lastSyncTime;
-                status.lastSuccessSource = item.source;
-                status.lastFailureTime = 0;
-                status.lastFailureSource = -1;
-                status.lastFailureMesg = null;
-                status.initialFailureTime = 0;
+                status.setLastSuccess(item.source, lastSyncTime);
                 ds.successCount++;
                 ds.successTime += elapsedTime;
             } else if (!MESG_CANCELED.equals(resultMessage)) {
@@ -1246,12 +1246,8 @@
                 status.totalStats.numFailures++;
                 status.todayStats.numFailures++;
 
-                status.lastFailureTime = lastSyncTime;
-                status.lastFailureSource = item.source;
-                status.lastFailureMesg = resultMessage;
-                if (status.initialFailureTime == 0) {
-                    status.initialFailureTime = lastSyncTime;
-                }
+                status.setLastFailure(item.source, lastSyncTime, resultMessage);
+
                 ds.failureCount++;
                 ds.failureTime += elapsedTime;
             } else {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 6490964..61d67b7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -17,6 +17,7 @@
 package com.android.server.net;
 
 import android.net.Network;
+import android.net.NetworkTemplate;
 import android.telephony.SubscriptionPlan;
 
 import java.util.Set;
@@ -58,6 +59,11 @@
      */
     public abstract SubscriptionPlan getSubscriptionPlan(Network network);
 
+    /**
+     * Return the active {@link SubscriptionPlan} for the given template.
+     */
+    public abstract SubscriptionPlan getSubscriptionPlan(NetworkTemplate template);
+
     public static final int QUOTA_TYPE_JOBS = 1;
     public static final int QUOTA_TYPE_MULTIPATH = 2;
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5b8e8c0..5bd7c0b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -189,6 +189,7 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -198,6 +199,7 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.DataUnit;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Range;
@@ -228,6 +230,7 @@
 import com.android.server.SystemConfig;
 
 import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
@@ -249,6 +252,7 @@
 import java.time.ZonedDateTime;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.List;
 import java.util.Objects;
@@ -415,7 +419,6 @@
     final Object mNetworkPoliciesSecondLock = new Object();
 
     @GuardedBy("allLocks") volatile boolean mSystemReady;
-    volatile boolean mSystemReallyReady;
 
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground;
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower;
@@ -510,7 +513,6 @@
     /** Map from network ID to last observed meteredness state */
     @GuardedBy("mNetworkPoliciesSecondLock")
     private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray();
-
     /** Map from network ID to last observed roaming state */
     @GuardedBy("mNetworkPoliciesSecondLock")
     private final SparseBooleanArray mNetworkRoaming = new SparseBooleanArray();
@@ -519,6 +521,13 @@
     @GuardedBy("mNetworkPoliciesSecondLock")
     private final SparseIntArray mNetIdToSubId = new SparseIntArray();
 
+    /** Map from subId to subscriberId as of last update */
+    @GuardedBy("mNetworkPoliciesSecondLock")
+    private final SparseArray<String> mSubIdToSubscriberId = new SparseArray<>();
+    /** Set of all merged subscriberId as of last update */
+    @GuardedBy("mNetworkPoliciesSecondLock")
+    private String[] mMergedSubscriberIds = EmptyArray.STRING;
+
     /**
      * Indicates the uids restricted by admin from accessing metered data. It's a mapping from
      * userId to restricted uids which belong to that user.
@@ -843,6 +852,16 @@
                     new NetworkRequest.Builder().build(), mNetworkCallback);
 
             mUsageStats.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
+
+            // Listen for subscriber changes
+            mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener(
+                    new OnSubscriptionsChangedListener(mHandler.getLooper()) {
+                        @Override
+                        public void onSubscriptionsChanged() {
+                            updateNetworksInternal();
+                        }
+                    });
+
             // tell systemReady() that the service has been initialized
             initCompleteSignal.countDown();
         } finally {
@@ -868,7 +887,6 @@
             Thread.currentThread().interrupt();
             throw new IllegalStateException("Service " + TAG + " init interrupted", e);
         }
-        mSystemReallyReady = true;
     }
 
     final private IUidObserver mUidObserver = new IUidObserver.Stub() {
@@ -1114,7 +1132,7 @@
         final long now = mClock.millis();
         for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
             final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
-            final int subId = findRelevantSubId(policy.template);
+            final int subId = findRelevantSubIdNL(policy.template);
 
             // ignore policies that aren't relevant to user
             if (subId == INVALID_SUBSCRIPTION_ID) continue;
@@ -1245,14 +1263,11 @@
      * @return relevant subId, or {@link #INVALID_SUBSCRIPTION_ID} when no
      *         matching subId found.
      */
-    private int findRelevantSubId(NetworkTemplate template) {
-        final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
-        final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
-
+    private int findRelevantSubIdNL(NetworkTemplate template) {
         // Mobile template is relevant when any active subscriber matches
-        final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
-        for (int subId : subIds) {
-            final String subscriberId = tele.getSubscriberId(subId);
+        for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
+            final int subId = mSubIdToSubscriberId.keyAt(i);
+            final String subscriberId = mSubIdToSubscriberId.valueAt(i);
             final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
                     TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
                     true);
@@ -1407,23 +1422,29 @@
         public void onReceive(Context context, Intent intent) {
             // on background handler thread, and verified CONNECTIVITY_INTERNAL
             // permission above.
-
-            if (!mSystemReallyReady) return;
-            synchronized (mUidRulesFirstLock) {
-                synchronized (mNetworkPoliciesSecondLock) {
-                    ensureActiveMobilePolicyAL();
-                    normalizePoliciesNL();
-                    updateNetworkEnabledNL();
-                    updateNetworkRulesNL();
-                    updateNotificationsNL();
-                }
-            }
+            updateNetworksInternal();
         }
     };
 
+    private void updateNetworksInternal() {
+        // Get all of our cross-process communication with telephony out of
+        // the way before we acquire internal locks.
+        updateSubscriptions();
+
+        synchronized (mUidRulesFirstLock) {
+            synchronized (mNetworkPoliciesSecondLock) {
+                ensureActiveMobilePolicyAL();
+                normalizePoliciesNL();
+                updateNetworkEnabledNL();
+                updateNetworkRulesNL();
+                updateNotificationsNL();
+            }
+        }
+    }
+
     @VisibleForTesting
     public void updateNetworks() throws InterruptedException {
-        mConnReceiver.onReceive(null, null);
+        updateNetworksInternal();
         final CountDownLatch latch = new CountDownLatch(1);
         mHandler.post(() -> {
             latch.countDown();
@@ -1438,14 +1459,11 @@
      * @param subId that has its associated NetworkPolicy updated if necessary
      * @return if any policies were updated
      */
-    private boolean maybeUpdateMobilePolicyCycleAL(int subId) {
+    private boolean maybeUpdateMobilePolicyCycleAL(int subId, String subscriberId) {
         if (LOGV) Slog.v(TAG, "maybeUpdateMobilePolicyCycleAL()");
 
-        boolean policyUpdated = false;
-        final String subscriberId = mContext.getSystemService(TelephonyManager.class)
-                .getSubscriberId(subId);
-
         // find and update the mobile NetworkPolicy for this subscriber id
+        boolean policyUpdated = false;
         final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
                 TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true);
         for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
@@ -1568,15 +1586,21 @@
                 return;
             }
             final int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, -1);
-            final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
-            final String subscriberId = tele.getSubscriberId(subId);
+
+            // Get all of our cross-process communication with telephony out of
+            // the way before we acquire internal locks.
+            updateSubscriptions();
 
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
-                    final boolean added = ensureActiveMobilePolicyAL(subId, subscriberId);
-                    if (added) return;
-                    final boolean updated = maybeUpdateMobilePolicyCycleAL(subId);
-                    if (!updated) return;
+                    final String subscriberId = mSubIdToSubscriberId.get(subId, null);
+                    if (subscriberId != null) {
+                        ensureActiveMobilePolicyAL(subId, subscriberId);
+                        maybeUpdateMobilePolicyCycleAL(subId, subscriberId);
+                    } else {
+                        Slog.wtf(TAG, "Missing subscriberId for subId " + subId);
+                    }
+
                     // update network and notification rules, as the data cycle changed and it's
                     // possible that we should be triggering warnings/limits now
                     handleNetworkPoliciesUpdateAL(true);
@@ -1659,20 +1683,29 @@
         if (template.getMatchRule() == MATCH_MOBILE) {
             // If mobile data usage hits the limit or if the user resumes the data, we need to
             // notify telephony.
-            final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
-            final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
 
-            final int[] subIds = ArrayUtils.defeatNullable(sm.getActiveSubscriptionIdList());
-            for (int subId : subIds) {
-                final String subscriberId = tm.getSubscriberId(subId);
-                final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                        TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
-                        true);
-                // Template is matched when subscriber id matches.
-                if (template.matches(probeIdent)) {
-                    tm.setPolicyDataEnabled(enabled, subId);
+            final IntArray matchingSubIds = new IntArray();
+            synchronized (mNetworkPoliciesSecondLock) {
+                for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
+                    final int subId = mSubIdToSubscriberId.keyAt(i);
+                    final String subscriberId = mSubIdToSubscriberId.valueAt(i);
+
+                    final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
+                            TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
+                            true);
+                    // Template is matched when subscriber id matches.
+                    if (template.matches(probeIdent)) {
+                        matchingSubIds.add(subId);
+                    }
                 }
             }
+
+            // Only talk with telephony outside of locks
+            final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            for (int i = 0; i < matchingSubIds.size(); i++) {
+                final int subId = matchingSubIds.get(i);
+                tm.setPolicyDataEnabled(enabled, subId);
+            }
         }
     }
 
@@ -1693,6 +1726,46 @@
     }
 
     /**
+     * Examine all currently active subscriptions from
+     * {@link SubscriptionManager#getActiveSubscriptionIdList()} and update
+     * internal data structures.
+     * <p>
+     * Callers <em>must not</em> hold any locks when this method called.
+     */
+    void updateSubscriptions() {
+        if (LOGV) Slog.v(TAG, "updateSubscriptions()");
+        Trace.traceBegin(TRACE_TAG_NETWORK, "updateSubscriptions");
+
+        final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
+
+        final int[] subIds = ArrayUtils.defeatNullable(sm.getActiveSubscriptionIdList());
+        final String[] mergedSubscriberIds = ArrayUtils.defeatNullable(tm.getMergedSubscriberIds());
+
+        final SparseArray<String> subIdToSubscriberId = new SparseArray<>(subIds.length);
+        for (int subId : subIds) {
+            final String subscriberId = tm.getSubscriberId(subId);
+            if (!TextUtils.isEmpty(subscriberId)) {
+                subIdToSubscriberId.put(subId, subscriberId);
+            } else {
+                Slog.wtf(TAG, "Missing subscriberId for subId " + subId);
+            }
+        }
+
+        synchronized (mNetworkPoliciesSecondLock) {
+            mSubIdToSubscriberId.clear();
+            for (int i = 0; i < subIdToSubscriberId.size(); i++) {
+                mSubIdToSubscriberId.put(subIdToSubscriberId.keyAt(i),
+                        subIdToSubscriberId.valueAt(i));
+            }
+
+            mMergedSubscriberIds = mergedSubscriberIds;
+        }
+
+        Trace.traceEnd(TRACE_TAG_NETWORK);
+    }
+
+    /**
      * 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.
@@ -1885,12 +1958,10 @@
         if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyAL()");
         if (mSuppressDefaultPolicy) return;
 
-        final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
-        final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
+        for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
+            final int subId = mSubIdToSubscriberId.keyAt(i);
+            final String subscriberId = mSubIdToSubscriberId.valueAt(i);
 
-        final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
-        for (int subId : subIds) {
-            final String subscriberId = tele.getSubscriberId(subId);
             ensureActiveMobilePolicyAL(subId, subscriberId);
         }
     }
@@ -2633,14 +2704,11 @@
     }
 
     private void normalizePoliciesNL(NetworkPolicy[] policies) {
-        final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
-        final String[] merged = tele.getMergedSubscriberIds();
-
         mNetworkPolicy.clear();
         for (NetworkPolicy policy : policies) {
             // When two normalized templates conflict, prefer the most
             // restrictive policy
-            policy.template = NetworkTemplate.normalize(policy.template, merged);
+            policy.template = NetworkTemplate.normalize(policy.template, mMergedSubscriberIds);
             final NetworkPolicy existing = mNetworkPolicy.get(policy.template);
             if (existing == null || existing.compareTo(policy) > 0) {
                 if (existing != null) {
@@ -3092,10 +3160,14 @@
                     mSubscriptionPlans.put(subId, plans);
                     mSubscriptionPlansOwner.put(subId, callingPackage);
 
-                    final String subscriberId = mContext.getSystemService(TelephonyManager.class)
-                            .getSubscriberId(subId);
-                    ensureActiveMobilePolicyAL(subId, subscriberId);
-                    maybeUpdateMobilePolicyCycleAL(subId);
+                    final String subscriberId = mSubIdToSubscriberId.get(subId, null);
+                    if (subscriberId != null) {
+                        ensureActiveMobilePolicyAL(subId, subscriberId);
+                        maybeUpdateMobilePolicyCycleAL(subId, subscriberId);
+                    } else {
+                        Slog.wtf(TAG, "Missing subscriberId for subId " + subId);
+                    }
+
                     handleNetworkPoliciesUpdateAL(true);
                 }
             }
@@ -3213,6 +3285,21 @@
                 fout.decreaseIndent();
 
                 fout.println();
+                fout.println("Active subscriptions:");
+                fout.increaseIndent();
+                for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
+                    final int subId = mSubIdToSubscriberId.keyAt(i);
+                    final String subscriberId = mSubIdToSubscriberId.valueAt(i);
+
+                    fout.println(subId + "=" + NetworkIdentity.scrubSubscriberId(subscriberId));
+                }
+                fout.decreaseIndent();
+
+                fout.println();
+                fout.println("Merged subscriptions: "
+                        + Arrays.toString(NetworkIdentity.scrubSubscriberId(mMergedSubscriberIds)));
+
+                fout.println();
                 fout.println("Policy for UIDs:");
                 fout.increaseIndent();
                 int size = mUidPolicy.size();
@@ -4810,6 +4897,14 @@
         }
 
         @Override
+        public SubscriptionPlan getSubscriptionPlan(NetworkTemplate template) {
+            synchronized (mNetworkPoliciesSecondLock) {
+                final int subId = findRelevantSubIdNL(template);
+                return getPrimarySubscriptionPlanLocked(subId);
+            }
+        }
+
+        @Override
         public long getSubscriptionOpportunisticQuota(Network network, int quotaType) {
             final long quotaBytes;
             synchronized (mNetworkPoliciesSecondLock) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 7ee17bc..9ef6c66 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -116,7 +116,6 @@
 import android.provider.Settings.Global;
 import android.service.NetworkInterfaceProto;
 import android.service.NetworkStatsServiceDumpProto;
-import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
@@ -545,7 +544,8 @@
         final int usedFlags = isRateLimitedForPoll(callingUid)
                 ? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
                 : flags;
-        if ((usedFlags & NetworkStatsManager.FLAG_POLL_ON_OPEN) != 0) {
+        if ((usedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
+                | NetworkStatsManager.FLAG_POLL_FORCE)) != 0) {
             final long ident = Binder.clearCallingIdentity();
             try {
                 performPoll(FLAG_PERSIST_ALL);
@@ -679,22 +679,12 @@
     private SubscriptionPlan resolveSubscriptionPlan(NetworkTemplate template, int flags) {
         SubscriptionPlan plan = null;
         if ((flags & NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN) != 0
-                && (template.getMatchRule() == NetworkTemplate.MATCH_MOBILE)
                 && mSettings.getAugmentEnabled()) {
             if (LOGD) Slog.d(TAG, "Resolving plan for " + template);
             final long token = Binder.clearCallingIdentity();
             try {
-                final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
-                final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-                for (int subId : sm.getActiveSubscriptionIdList()) {
-                    if (template.matchesSubscriberId(tm.getSubscriberId(subId))) {
-                        if (LOGD) Slog.d(TAG, "Found active matching subId " + subId);
-                        final List<SubscriptionPlan> plans = sm.getSubscriptionPlans(subId);
-                        if (!plans.isEmpty()) {
-                            plan = plans.get(0);
-                        }
-                    }
-                }
+                plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
+                        .getSubscriptionPlan(template);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 4c8b91b..477b062 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -926,7 +926,11 @@
                 Slog.v(TAG, "    disconnecting old " + getCaption() + ": " + info.service);
                 removeServiceLocked(i);
                 if (info.connection != null) {
-                    mContext.unbindService(info.connection);
+                    try {
+                        mContext.unbindService(info.connection);
+                    } catch (IllegalArgumentException e) {
+                        Slog.e(TAG, "failed to unbind " + name, e);
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 53e741e..5948864 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2397,6 +2397,11 @@
         }
 
         @Override
+        public boolean areChannelsBypassingDnd() {
+            return mRankingHelper.areChannelsBypassingDnd();
+        }
+
+        @Override
         public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
             checkCallerIsSystem();
 
@@ -3278,7 +3283,6 @@
                 policy = new Policy(policy.priorityCategories,
                         policy.priorityCallSenders, policy.priorityMessageSenders,
                         newVisualEffects);
-
                 ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion, policy);
                 mZenModeHelper.setNotificationPolicy(policy);
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 72a1a71..2aec3ea 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -15,10 +15,6 @@
  */
 package com.android.server.notification;
 
-import static android.app.Notification.EXTRA_AUDIO_CONTENTS_URI;
-import static android.app.Notification.EXTRA_BACKGROUND_IMAGE_URI;
-import static android.app.Notification.EXTRA_HISTORIC_MESSAGES;
-import static android.app.Notification.EXTRA_MESSAGES;
 import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -32,10 +28,8 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
 import android.app.IActivityManager;
 import android.app.Notification;
-import android.app.Notification.MessagingStyle;
 import android.app.NotificationChannel;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -55,7 +49,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -75,7 +68,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.ArrayUtils;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 
@@ -1029,36 +1021,14 @@
      */
     private void calculateGrantableUris() {
         final Notification notification = getNotification();
+        notification.visitUris((uri) -> {
+            visitGrantableUri(uri);
+        });
 
-        noteGrantableUri(notification.sound);
         if (notification.getChannelId() != null) {
             NotificationChannel channel = getChannel();
             if (channel != null) {
-                noteGrantableUri(channel.getSound());
-            }
-        }
-
-        final Bundle extras = notification.extras;
-        if (extras != null) {
-            noteGrantableUri(extras.getParcelable(EXTRA_AUDIO_CONTENTS_URI));
-            noteGrantableUri(extras.getParcelable(EXTRA_BACKGROUND_IMAGE_URI));
-        }
-
-        if (MessagingStyle.class.equals(notification.getNotificationStyle()) && extras != null) {
-            final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
-            if (!ArrayUtils.isEmpty(messages)) {
-                for (MessagingStyle.Message message : MessagingStyle.Message
-                        .getMessagesFromBundleArray(messages)) {
-                    noteGrantableUri(message.getDataUri());
-                }
-            }
-
-            final Parcelable[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
-            if (!ArrayUtils.isEmpty(historic)) {
-                for (MessagingStyle.Message message : MessagingStyle.Message
-                        .getMessagesFromBundleArray(historic)) {
-                    noteGrantableUri(message.getDataUri());
-                }
+                visitGrantableUri(channel.getSound());
             }
         }
     }
@@ -1071,7 +1041,7 @@
      * {@link #mGrantableUris}. Otherwise, this will either log or throw
      * {@link SecurityException} depending on target SDK of enqueuing app.
      */
-    private void noteGrantableUri(Uri uri) {
+    private void visitGrantableUri(Uri uri) {
         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
 
         // We can't grant Uri permissions from system
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 89bd660..febce31 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -17,13 +17,6 @@
 
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.Notification;
@@ -52,6 +45,13 @@
 import android.util.SparseBooleanArray;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -65,11 +65,11 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
 
 public class RankingHelper implements RankingConfig {
     private static final String TAG = "RankingHelper";
@@ -127,12 +127,15 @@
     private String mPermissionControllerPackageName;
     private String mServicesSystemSharedLibPackageName;
     private String mSharedSystemSharedLibPackageName;
+    private boolean mAreChannelsBypassingDnd;
+    private ZenModeHelper mZenModeHelper;
 
     public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
             ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) {
         mContext = context;
         mRankingHandler = rankingHandler;
         mPm = pm;
+        mZenModeHelper= zenHelper;
 
         mPreliminaryComparator = new NotificationComparator(mContext);
 
@@ -159,6 +162,7 @@
         }
 
         getSignatures();
+        updateChannelsBypassingDnd();
     }
 
     @SuppressWarnings("unchecked")
@@ -648,7 +652,12 @@
             // system apps and dnd access apps can bypass dnd if the user hasn't changed any
             // fields on the channel yet
             if (existing.getUserLockedFields() == 0 && (isSystemApp || hasDndAccess)) {
-                existing.setBypassDnd(channel.canBypassDnd());
+                boolean bypassDnd = channel.canBypassDnd();
+                existing.setBypassDnd(bypassDnd);
+
+                if (bypassDnd != mAreChannelsBypassingDnd) {
+                    updateChannelsBypassingDnd();
+                }
             }
 
             updateConfig();
@@ -675,6 +684,9 @@
         }
 
         r.channels.put(channel.getId(), channel);
+        if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
+            updateChannelsBypassingDnd();
+        }
         MetricsLogger.action(getChannelLog(channel, pkg).setType(
                 MetricsProto.MetricsEvent.TYPE_OPEN));
     }
@@ -781,6 +793,10 @@
             // only log if there are real changes
             MetricsLogger.action(getChannelLog(updatedChannel, pkg));
         }
+
+        if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) {
+            updateChannelsBypassingDnd();
+        }
         updateConfig();
     }
 
@@ -814,6 +830,10 @@
             LogMaker lm = getChannelLog(channel, pkg);
             lm.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
             MetricsLogger.action(lm);
+
+            if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
+                updateChannelsBypassingDnd();
+            }
         }
     }
 
@@ -1026,6 +1046,45 @@
         return count;
     }
 
+    public void updateChannelsBypassingDnd() {
+        synchronized (mRecords) {
+            final int numRecords = mRecords.size();
+            for (int recordIndex = 0; recordIndex < numRecords; recordIndex++) {
+                final Record r = mRecords.valueAt(recordIndex);
+                final int numChannels = r.channels.size();
+
+                for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) {
+                    NotificationChannel channel = r.channels.valueAt(channelIndex);
+                    if (!channel.isDeleted() && channel.canBypassDnd()) {
+                        if (!mAreChannelsBypassingDnd) {
+                            mAreChannelsBypassingDnd = true;
+                            updateZenPolicy(true);
+                        }
+                        return;
+                    }
+                }
+            }
+        }
+
+        if (mAreChannelsBypassingDnd) {
+            mAreChannelsBypassingDnd = false;
+            updateZenPolicy(false);
+        }
+    }
+
+    public void updateZenPolicy(boolean areChannelsBypassingDnd) {
+        NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
+        mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
+                policy.priorityCategories, policy.priorityCallSenders,
+                policy.priorityMessageSenders, policy.suppressedVisualEffects,
+                (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
+                        : 0)));
+    }
+
+    public boolean areChannelsBypassingDnd() {
+        return mAreChannelsBypassingDnd;
+    }
+
     /**
      * Sets importance.
      */
@@ -1225,12 +1284,16 @@
                         if (r.showBadge != DEFAULT_SHOW_BADGE) {
                             record.put("showBadge", Boolean.valueOf(r.showBadge));
                         }
+                        JSONArray channels = new JSONArray();
                         for (NotificationChannel channel : r.channels.values()) {
-                            record.put("channel", channel.toJson());
+                            channels.put(channel.toJson());
                         }
+                        record.put("channels", channels);
+                        JSONArray groups = new JSONArray();
                         for (NotificationChannelGroup group : r.groups.values()) {
-                            record.put("group", group.toJson());
+                            groups.put(group.toJson());
                         }
+                        record.put("groups", groups);
                     } catch (JSONException e) {
                         // pass
                     }
diff --git a/services/core/java/com/android/server/os/SchedulingPolicyService.java b/services/core/java/com/android/server/os/SchedulingPolicyService.java
index e0b8426..c64e745 100644
--- a/services/core/java/com/android/server/os/SchedulingPolicyService.java
+++ b/services/core/java/com/android/server/os/SchedulingPolicyService.java
@@ -18,8 +18,10 @@
 
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.ISchedulingPolicyService;
 import android.os.Process;
+import android.os.RemoteException;
 import android.util.Log;
 
 /**
@@ -35,7 +37,36 @@
     private static final int PRIORITY_MIN = 1;
     private static final int PRIORITY_MAX = 3;
 
+    private static final String[] MEDIA_PROCESS_NAMES = new String[] {
+            "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
+    };
+    private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            requestCpusetBoost(false /*enable*/, null /*client*/);
+        }
+    };
+    // Current process that received a cpuset boost
+    private int mBoostedPid = -1;
+    // Current client registered to the death recipient
+    private IBinder mClient;
+
     public SchedulingPolicyService() {
+        // system_server (our host) could have crashed before. The app may not survive
+        // it, but mediaserver/media.codec could have, and mediaserver probably tried
+        // to disable the boost while we were dead.
+        // We do a restore of media.codec to default cpuset upon service restart to
+        // catch this case. We can't leave media.codec in boosted state, because we've
+        // lost the death recipient of mClient from mediaserver after the restart,
+        // if mediaserver dies in the future we won't have a notification to reset.
+        // (Note that if mediaserver thinks we're in boosted state before the crash,
+        // the state could go out of sync temporarily until mediaserver enables/disable
+        // boost next time, but this won't be a big issue.)
+        int[] nativePids = Process.getPidsForCommands(MEDIA_PROCESS_NAMES);
+        if (nativePids != null && nativePids.length == 1) {
+            mBoostedPid = nativePids[0];
+            disableCpusetBoost(nativePids[0]);
+        }
     }
 
     // TODO(b/35196900) We should pass the period in time units, rather
@@ -74,6 +105,94 @@
         return PackageManager.PERMISSION_GRANTED;
     }
 
+    // Request to move media.codec process between SP_FOREGROUND and SP_TOP_APP.
+    public int requestCpusetBoost(boolean enable, IBinder client) {
+        if (!isPermitted()) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
+        int[] nativePids = Process.getPidsForCommands(MEDIA_PROCESS_NAMES);
+        if (nativePids == null || nativePids.length != 1) {
+            Log.e(TAG, "requestCpusetBoost: can't find media.codec process");
+            return PackageManager.PERMISSION_DENIED;
+        }
+
+        synchronized (mDeathRecipient) {
+            if (enable) {
+                return enableCpusetBoost(nativePids[0], client);
+            } else {
+                return disableCpusetBoost(nativePids[0]);
+            }
+        }
+    }
+
+    private int enableCpusetBoost(int pid, IBinder client) {
+        if (mBoostedPid == pid) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
+        // The mediacodec process has changed, clean up the old pid and
+        // client before we boost the new process, so that the state
+        // is left clean if things go wrong.
+        mBoostedPid = -1;
+        if (mClient != null) {
+            try {
+                mClient.unlinkToDeath(mDeathRecipient, 0);
+            } catch (Exception e) {
+            } finally {
+                mClient = null;
+            }
+        }
+
+        try {
+            client.linkToDeath(mDeathRecipient, 0);
+
+            Log.i(TAG, "Moving " + pid + " to group " + Process.THREAD_GROUP_TOP_APP);
+            Process.setProcessGroup(pid, Process.THREAD_GROUP_TOP_APP);
+
+            mBoostedPid = pid;
+            mClient = client;
+
+            return PackageManager.PERMISSION_GRANTED;
+        } catch (Exception e) {
+            Log.e(TAG, "Failed enableCpusetBoost: " + e);
+            try {
+                // unlink if things go wrong and don't crash.
+                client.unlinkToDeath(mDeathRecipient, 0);
+            } catch (Exception e1) {}
+        }
+
+        return PackageManager.PERMISSION_DENIED;
+    }
+
+    private int disableCpusetBoost(int pid) {
+        int boostedPid = mBoostedPid;
+
+        // Clean up states first.
+        mBoostedPid = -1;
+        if (mClient != null) {
+            try {
+                mClient.unlinkToDeath(mDeathRecipient, 0);
+            } catch (Exception e) {
+            } finally {
+                mClient = null;
+            }
+        }
+
+        // Try restore the old thread group, no need to fail as the
+        // mediacodec process could be dead just now.
+        if (boostedPid == pid) {
+            try {
+                Log.i(TAG, "Moving " + pid + " back to group default");
+                Process.setProcessGroup(pid, Process.THREAD_GROUP_DEFAULT);
+            } catch (Exception e) {
+                Log.w(TAG, "Couldn't move pid " + pid + " back to group default");
+            }
+        }
+
+        return PackageManager.PERMISSION_GRANTED;
+    }
+
     private boolean isPermitted() {
         // schedulerservice hidl
         if (Binder.getCallingPid() == Process.myPid()) {
@@ -81,9 +200,10 @@
         }
 
         switch (Binder.getCallingUid()) {
-        case Process.AUDIOSERVER_UID: // fastcapture, fastmixer
+        case Process.AUDIOSERVER_UID:  // fastcapture, fastmixer
+        case Process.MEDIA_UID:        // mediaserver
         case Process.CAMERASERVER_UID: // camera high frame rate recording
-        case Process.BLUETOOTH_UID: // Bluetooth audio playback
+        case Process.BLUETOOTH_UID:    // Bluetooth audio playback
             return true;
         default:
             return false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 50ac4db..a200231 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2462,7 +2462,7 @@
                 installer, mInstallLock);
         mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
                 dexManagerListener);
-        mArtManagerService = new ArtManagerService(this, installer, mInstallLock);
+        mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
         mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
 
         mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -23603,7 +23603,8 @@
 
         private SigningDetails getSigningDetails(int uid) {
             synchronized (mPackages) {
-                final Object obj = mSettings.getUserIdLPr(uid);
+                final int appId = UserHandle.getAppId(uid);
+                final Object obj = mSettings.getUserIdLPr(appId);
                 if (obj != null) {
                     if (obj instanceof SharedUserSetting) {
                         return ((SharedUserSetting) obj).signatures.mSigningDetails;
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 9c2ad463..1d5c580 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -16,8 +16,9 @@
 
 package com.android.server.pm.dex;
 
-import android.Manifest;
 import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
@@ -38,7 +39,9 @@
 import android.os.UserHandle;
 import android.system.Os;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.Slog;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
@@ -47,14 +50,17 @@
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PackageManagerServiceCompilerMapping;
+
 import dalvik.system.DexFile;
 import dalvik.system.VMRuntime;
-import java.io.File;
-import java.io.FileNotFoundException;
+
 import libcore.io.IoUtils;
 import libcore.util.NonNull;
 import libcore.util.Nullable;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+
 /**
  * A system service that provides access to runtime and compiler artifacts.
  *
@@ -69,9 +75,7 @@
  */
 public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
     private static final String TAG = "ArtManagerService";
-
-    private static boolean DEBUG = false;
-    private static boolean DEBUG_IGNORE_PERMISSIONS = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     // Package name used to create the profile directory layout when
     // taking a snapshot of the boot image profile.
@@ -79,6 +83,7 @@
     // Profile name used for the boot image profile.
     private static final String BOOT_IMAGE_PROFILE_NAME = "android.prof";
 
+    private final Context mContext;
     private final IPackageManager mPackageManager;
     private final Object mInstallLock;
     @GuardedBy("mInstallLock")
@@ -90,7 +95,9 @@
         verifyTronLoggingConstants();
     }
 
-    public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) {
+    public ArtManagerService(Context context, IPackageManager pm, Installer installer,
+            Object installLock) {
+        mContext = context;
         mPackageManager = pm;
         mInstaller = installer;
         mInstallLock = installLock;
@@ -99,9 +106,37 @@
         LocalServices.addService(ArtManagerInternal.class, new ArtManagerInternalImpl());
     }
 
+    private boolean checkPermission(int callingUid, String callingPackage) {
+        // Callers always need this permission
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.READ_RUNTIME_PROFILES, TAG);
+
+        // Callers also need the ability to read usage statistics
+        switch (mContext.getSystemService(AppOpsManager.class)
+                .noteOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage)) {
+            case AppOpsManager.MODE_ALLOWED:
+                return true;
+            case AppOpsManager.MODE_DEFAULT:
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
+                return true;
+            default:
+                return false;
+        }
+    }
+
     @Override
     public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
-            @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback) {
+            @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback,
+            String callingPackage) {
+        if (!checkPermission(Binder.getCallingUid(), callingPackage)) {
+            try {
+                callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+            } catch (RemoteException ignored) {
+            }
+            return;
+        }
+
         // Sanity checks on the arguments.
         Preconditions.checkNotNull(callback);
 
@@ -111,9 +146,8 @@
             Preconditions.checkStringNotEmpty(packageName);
         }
 
-        // Verify that the caller has the right permissions and that the runtime profiling is
-        // enabled. The call to isRuntimePermissions will checkReadRuntimeProfilePermission.
-        if (!isRuntimeProfilingEnabled(profileType)) {
+        // Verify that runtime profiling is enabled.
+        if (!isRuntimeProfilingEnabled(profileType, callingPackage)) {
             throw new IllegalStateException("Runtime profiling is not enabled for " + profileType);
         }
 
@@ -231,9 +265,10 @@
     }
 
     @Override
-    public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
-        // Verify that the caller has the right permissions.
-        checkReadRuntimeProfilePermission();
+    public boolean isRuntimeProfilingEnabled(@ProfileType int profileType, String callingPackage) {
+        if (!checkPermission(Binder.getCallingUid(), callingPackage)) {
+            return false;
+        }
 
         switch (profileType) {
             case ArtManager.PROFILE_APPS :
@@ -306,27 +341,6 @@
     }
 
     /**
-     * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}.
-     * If not, it throws a {@link SecurityException}.
-     */
-    private void checkReadRuntimeProfilePermission() {
-        if (DEBUG_IGNORE_PERMISSIONS) {
-            return;
-        }
-        try {
-            int result = mPackageManager.checkUidPermission(
-                    Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid());
-            if (result != PackageManager.PERMISSION_GRANTED) {
-                throw new SecurityException("You need "
-                        + Manifest.permission.READ_RUNTIME_PROFILES
-                        + " permission to snapshot profiles.");
-            }
-        } catch (RemoteException e) {
-            // Should not happen.
-        }
-    }
-
-    /**
      * Prepare the application profiles.
      * For all code paths:
      *   - create the current primary profile to save time at app startup time.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 920e77f..bd4210c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6091,6 +6091,14 @@
                 && (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled)
                 && event.getRepeatCount() == 0;
 
+        // Cancel any pending remote recents animations before handling the button itself. In the
+        // case where we are going home and the recents animation has already started, just cancel
+        // the recents animation, leaving the home stack in place for the pending start activity
+        if (isNavBarVirtKey && !down) {
+            boolean isHomeKey = keyCode == KeyEvent.KEYCODE_HOME;
+            mActivityManagerInternal.cancelRecentsAnimation(!isHomeKey);
+        }
+
         // Handle special keys.
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK: {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 608d0aa..68be50c 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -811,36 +811,35 @@
                             return;
                         }
                         mInvalidated = false;
-                        Canvas canvas = null;
-                        try {
-                            // Empty dirty rectangle means unspecified.
-                            if (mDirtyRect.isEmpty()) {
-                                mBounds.getBounds(mDirtyRect);
-                            }
-                            mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
-                            canvas = mSurface.lockCanvas(mDirtyRect);
-                            if (DEBUG_VIEWPORT_WINDOW) {
-                                Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
-                            }
-                        } catch (IllegalArgumentException iae) {
-                            /* ignore */
-                        } catch (Surface.OutOfResourcesException oore) {
-                            /* ignore */
-                        }
-                        if (canvas == null) {
-                            return;
-                        }
-                        if (DEBUG_VIEWPORT_WINDOW) {
-                            Slog.i(LOG_TAG, "Bounds: " + mBounds);
-                        }
-                        canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
-                        mPaint.setAlpha(mAlpha);
-                        Path path = mBounds.getBoundaryPath();
-                        canvas.drawPath(path, mPaint);
-
-                        mSurface.unlockCanvasAndPost(canvas);
-
                         if (mAlpha > 0) {
+                            Canvas canvas = null;
+                            try {
+                                // Empty dirty rectangle means unspecified.
+                                if (mDirtyRect.isEmpty()) {
+                                    mBounds.getBounds(mDirtyRect);
+                                }
+                                mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
+                                canvas = mSurface.lockCanvas(mDirtyRect);
+                                if (DEBUG_VIEWPORT_WINDOW) {
+                                    Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
+                                }
+                            } catch (IllegalArgumentException iae) {
+                                /* ignore */
+                            } catch (Surface.OutOfResourcesException oore) {
+                                /* ignore */
+                            }
+                            if (canvas == null) {
+                                return;
+                            }
+                            if (DEBUG_VIEWPORT_WINDOW) {
+                                Slog.i(LOG_TAG, "Bounds: " + mBounds);
+                            }
+                            canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
+                            mPaint.setAlpha(mAlpha);
+                            Path path = mBounds.getBoundaryPath();
+                            canvas.drawPath(path, mPaint);
+
+                            mSurface.unlockCanvasAndPost(canvas);
                             mSurfaceControl.show();
                         } else {
                             mSurfaceControl.hide();
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index ff2f687..0dc06b2 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -82,7 +82,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.ResourceId;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -555,17 +554,18 @@
         return null;
     }
 
-    Animation loadAnimationAttr(LayoutParams lp, int animAttr) {
-        int anim = ResourceId.ID_NULL;
+    Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
+        int anim = 0;
         Context context = mContext;
-        if (ResourceId.isValid(animAttr)) {
+        if (animAttr >= 0) {
             AttributeCache.Entry ent = getCachedAnimations(lp);
             if (ent != null) {
                 context = ent.context;
                 anim = ent.array.getResourceId(animAttr, 0);
             }
         }
-        if (ResourceId.isValid(anim)) {
+        anim = updateToTranslucentAnimIfNeeded(anim, transit);
+        if (anim != 0) {
             return AnimationUtils.loadAnimation(context, anim);
         }
         return null;
@@ -573,7 +573,7 @@
 
     Animation loadAnimationRes(LayoutParams lp, int resId) {
         Context context = mContext;
-        if (ResourceId.isValid(resId)) {
+        if (resId >= 0) {
             AttributeCache.Entry ent = getCachedAnimations(lp);
             if (ent != null) {
                 context = ent.context;
@@ -584,21 +584,31 @@
     }
 
     private Animation loadAnimationRes(String packageName, int resId) {
-        int anim = ResourceId.ID_NULL;
+        int anim = 0;
         Context context = mContext;
-        if (ResourceId.isValid(resId)) {
+        if (resId >= 0) {
             AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
             if (ent != null) {
                 context = ent.context;
                 anim = resId;
             }
         }
-        if (ResourceId.isValid(anim)) {
+        if (anim != 0) {
             return AnimationUtils.loadAnimation(context, anim);
         }
         return null;
     }
 
+    private int updateToTranslucentAnimIfNeeded(int anim, int transit) {
+        if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_OPEN && anim == R.anim.activity_open_enter) {
+            return R.anim.activity_translucent_open_enter;
+        }
+        if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE && anim == R.anim.activity_close_exit) {
+            return R.anim.activity_translucent_close_exit;
+        }
+        return anim;
+    }
+
     /**
      * Compute the pivot point for an animation that is scaling from a small
      * rect on screen to a larger rect.  The pivot point varies depending on
@@ -1665,29 +1675,17 @@
                     "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS:"
                             + " anim=" + a + " transit=" + appTransitionToString(transit)
                             + " isEntrance=true" + " Callers=" + Debug.getCallers(3));
-        } else if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_OPEN && enter) {
-            a = loadAnimationRes("android",
-                    com.android.internal.R.anim.activity_translucent_open_enter);
-            Slog.v(TAG,
-                    "applyAnimation TRANSIT_TRANSLUCENT_ACTIVITY_OPEN:"
-                            + " anim=" + a + " transit=" + appTransitionToString(transit)
-                            + " isEntrance=true" + " Callers=" + Debug.getCallers(3));
-        } else if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE && !enter) {
-            a = loadAnimationRes("android",
-                    com.android.internal.R.anim.activity_translucent_close_exit);
-            Slog.v(TAG,
-                    "applyAnimation TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE:"
-                            + " anim=" + a + " transit=" + appTransitionToString(transit)
-                            + " isEntrance=false" + " Callers=" + Debug.getCallers(3));
         } else {
             int animAttr = 0;
             switch (transit) {
                 case TRANSIT_ACTIVITY_OPEN:
+                case TRANSIT_TRANSLUCENT_ACTIVITY_OPEN:
                     animAttr = enter
                             ? WindowAnimation_activityOpenEnterAnimation
                             : WindowAnimation_activityOpenExitAnimation;
                     break;
                 case TRANSIT_ACTIVITY_CLOSE:
+                case TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE:
                     animAttr = enter
                             ? WindowAnimation_activityCloseEnterAnimation
                             : WindowAnimation_activityCloseExitAnimation;
@@ -1738,7 +1736,7 @@
                             ? WindowAnimation_launchTaskBehindSourceAnimation
                             : WindowAnimation_launchTaskBehindTargetAnimation;
             }
-            a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
+            a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null;
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
                     "applyAnimation:"
                     + " anim=" + a
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 5676f58..a701d42 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1662,7 +1662,9 @@
     }
 
     SurfaceControl getAppAnimationLayer() {
-        return getAppAnimationLayer(needsZBoost());
+        return getAppAnimationLayer(isActivityTypeHome() ? ANIMATION_LAYER_HOME
+                : needsZBoost() ? ANIMATION_LAYER_BOOSTED
+                : ANIMATION_LAYER_STANDARD);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 79eb2c9..4fd31ff 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3190,6 +3190,7 @@
          */
         SurfaceControl mAppAnimationLayer = null;
         SurfaceControl mBoostedAppAnimationLayer = null;
+        SurfaceControl mHomeAppAnimationLayer = null;
 
         /**
          * Given that the split-screen divider does not have an AppWindowToken, it
@@ -3552,6 +3553,7 @@
             int layer = 0;
             int layerForAnimationLayer = 0;
             int layerForBoostedAnimationLayer = 0;
+            int layerForHomeAnimationLayer = 0;
 
             for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
                 for (int i = 0; i < mChildren.size(); i++) {
@@ -3578,6 +3580,9 @@
                         layerForBoostedAnimationLayer = layer++;
                     }
                 }
+                if (state == HOME_STACK_STATE) {
+                    layerForHomeAnimationLayer = layer++;
+                }
             }
             if (mAppAnimationLayer != null) {
                 t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
@@ -3585,11 +3590,22 @@
             if (mBoostedAppAnimationLayer != null) {
                 t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
             }
+            if (mHomeAppAnimationLayer != null) {
+                t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
+            }
         }
 
         @Override
-        SurfaceControl getAppAnimationLayer(boolean boosted) {
-            return boosted ? mBoostedAppAnimationLayer : mAppAnimationLayer;
+        SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
+            switch (animationLayer) {
+                case ANIMATION_LAYER_BOOSTED:
+                    return mBoostedAppAnimationLayer;
+                case ANIMATION_LAYER_HOME:
+                    return mHomeAppAnimationLayer;
+                case ANIMATION_LAYER_STANDARD:
+                default:
+                    return mAppAnimationLayer;
+            }
         }
 
         SurfaceControl getSplitScreenDividerAnchor() {
@@ -3606,12 +3622,16 @@
                 mBoostedAppAnimationLayer = makeChildSurface(null)
                         .setName("boostedAnimationLayer")
                         .build();
+                mHomeAppAnimationLayer = makeChildSurface(null)
+                        .setName("homeAnimationLayer")
+                        .build();
                 mSplitScreenDividerAnchor = makeChildSurface(null)
                         .setName("splitScreenDividerAnchor")
                         .build();
                 getPendingTransaction()
                         .show(mAppAnimationLayer)
                         .show(mBoostedAppAnimationLayer)
+                        .show(mHomeAppAnimationLayer)
                         .show(mSplitScreenDividerAnchor);
                 scheduleAnimation();
             } else {
@@ -3619,6 +3639,8 @@
                 mAppAnimationLayer = null;
                 mBoostedAppAnimationLayer.destroy();
                 mBoostedAppAnimationLayer = null;
+                mHomeAppAnimationLayer.destroy();
+                mHomeAppAnimationLayer = null;
                 mSplitScreenDividerAnchor.destroy();
                 mSplitScreenDividerAnchor = null;
             }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 08fa153..79b230d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -369,7 +369,7 @@
     }
 
     void cancelAnimation(@ReorderMode int reorderMode, String reason) {
-        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation()");
+        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
         synchronized (mService.getWindowManagerLock()) {
             if (mCanceled) {
                 // We've already canceled the animation
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 1b06b2f..67ef471 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -83,7 +83,8 @@
      */
     AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
             Rect stackBounds) {
-        if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token=" + appWindowToken);
+        if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token="
+                + appWindowToken);
         final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
                 appWindowToken, position, stackBounds);
         mPendingAnimations.add(adapter);
@@ -96,8 +97,9 @@
     void goodToGo() {
         if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo()");
         if (mPendingAnimations.isEmpty() || mCanceled) {
-            if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished before good to go, canceled="
-                    + mCanceled + " mPendingAnimations=" + mPendingAnimations.size());
+            if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished already,"
+                    + " canceled=" + mCanceled
+                    + " mPendingAnimations=" + mPendingAnimations.size());
             onAnimationFinished();
             return;
         }
@@ -123,10 +125,6 @@
             }
             if (DEBUG_REMOTE_ANIMATIONS) {
                 Slog.d(TAG, "startAnimation(): Notify animation start:");
-                for (int i = 0; i < mPendingAnimations.size(); i++) {
-                    Slog.d(TAG, "\t" + mPendingAnimations.get(i).mAppWindowToken);
-                }
-            } else if (DEBUG_APP_TRANSITIONS) {
                 writeStartDebugStatement();
             }
         });
@@ -166,7 +164,8 @@
                 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrapper.mAppWindowToken);
                 targets.add(target);
             } else {
-                if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token=" + wrapper.mAppWindowToken);
+                if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token="
+                        + wrapper.mAppWindowToken);
 
                 // We can't really start an animation but we still need to make sure to finish the
                 // pending animation that was started by SurfaceAnimator
@@ -188,7 +187,8 @@
             releaseFinishedCallback();
             mService.openSurfaceTransaction();
             try {
-                if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): Notify animation finished:");
+                if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG,
+                        "onAnimationFinished(): Notify animation finished:");
                 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
                     final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
                     adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 95223d8..f87538a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -564,7 +564,7 @@
     public SurfaceControl getAnimationLeashParent() {
         // Reparent to the animation layer so that we aren't clipped by the non-minimized
         // stack bounds, currently we only animate the task for the recents animation
-        return getAppAnimationLayer(false /* boosted */);
+        return getAppAnimationLayer(ANIMATION_LAYER_STANDARD);
     }
 
     boolean isTaskAnimating() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 60e7c0d..331a0bd 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -29,6 +29,8 @@
 import static com.android.server.wm.WindowContainerProto.VISIBLE;
 
 import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -60,6 +62,25 @@
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
 
+    /** Animation layer that happens above all animating {@link TaskStack}s. */
+    static final int ANIMATION_LAYER_STANDARD = 0;
+
+    /** Animation layer that happens above all {@link TaskStack}s. */
+    static final int ANIMATION_LAYER_BOOSTED = 1;
+
+    /**
+     * Animation layer that is reserved for {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
+     * activities that happens below all {@link TaskStack}s.
+     */
+    static final int ANIMATION_LAYER_HOME = 2;
+
+    @IntDef(prefix = { "ANIMATION_LAYER_" }, value = {
+            ANIMATION_LAYER_STANDARD,
+            ANIMATION_LAYER_BOOSTED,
+            ANIMATION_LAYER_HOME,
+    })
+    @interface AnimationLayer {}
+
     static final int POSITION_TOP = Integer.MAX_VALUE;
     static final int POSITION_BOTTOM = Integer.MIN_VALUE;
 
@@ -1125,15 +1146,12 @@
     }
 
     /**
-     * @param boosted If true, returns an animation layer that happens above all {@link TaskStack}s
-     *                Otherwise, the layer will be positioned above all animating
-     *                {@link TaskStack}s.
      * @return The layer on which all app animations are happening.
      */
-    SurfaceControl getAppAnimationLayer(boolean boosted) {
+    SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
         final WindowContainer parent = getParent();
         if (parent != null) {
-            return parent.getAppAnimationLayer(boosted);
+            return parent.getAppAnimationLayer(animationLayer);
         }
         return null;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b1b026e..09e43f8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6864,7 +6864,6 @@
         }
     }
 
-    @Override
     public void setDockedStackResizing(boolean resizing) {
         synchronized (mWindowMap) {
             getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 61ce062..6c2821d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1088,6 +1088,8 @@
                             :  Math.max(mFrame.bottom - mStableFrame.bottom, 0));
         }
 
+        mDisplayCutout = displayCutout.calculateRelativeTo(mFrame);
+
         // Offset the actual frame by the amount layout frame is off.
         mFrame.offset(-layoutXDiff, -layoutYDiff);
         mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
@@ -1095,10 +1097,6 @@
         mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
         mStableFrame.offset(-layoutXDiff, -layoutYDiff);
 
-        // TODO(roosa): Figure out what frame exactly this needs to be calculated with.
-        mDisplayCutout = displayCutout.calculateRelativeTo(mFrame);
-
-
         mCompatFrame.set(mFrame);
         if (mEnforceSizeCompat) {
             // If there is a size compatibility scale being applied to the
@@ -2407,6 +2405,7 @@
         @Override
         public void binderDied() {
             try {
+                boolean resetSplitScreenResizing = false;
                 synchronized(mService.mWindowMap) {
                     final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
                     Slog.i(TAG, "WIN DEATH: " + win);
@@ -2426,13 +2425,23 @@
                             if (stack != null) {
                                 stack.resetDockedStackToMiddle();
                             }
-                            mService.setDockedStackResizing(false);
+                            resetSplitScreenResizing = true;
                         }
                     } else if (mHasSurface) {
                         Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
                         WindowState.this.removeIfPossible();
                     }
                 }
+                if (resetSplitScreenResizing) {
+                    try {
+                        // Note: this calls into ActivityManager, so we must *not* hold the window
+                        // manager lock while calling this.
+                        mService.mActivityManager.setSplitScreenResizing(false);
+                    } catch (RemoteException e) {
+                        // Local call, shouldn't return RemoteException.
+                        throw e.rethrowAsRuntimeException();
+                    }
+                }
             } catch (IllegalArgumentException ex) {
                 // This will happen if the window has already been removed.
             }
@@ -4708,16 +4717,38 @@
         outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top);
     }
 
+    boolean needsRelativeLayeringToIme() {
+        // We only use the relative layering mode in split screen, as part of elevating the IME
+        // and windows above it's target above the docked divider.
+        if (!inSplitScreenWindowingMode()) {
+            return false;
+        }
+
+        if (isChildWindow()) {
+            // If we are a child of the input method target we need this promotion.
+            if (getParentWindow().isInputMethodTarget()) {
+                return true;
+            }
+        } else if (mAppToken != null) {
+            // Likewise if we share a token with the Input method target and are ordered
+            // above it but not necessarily a child (e.g. a Dialog) then we also need
+            // this promotion.
+            final WindowState imeTarget = mService.mInputMethodTarget;
+            boolean inTokenWithAndAboveImeTarget = imeTarget != null && imeTarget != this
+                    && imeTarget.mToken == mToken && imeTarget.compareTo(this) <= 0;
+            return inTokenWithAndAboveImeTarget;
+        }
+        return false;
+    }
+
     @Override
     void assignLayer(Transaction t, int layer) {
         // See comment in assignRelativeLayerForImeTargetChild
-        if (!isChildWindow()
-                || (!getParentWindow().isInputMethodTarget())
-                || !inSplitScreenWindowingMode()) {
-            super.assignLayer(t, layer);
+        if (needsRelativeLayeringToIme()) {
+            getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
             return;
         }
-        getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
+        super.assignLayer(t, layer);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 195274a..a3428f0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_NONE;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
@@ -1360,7 +1361,7 @@
                         break;
                 }
                 if (attr >= 0) {
-                    a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr);
+                    a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr, TRANSIT_NONE);
                 }
             }
             if (DEBUG_ANIM) Slog.v(TAG,
diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java
index fcfb19c..99b2fc6 100644
--- a/services/net/java/android/net/apf/ApfGenerator.java
+++ b/services/net/java/android/net/apf/ApfGenerator.java
@@ -841,30 +841,30 @@
 
     /**
      * Add an instruction to the end of the program to load 32 bits from the data memory into
-     * {@code register}. The source address is computed by adding @{code offset} to the other
-     * register.
+     * {@code register}. The source address is computed by adding the signed immediate
+     * @{code offset} to the other register.
      * Requires APF v3 or greater.
      */
     public ApfGenerator addLoadData(Register destinationRegister, int offset)
             throws IllegalInstructionException {
         requireApfVersion(3);
         Instruction instruction = new Instruction(Opcodes.LDDW, destinationRegister);
-        instruction.setUnsignedImm(offset);
+        instruction.setSignedImm(offset);
         addInstruction(instruction);
         return this;
     }
 
     /**
      * Add an instruction to the end of the program to store 32 bits from {@code register} into the
-     * data memory. The destination address is computed by adding @{code offset} to the other
-     * register.
+     * data memory. The destination address is computed by adding the signed immediate
+     * @{code offset} to the other register.
      * Requires APF v3 or greater.
      */
     public ApfGenerator addStoreData(Register sourceRegister, int offset)
             throws IllegalInstructionException {
         requireApfVersion(3);
         Instruction instruction = new Instruction(Opcodes.STDW, sourceRegister);
-        instruction.setUnsignedImm(offset);
+        instruction.setSignedImm(offset);
         addInstruction(instruction);
         return this;
     }
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 87249df..9fdb31e 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -16,6 +16,7 @@
 
 package android.net.ip;
 
+import com.android.internal.util.HexDump;
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.WakeupMessage;
 
@@ -142,6 +143,12 @@
         // Install an APF program to filter incoming packets.
         public void installPacketFilter(byte[] filter) {}
 
+        // Asynchronously read back the APF program & data buffer from the wifi driver.
+        // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
+        // buffer. In response to this request, the driver returns the data buffer asynchronously
+        // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
+        public void startReadPacketFilter() {}
+
         // If multicast filtering cannot be accomplished with APF, this function will be called to
         // actuate multicast filtering using another means.
         public void setFallbackMulticastFilter(boolean enabled) {}
@@ -248,6 +255,11 @@
             log("installPacketFilter(byte[" + filter.length + "])");
         }
         @Override
+        public void startReadPacketFilter() {
+            mCallback.startReadPacketFilter();
+            log("startReadPacketFilter()");
+        }
+        @Override
         public void setFallbackMulticastFilter(boolean enabled) {
             mCallback.setFallbackMulticastFilter(enabled);
             log("setFallbackMulticastFilter(" + enabled + ")");
@@ -559,6 +571,7 @@
     private static final int CMD_SET_MULTICAST_FILTER             = 9;
     private static final int EVENT_PROVISIONING_TIMEOUT           = 10;
     private static final int EVENT_DHCPACTION_TIMEOUT             = 11;
+    private static final int EVENT_READ_PACKET_FILTER_COMPLETE    = 12;
 
     private static final int MAX_LOG_RECORDS = 500;
     private static final int MAX_PACKET_RECORDS = 100;
@@ -611,6 +624,7 @@
     private ApfFilter mApfFilter;
     private boolean mMulticastFiltering;
     private long mStartTimeMillis;
+    private byte[] mApfDataSnapshot;
 
     public static class Dependencies {
         public INetworkManagementService getNMS() {
@@ -823,6 +837,10 @@
         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
     }
 
+    public void readPacketFilterComplete(byte[] data) {
+        sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
+    }
+
     /**
      * Set the TCP buffer sizes to use.
      *
@@ -863,6 +881,7 @@
         final ProvisioningConfiguration provisioningConfig = mConfiguration;
         final ApfCapabilities apfCapabilities = (provisioningConfig != null)
                 ? provisioningConfig.mApfCapabilities : null;
+        final byte[] apfDataSnapshot = mApfDataSnapshot;
 
         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
         pw.println(mTag + " APF dump:");
@@ -880,6 +899,14 @@
             }
         }
         pw.decreaseIndent();
+        pw.println(mTag + " latest APF data snapshot: ");
+        pw.increaseIndent();
+        if (apfDataSnapshot != null) {
+            pw.println(HexDump.dumpHexString(apfDataSnapshot));
+        } else {
+            pw.println("No last snapshot.");
+        }
+        pw.decreaseIndent();
 
         pw.println();
         pw.println(mTag + " current ProvisioningConfiguration:");
@@ -1676,6 +1703,11 @@
                     break;
                 }
 
+                case EVENT_READ_PACKET_FILTER_COMPLETE: {
+                    mApfDataSnapshot = (byte[]) msg.obj;
+                    break;
+                }
+
                 case EVENT_DHCPACTION_TIMEOUT:
                     stopDhcpAction();
                     break;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 4638635..5a56332 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -441,6 +441,28 @@
         assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetRight(), 0);
     }
 
+    @Test
+    public void testDisplayCutout_tempInsetBounds() {
+        // Regular fullscreen task and window
+        TaskWithBounds task = new TaskWithBounds(new Rect(0, -500, 1000, 1500));
+        task.mFullscreenForTest = false;
+        task.mInsetBounds.set(0, 0, 1000, 2000);
+        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
+
+        final Rect pf = new Rect(0, -500, 1000, 1500);
+        // Create a display cutout of size 50x50, aligned top-center
+        final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
+
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout, false);
+
+        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetTop(), 50);
+        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetBottom(), 0);
+        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetLeft(), 0);
+        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetRight(), 0);
+    }
+
     private WindowStateWithTask createWindow(Task task, int width, int height) {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
         attrs.width = width;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 181fceb..ef9ba78 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -24,7 +24,13 @@
         .USER_SENTIMENT_POSITIVE;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
+import android.app.INotificationManager;
 import android.app.NotificationChannel;
 import android.content.Intent;
 import android.os.Binder;
@@ -34,6 +40,7 @@
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationRankingUpdate;
 import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -52,6 +59,19 @@
     private String[] mKeys = new String[] { "key", "key1", "key2", "key3"};
 
     @Test
+    public void testGetActiveNotifications_notNull() throws Exception {
+        TestListenerService service = new TestListenerService();
+        INotificationManager noMan = service.getNoMan();
+        when(noMan.getActiveNotificationsFromListener(any(), any(), anyInt())).thenReturn(null);
+
+        assertNotNull(service.getActiveNotifications());
+        assertNotNull(service.getActiveNotifications(NotificationListenerService.TRIM_FULL));
+        assertNotNull(service.getActiveNotifications(new String[0]));
+        assertNotNull(service.getActiveNotifications(
+                new String[0], NotificationListenerService.TRIM_LIGHT));
+    }
+
+    @Test
     public void testRanking() throws Exception {
         TestListenerService service = new TestListenerService();
         service.applyUpdateLocked(generateUpdate());
@@ -180,7 +200,12 @@
         private final IBinder binder = new LocalBinder();
 
         public TestListenerService() {
+            mWrapper = mock(NotificationListenerWrapper.class);
+            mNoMan = mock(INotificationManager.class);
+        }
 
+        INotificationManager getNoMan() {
+            return mNoMan;
         }
 
         @Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index bda6b8a..8183a74 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -172,9 +172,13 @@
         when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
                 .thenReturn(SOUND_URI);
 
-        mHelper = new RankingHelper(getContext(), mPm, mHandler, mock(ZenModeHelper.class),
+        ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
+        mHelper = new RankingHelper(getContext(), mPm, mHandler, mockZenModeHelper,
                 mUsageStats, new String[] {ImportanceExtractor.class.getName()});
 
+        when(mockZenModeHelper.getNotificationPolicy()).thenReturn(new NotificationManager.Policy(
+                0, 0, 0));
+
         mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("A")
                 .setGroup("G")
@@ -1129,6 +1133,50 @@
     }
 
     @Test
+    public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
+        // create notification channel that can't bypass dnd
+        // expected result: areChannelsBypassingDnd = false
+        NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+
+        //  create notification channel that can bypass dnd
+        // expected result: areChannelsBypassingDnd = true
+        NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel2.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG, UID, channel2, true, true);
+        assertTrue(mHelper.areChannelsBypassingDnd());
+
+        // delete channels
+        mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+        assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
+        mHelper.deleteNotificationChannel(PKG, UID, channel2.getId());
+        assertFalse(mHelper.areChannelsBypassingDnd());
+
+    }
+
+    @Test
+    public void testUpdateCanChannelsBypassDnd() throws Exception {
+        // create notification channel that can't bypass dnd
+        // expected result: areChannelsBypassingDnd = false
+        NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+
+        // update channel so it CAN bypass dnd:
+        // expected result: areChannelsBypassingDnd = true
+        channel.setBypassDnd(true);
+        mHelper.updateNotificationChannel(PKG, UID, channel, true);
+        assertTrue(mHelper.areChannelsBypassingDnd());
+
+        // update channel so it can't bypass dnd:
+        // expected result: areChannelsBypassingDnd = false
+        channel.setBypassDnd(false);
+        mHelper.updateNotificationChannel(PKG, UID, channel, true);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+    }
+
+    @Test
     public void testCreateDeletedChannel() throws Exception {
         long[] vibration = new long[]{100, 67, 145, 156};
         NotificationChannel channel =
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index dd56e0e..4ca175f 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -98,7 +98,7 @@
     private static final String LAUNCH_FILE = "applaunch.txt";
     private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
     private static final String DEFAULT_TRACE_CATEGORIES =
-            "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm";
+            "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm,binder_driver,hal";
     private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
     private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
     private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH";
@@ -310,7 +310,8 @@
                     try {
                         atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize,
                                 traceDumpInterval, rootTraceSubDir,
-                                String.format("%s-%s", launch.getApp(), launch.getLaunchReason()));
+                                String.format("%s-%s-%s", launch.getApp(),
+                                        launch.getCompilerFilter(), launch.getLaunchReason()));
                         startApp(launch.getApp(), launch.getLaunchReason());
                         sleep(POST_LAUNCH_IDLE_TIMEOUT);
                     } finally {
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index f3c22a5..9695e9a 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -27,6 +27,7 @@
 import android.net.LinkProperties.CompareResult;
 import android.net.LinkProperties.ProvisioningChange;
 import android.net.RouteInfo;
+import android.os.Parcel;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.system.OsConstants;
@@ -82,6 +83,9 @@
         assertTrue(source.isIdenticalPrivateDns(target));
         assertTrue(target.isIdenticalPrivateDns(source));
 
+        assertTrue(source.isIdenticalValidatedPrivateDnses(target));
+        assertTrue(target.isIdenticalValidatedPrivateDnses(source));
+
         assertTrue(source.isIdenticalRoutes(target));
         assertTrue(target.isIdenticalRoutes(source));
 
@@ -784,4 +788,35 @@
         assertEquals(new ArraySet<>(expectAdded), new ArraySet<>(result.added));
         assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
     }
+
+    @Test
+    public void testLinkPropertiesParcelable() {
+        LinkProperties source = new LinkProperties();
+        source.setInterfaceName(NAME);
+        // set 2 link addresses
+        source.addLinkAddress(LINKADDRV4);
+        source.addLinkAddress(LINKADDRV6);
+        // set 2 dnses
+        source.addDnsServer(DNS1);
+        source.addDnsServer(DNS2);
+        // set 2 gateways
+        source.addRoute(new RouteInfo(GATEWAY1));
+        source.addRoute(new RouteInfo(GATEWAY2));
+        // set 2 validated private dnses
+        source.addValidatedPrivateDnsServer(DNS6);
+        source.addValidatedPrivateDnsServer(GATEWAY61);
+
+        source.setMtu(MTU);
+
+        Parcel p = Parcel.obtain();
+        source.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        final byte[] marshalled = p.marshall();
+        p = Parcel.obtain();
+        p.unmarshall(marshalled, 0, marshalled.length);
+        p.setDataPosition(0);
+        LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p);
+
+        assertEquals(source, dest);
+    }
 }
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 082f310..f8a4132 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -160,7 +160,7 @@
             throw new Exception(
                     "program: " + HexDump.toHexString(program) +
                     "\ndata memory: " + HexDump.toHexString(data) +
-                    "\nexpected: " + HexDump.toHexString(expected_data));
+                    "\nexpected:    " + HexDump.toHexString(expected_data));
         }
     }
 
@@ -625,18 +625,19 @@
     @Test
     public void testApfDataWrite() throws IllegalInstructionException, Exception {
         byte[] packet = new byte[MIN_PKT_SIZE];
-        byte[] data = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
+        byte[] data = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
         byte[] expected_data = data.clone();
 
         // No memory access instructions: should leave the data segment untouched.
         ApfGenerator gen = new ApfGenerator(3);
         assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
 
-        // Expect value 0x87654321 to be stored starting from address 3 + 2, in big-endian order.
+        // Expect value 0x87654321 to be stored starting from address -11 from the end of the
+        // data buffer, in big-endian order.
         gen = new ApfGenerator(3);
         gen.addLoadImmediate(Register.R0, 0x87654321);
-        gen.addLoadImmediate(Register.R1, 2);
-        gen.addStoreData(Register.R0, 3);
+        gen.addLoadImmediate(Register.R1, -5);
+        gen.addStoreData(Register.R0, -6);  // -5 + -6 = -11 (offset +5 with data_len=16)
         expected_data[5] = (byte)0x87;
         expected_data[6] = (byte)0x65;
         expected_data[7] = (byte)0x43;
@@ -646,16 +647,16 @@
 
     @Test
     public void testApfDataRead() throws IllegalInstructionException, Exception {
-        // Program that DROPs if address 11 (7 + 3) contains 0x87654321.
+        // Program that DROPs if address 10 (-6) contains 0x87654321.
         ApfGenerator gen = new ApfGenerator(3);
-        gen.addLoadImmediate(Register.R1, 3);
-        gen.addLoadData(Register.R0, 7);
+        gen.addLoadImmediate(Register.R1, 10);
+        gen.addLoadData(Register.R0, -16);  // 10 + -16 = -6 (offset +10 with data_len=16)
         gen.addJumpIfR0Equals(0x87654321, gen.DROP_LABEL);
         byte[] program = gen.generate();
         byte[] packet = new byte[MIN_PKT_SIZE];
 
         // Content is incorrect (last byte does not match) -> PASS
-        byte[] data = new byte[32];
+        byte[] data = new byte[16];
         data[10] = (byte)0x87;
         data[11] = (byte)0x65;
         data[12] = (byte)0x43;
@@ -672,10 +673,10 @@
     @Test
     public void testApfDataReadModifyWrite() throws IllegalInstructionException, Exception {
         ApfGenerator gen = new ApfGenerator(3);
-        gen.addLoadImmediate(Register.R1, 3);
-        gen.addLoadData(Register.R0, 7);  // Load from address 7 + 3 = 10
+        gen.addLoadImmediate(Register.R1, -22);
+        gen.addLoadData(Register.R0, 0);  // Load from address 32 -22 + 0 = 10
         gen.addAdd(0x78453412);  // 87654321 + 78453412 = FFAA7733
-        gen.addStoreData(Register.R0, 11);  // Write back to address 11 + 3 = 14
+        gen.addStoreData(Register.R0, 4);  // Write back to address 32 -22 + 4 = 14
 
         byte[] packet = new byte[MIN_PKT_SIZE];
         byte[] data = new byte[32];
@@ -718,10 +719,17 @@
         gen.addJump(gen.DROP_LABEL);
         assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
 
-        // ...but underflowing isn't allowed.
+        // ...and underflowing simply wraps around to the end of the buffer...
         gen = new ApfGenerator(3);
         gen.addLoadImmediate(Register.R0, 20);
         gen.addLoadData(Register.R1, -30);
+        gen.addJump(gen.DROP_LABEL);
+        assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
+
+        // ...but doesn't allow accesses before the start of the buffer
+        gen = new ApfGenerator(3);
+        gen.addLoadImmediate(Register.R0, 20);
+        gen.addLoadData(Register.R1, -1000);
         gen.addJump(gen.DROP_LABEL);  // Not reached.
         assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
     }
diff --git a/tests/net/jni/apf_jni.cpp b/tests/net/jni/apf_jni.cpp
index 79444e3..1ea9e27 100644
--- a/tests/net/jni/apf_jni.cpp
+++ b/tests/net/jni/apf_jni.cpp
@@ -36,11 +36,20 @@
     uint32_t program_len = env->GetArrayLength(program);
     uint32_t packet_len = env->GetArrayLength(packet);
     uint32_t data_len = data ? env->GetArrayLength(data) : 0;
-    jint result = accept_packet(program_raw, program_len, packet_raw,
-                                packet_len, data_raw, data_len, filter_age);
+
+    // Merge program and data into a single buffer.
+    uint8_t* program_and_data = (uint8_t*)malloc(program_len + data_len);
+    memcpy(program_and_data, program_raw, program_len);
+    memcpy(program_and_data + program_len, data_raw, data_len);
+
+    jint result =
+        accept_packet(program_and_data, program_len, program_len + data_len,
+                      packet_raw, packet_len, filter_age);
     if (data) {
+        memcpy(data_raw, program_and_data + program_len, data_len);
         env->ReleaseByteArrayElements(data, (jbyte*)data_raw, 0 /* copy back */);
     }
+    free(program_and_data);
     env->ReleaseByteArrayElements(packet, (jbyte*)packet_raw, JNI_ABORT);
     env->ReleaseByteArrayElements(program, (jbyte*)program_raw, JNI_ABORT);
     return result;
@@ -109,8 +118,8 @@
         jstring jpcap_filename, jbyteArray japf_program) {
     ScopedUtfChars filter(env, jfilter);
     ScopedUtfChars pcap_filename(env, jpcap_filename);
-    const uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL);
-    const uint32_t apf_program_len = env->GetArrayLength(japf_program);
+    uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL);
+    uint32_t apf_program_len = env->GetArrayLength(japf_program);
 
     // Open pcap file for BPF filtering
     ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb"));
@@ -152,8 +161,8 @@
         do {
             apf_packet = pcap_next(apf_pcap.get(), &apf_header);
         } while (apf_packet != NULL && !accept_packet(
-                apf_program, apf_program_len, apf_packet, apf_header.len,
-                nullptr, 0, 0));
+                apf_program, apf_program_len, 0 /* data_len */,
+                apf_packet, apf_header.len, 0 /* filter_age */));
 
         // Make sure both filters matched the same packet.
         if (apf_packet == NULL && bpf_packet == NULL)
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1b6f882..19c6c31 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -586,7 +586,29 @@
 
     out_resource->name.type = ResourceType::kId;
     out_resource->name.entry = maybe_name.value().to_string();
-    out_resource->value = util::make_unique<Id>();
+
+    // Ids either represent a unique resource id or reference another resource id
+    auto item = ParseItem(parser, out_resource, resource_format);
+    if (!item) {
+      return false;
+    }
+
+    String* empty = ValueCast<String>(out_resource->value.get());
+    if (empty && *empty->value == "") {
+      // If no inner element exists, represent a unique identifier
+      out_resource->value = util::make_unique<Id>();
+    } else {
+      // If an inner element exists, the inner element must be a reference to
+      // another resource id
+      Reference* ref = ValueCast<Reference>(out_resource->value.get());
+      if (!ref || ref->name.value().type != ResourceType::kId) {
+        diag_->Error(DiagMessage(out_resource->source)
+                         << "<" << parser->element_name()
+                         << "> inner element must either be a resource reference or empty");
+        return false;
+      }
+    }
+
     return true;
   }
 
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index fc1aeaa..c12b9fa 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -933,4 +933,32 @@
   EXPECT_FALSE(TestParse(input));
 }
 
+TEST_F(ResourceParserTest, ParseIdItem) {
+  std::string input = R"(
+    <item name="foo" type="id">@id/bar</item>
+    <item name="bar" type="id"/>
+    <item name="baz" type="id"></item>)";
+  ASSERT_TRUE(TestParse(input));
+
+  ASSERT_THAT(test::GetValue<Reference>(&table_, "id/foo"), NotNull());
+  ASSERT_THAT(test::GetValue<Id>(&table_, "id/bar"), NotNull());
+  ASSERT_THAT(test::GetValue<Id>(&table_, "id/baz"), NotNull());
+
+  // Reject attribute references
+  input = R"(<item name="foo2" type="id">?attr/bar"</item>)";
+  ASSERT_FALSE(TestParse(input));
+
+  // Reject non-references
+  input = R"(<item name="foo3" type="id">0x7f010001</item>)";
+  ASSERT_FALSE(TestParse(input));
+  input = R"(<item name="foo4" type="id">@drawable/my_image</item>)";
+  ASSERT_FALSE(TestParse(input));
+  input = R"(<item name="foo5" type="id"><string name="biz"></string></item>)";
+  ASSERT_FALSE(TestParse(input));
+
+  // Ids that reference other resource ids cannot be public
+  input = R"(<public name="foo6" type="id">@id/bar6</item>)";
+  ASSERT_FALSE(TestParse(input));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index df483b2..d7a3771 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -306,6 +306,7 @@
 }
 
 // A value that represents a primitive data type (float, int, boolean, etc.).
+// Refer to Res_value in ResourceTypes.h for info on types and formatting
 message Primitive {
   message NullType {
   }
@@ -315,8 +316,8 @@
     NullType null_value = 1;
     EmptyType empty_value = 2;
     float float_value = 3;
-    float dimension_value = 4;
-    float fraction_value = 5;
+    uint32 dimension_value = 13;
+    uint32 fraction_value = 14;
     int32 int_decimal_value = 6;
     uint32 int_hexadecimal_value = 7;
     bool boolean_value = 8;
@@ -324,6 +325,8 @@
     uint32 color_rgb8_value = 10;
     uint32 color_argb4_value = 11;
     uint32 color_rgb4_value = 12;
+    float dimension_value_deprecated = 4 [deprecated=true];
+    float fraction_value_deprecated = 5 [deprecated=true];
   }
 }
 
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index f1eb952..3b101b7 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -780,13 +780,11 @@
         } break;
         case pb::Primitive::kDimensionValue: {
           val.dataType = android::Res_value::TYPE_DIMENSION;
-          float dimen_val = pb_prim.dimension_value();
-          val.data = *(uint32_t*)&dimen_val;
+          val.data  = pb_prim.dimension_value();
         } break;
         case pb::Primitive::kFractionValue: {
           val.dataType = android::Res_value::TYPE_FRACTION;
-          float fraction_val = pb_prim.fraction_value();
-          val.data = *(uint32_t*)&fraction_val;
+          val.data  = pb_prim.fraction_value();
         } break;
         case pb::Primitive::kIntDecimalValue: {
           val.dataType = android::Res_value::TYPE_INT_DEC;
@@ -816,6 +814,16 @@
           val.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
           val.data = pb_prim.color_rgb4_value();
         } break;
+        case pb::Primitive::kDimensionValueDeprecated: {  // DEPRECATED
+          val.dataType = android::Res_value::TYPE_DIMENSION;
+          float dimen_val = pb_prim.dimension_value_deprecated();
+          val.data = *(uint32_t*)&dimen_val;
+        } break;
+        case pb::Primitive::kFractionValueDeprecated: {  // DEPRECATED
+          val.dataType = android::Res_value::TYPE_FRACTION;
+          float fraction_val = pb_prim.fraction_value_deprecated();
+          val.data = *(uint32_t*)&fraction_val;
+        } break;
         default: {
           LOG(FATAL) << "Unexpected Primitive type: "
                      << static_cast<uint32_t>(pb_prim.oneof_value_case());
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 2e56359..411cc29 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -452,10 +452,10 @@
         pb_prim->set_float_value(*(float*)&val.data);
       } break;
       case android::Res_value::TYPE_DIMENSION: {
-        pb_prim->set_dimension_value(*(float*)&val.data);
+        pb_prim->set_dimension_value(val.data);
       } break;
       case android::Res_value::TYPE_FRACTION: {
-        pb_prim->set_fraction_value(*(float*)&val.data);
+        pb_prim->set_fraction_value(val.data);
       } break;
       case android::Res_value::TYPE_INT_DEC: {
         pb_prim->set_int_decimal_value(static_cast<int32_t>(val.data));
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 6366a3d..21fdbd8 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -271,6 +271,7 @@
           .AddValue("android:integer/hex_int_abcd", ResourceUtils::TryParseInt("0xABCD"))
           .AddValue("android:dimen/dimen_1.39mm", ResourceUtils::TryParseFloat("1.39mm"))
           .AddValue("android:fraction/fraction_27", ResourceUtils::TryParseFloat("27%"))
+          .AddValue("android:dimen/neg_2.3in", ResourceUtils::TryParseFloat("-2.3in"))
           .AddValue("android:integer/null", ResourceUtils::MakeEmpty())
           .Build();
 
@@ -353,6 +354,12 @@
   EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_FRACTION));
   EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseFloat("27%")->value.data));
 
+  bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:dimen/neg_2.3in",
+                                                          ConfigDescription::DefaultConfig(), "");
+  ASSERT_THAT(bp, NotNull());
+  EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_DIMENSION));
+  EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseFloat("-2.3in")->value.data));
+
   bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:integer/null",
                                                           ConfigDescription::DefaultConfig(), "");
   ASSERT_THAT(bp, NotNull());