Merge "Avoid race condition when broadcasting device list changed." into pi-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a591eaf..54cb095 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4922,7 +4922,8 @@
 
             CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
             if (!p.ambient && validRemoteInput && replyText != null
-                    && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
+                    && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])
+                    && p.maxRemoteInputHistory > 0) {
                 boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER);
                 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
                 big.setViewVisibility(R.id.notification_material_reply_text_1_container,
@@ -4937,13 +4938,15 @@
                         ColorStateList.valueOf(
                                 isColorized() ? getPrimaryTextColor() : resolveContrastColor()));
 
-                if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
+                if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])
+                        && p.maxRemoteInputHistory > 1) {
                     big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
                     big.setTextViewText(R.id.notification_material_reply_text_2,
                             processTextSpans(replyText[1]));
                     setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
 
-                    if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
+                    if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])
+                            && p.maxRemoteInputHistory > 2) {
                         big.setViewVisibility(
                                 R.id.notification_material_reply_text_3, View.VISIBLE);
                         big.setTextViewText(R.id.notification_material_reply_text_3,
@@ -5106,7 +5109,13 @@
                 return null;
             }
 
-            return applyStandardTemplateWithActions(getBigBaseLayoutResource(), null /* result */);
+            // We only want at most a single remote input history to be shown here, otherwise
+            // the content would become squished.
+            StandardTemplateParams p = mParams.reset().fillTextsFrom(this)
+                    .setMaxRemoteInputHistory(1);
+            return applyStandardTemplateWithActions(getBigBaseLayoutResource(),
+                    p,
+                    null /* result */);
         }
 
         /**
@@ -5975,6 +5984,12 @@
      * object.
      */
     public static abstract class Style {
+
+        /**
+         * The number of items allowed simulatanously in the remote input history.
+         * @hide
+         */
+        static final int MAX_REMOTE_INPUT_HISTORY_LINES = 3;
         private CharSequence mBigContentTitle;
 
         /**
@@ -7376,7 +7391,14 @@
                 return messages;
             }
 
-            static Message getMessageFromBundle(Bundle bundle) {
+            /**
+             * @return The message that is stored in the bundle or null if the message couldn't be
+             * resolved.
+             *
+             * @hide
+             */
+            @Nullable
+            public static Message getMessageFromBundle(Bundle bundle) {
                 try {
                     if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
                         return null;
@@ -7434,6 +7456,11 @@
      * @see Notification#bigContentView
      */
     public static class InboxStyle extends Style {
+
+        /**
+         * The number of lines of remote input history allowed until we start reducing lines.
+         */
+        private static final int NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION = 1;
         private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
 
         public InboxStyle() {
@@ -7533,6 +7560,28 @@
             if (mBuilder.mActions.size() > 0) {
                 maxRows--;
             }
+            CharSequence[] remoteInputHistory = mBuilder.mN.extras.getCharSequenceArray(
+                    EXTRA_REMOTE_INPUT_HISTORY);
+            if (remoteInputHistory != null
+                    && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) {
+                // Let's remove some messages to make room for the remote input history.
+                // 1 is always able to fit, but let's remove them if they are 2 or 3
+                int numRemoteInputs = Math.min(remoteInputHistory.length,
+                        MAX_REMOTE_INPUT_HISTORY_LINES);
+                int totalNumRows = mTexts.size() + numRemoteInputs
+                        - NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION;
+                if (totalNumRows > maxRows) {
+                    int overflow = totalNumRows - maxRows;
+                    if (mTexts.size() > maxRows) {
+                        // Heuristic: if the Texts don't fit anyway, we'll rather drop the last
+                        // few messages, even with the remote input
+                        maxRows -= overflow;
+                    } else  {
+                        // otherwise we drop the first messages
+                        i = overflow;
+                    }
+                }
+            }
             while (i < mTexts.size() && i < maxRows) {
                 CharSequence str = mTexts.get(i);
                 if (!TextUtils.isEmpty(str)) {
@@ -9603,6 +9652,7 @@
         CharSequence title;
         CharSequence text;
         CharSequence headerTextSecondary;
+        int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
         boolean hideLargeIcon;
         boolean hideReplyIcon;
 
@@ -9612,6 +9662,7 @@
             title = null;
             text = null;
             headerTextSecondary = null;
+            maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
             return this;
         }
 
@@ -9663,5 +9714,15 @@
             this.text = b.processLegacyText(text, ambient);
             return this;
         }
+
+        /**
+         * Set the maximum lines of remote input history lines allowed.
+         * @param maxRemoteInputHistory The number of lines.
+         * @return The builder for method chaining.
+         */
+        public StandardTemplateParams setMaxRemoteInputHistory(int maxRemoteInputHistory) {
+            this.maxRemoteInputHistory = maxRemoteInputHistory;
+            return this;
+        }
     }
 }
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 2306e5f..16c1f6d 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -21,18 +21,15 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Paint.Align;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Typeface;
-import android.graphics.Paint.Align;
-import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.Keyboard.Key;
 import android.media.AudioManager;
 import android.os.Handler;
 import android.os.Message;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.GestureDetector;
@@ -662,11 +659,13 @@
             invalidateAllKeys();
             mKeyboardChanged = false;
         }
-        final Canvas canvas = mCanvas;
-        canvas.clipRect(mDirtyRect, Op.REPLACE);
 
         if (mKeyboard == null) return;
 
+        mCanvas.save();
+        final Canvas canvas = mCanvas;
+        canvas.clipRect(mDirtyRect);
+
         final Paint paint = mPaint;
         final Drawable keyBackground = mKeyBackground;
         final Rect clipRegion = mClipRegion;
@@ -758,7 +757,7 @@
             paint.setColor(0xFF00FF00);
             canvas.drawCircle((mStartX + mLastX) / 2, (mStartY + mLastY) / 2, 2, paint);
         }
-
+        mCanvas.restore();
         mDrawPending = false;
         mDirtyRect.setEmpty();
     }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c3b8f39..c5cb1f5 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -238,6 +238,14 @@
     public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
 
     /**
+     * Key for passing a {@link android.net.captiveportal.CaptivePortalProbeSpec} to the captive
+     * portal login activity.
+     * {@hide}
+     */
+    public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC =
+            "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
+
+    /**
      * Key for passing a user agent string to the captive portal login activity.
      * {@hide}
      */
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 15a0ee5..d75d439 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -85,6 +85,21 @@
     private static final long HANDLE_MAGIC = 0xcafed00dL;
     private static final int HANDLE_MAGIC_SIZE = 32;
 
+    // A boolean to control how getAllByName()/getByName() behaves in the face
+    // of Private DNS.
+    //
+    // When true, these calls will request that DNS resolution bypass any
+    // Private DNS that might otherwise apply. Use of this feature is restricted
+    // and permission checks are made by netd (attempts to bypass Private DNS
+    // without appropriate permission are silently turned into vanilla DNS
+    // requests). This only affects DNS queries made using this network object.
+    //
+    // It it not parceled to receivers because (a) it can be set or cleared at
+    // anytime and (b) receivers should be explicit about attempts to bypass
+    // Private DNS so that the intent of the code is easily determined and
+    // code search audits are possible.
+    private boolean mPrivateDnsBypass = false;
+
     /**
      * @hide
      */
@@ -108,7 +123,7 @@
      * @throws UnknownHostException if the address lookup fails.
      */
     public InetAddress[] getAllByName(String host) throws UnknownHostException {
-        return InetAddress.getAllByNameOnNet(host, netId);
+        return InetAddress.getAllByNameOnNet(host, getNetIdForResolv());
     }
 
     /**
@@ -122,7 +137,32 @@
      *             if the address lookup fails.
      */
     public InetAddress getByName(String host) throws UnknownHostException {
-        return InetAddress.getByNameOnNet(host, netId);
+        return InetAddress.getByNameOnNet(host, getNetIdForResolv());
+    }
+
+    /**
+     * Specify whether or not Private DNS should be bypassed when attempting
+     * to use {@link getAllByName()}/{@link getByName()} methods on the given
+     * instance for hostname resolution.
+     *
+     * @hide
+     */
+    public void setPrivateDnsBypass(boolean bypass) {
+        mPrivateDnsBypass = bypass;
+    }
+
+    /**
+     * Returns a netid marked with the Private DNS bypass flag.
+     *
+     * This flag must be kept in sync with the NETID_USE_LOCAL_NAMESERVERS flag
+     * in system/netd/include/NetdClient.h.
+     *
+     * @hide
+     */
+    public int getNetIdForResolv() {
+        return mPrivateDnsBypass
+                ? (int) (0x80000000L | (long) netId)  // Non-portable DNS resolution flag.
+                : netId;
     }
 
     /**
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index 66cdc99..ddf63ca 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -84,6 +84,10 @@
      * @return true if the transaction was successful.
      */
     public boolean requestTime(String host, int timeout, Network network) {
+        // This flag only affects DNS resolution and not other socket semantics,
+        // therefore it's safe to set unilaterally rather than take more
+        // defensive measures like making a copy.
+        network.setPrivateDnsBypass(true);
         InetAddress address = null;
         try {
             address = network.getByName(host);
diff --git a/core/java/android/net/captiveportal/CaptivePortalProbeResult.java b/core/java/android/net/captiveportal/CaptivePortalProbeResult.java
index 614c0b8..1634694 100644
--- a/core/java/android/net/captiveportal/CaptivePortalProbeResult.java
+++ b/core/java/android/net/captiveportal/CaptivePortalProbeResult.java
@@ -16,6 +16,8 @@
 
 package android.net.captiveportal;
 
+import android.annotation.Nullable;
+
 /**
  * Result of calling isCaptivePortal().
  * @hide
@@ -23,6 +25,7 @@
 public final class CaptivePortalProbeResult {
     public static final int SUCCESS_CODE = 204;
     public static final int FAILED_CODE = 599;
+    public static final int PORTAL_CODE = 302;
 
     public static final CaptivePortalProbeResult FAILED = new CaptivePortalProbeResult(FAILED_CODE);
     public static final CaptivePortalProbeResult SUCCESS =
@@ -32,15 +35,23 @@
     public final String redirectUrl;      // Redirect destination returned from Internet probe.
     public final String detectUrl;        // URL where a 204 response code indicates
                                           // captive portal has been appeased.
+    @Nullable
+    public final CaptivePortalProbeSpec probeSpec;
 
     public CaptivePortalProbeResult(int httpResponseCode) {
         this(httpResponseCode, null, null);
     }
 
     public CaptivePortalProbeResult(int httpResponseCode, String redirectUrl, String detectUrl) {
+        this(httpResponseCode, redirectUrl, detectUrl, null);
+    }
+
+    public CaptivePortalProbeResult(int httpResponseCode, String redirectUrl, String detectUrl,
+            CaptivePortalProbeSpec probeSpec) {
         mHttpResponseCode = httpResponseCode;
         this.redirectUrl = redirectUrl;
         this.detectUrl = detectUrl;
+        this.probeSpec = probeSpec;
     }
 
     public boolean isSuccessful() {
diff --git a/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java b/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java
new file mode 100644
index 0000000..57a926a
--- /dev/null
+++ b/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java
@@ -0,0 +1,180 @@
+/*
+ * 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.net.captiveportal;
+
+import static android.net.captiveportal.CaptivePortalProbeResult.PORTAL_CODE;
+import static android.net.captiveportal.CaptivePortalProbeResult.SUCCESS_CODE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/** @hide */
+public abstract class CaptivePortalProbeSpec {
+    public static final String HTTP_LOCATION_HEADER_NAME = "Location";
+
+    private static final String TAG = CaptivePortalProbeSpec.class.getSimpleName();
+    private static final String REGEX_SEPARATOR = "@@/@@";
+    private static final String SPEC_SEPARATOR = "@@,@@";
+
+    private final String mEncodedSpec;
+    private final URL mUrl;
+
+    CaptivePortalProbeSpec(String encodedSpec, URL url) {
+        mEncodedSpec = encodedSpec;
+        mUrl = url;
+    }
+
+    /**
+     * Parse a {@link CaptivePortalProbeSpec} from a {@link String}.
+     *
+     * <p>The valid format is a URL followed by two regular expressions, each separated by "@@/@@".
+     * @throws MalformedURLException The URL has invalid format for {@link URL#URL(String)}.
+     * @throws ParseException The string is empty, does not match the above format, or a regular
+     * expression is invalid for {@link Pattern#compile(String)}.
+     */
+    @NonNull
+    public static CaptivePortalProbeSpec parseSpec(String spec) throws ParseException,
+            MalformedURLException {
+        if (TextUtils.isEmpty(spec)) {
+            throw new ParseException("Empty probe spec", 0 /* errorOffset */);
+        }
+
+        String[] splits = TextUtils.split(spec, REGEX_SEPARATOR);
+        if (splits.length != 3) {
+            throw new ParseException("Probe spec does not have 3 parts", 0 /* errorOffset */);
+        }
+
+        final int statusRegexPos = splits[0].length() + REGEX_SEPARATOR.length();
+        final int locationRegexPos = statusRegexPos + splits[1].length() + REGEX_SEPARATOR.length();
+        final Pattern statusRegex = parsePatternIfNonEmpty(splits[1], statusRegexPos);
+        final Pattern locationRegex = parsePatternIfNonEmpty(splits[2], locationRegexPos);
+
+        return new RegexMatchProbeSpec(spec, new URL(splits[0]), statusRegex, locationRegex);
+    }
+
+    @Nullable
+    private static Pattern parsePatternIfNonEmpty(String pattern, int pos) throws ParseException {
+        if (TextUtils.isEmpty(pattern)) {
+            return null;
+        }
+        try {
+            return Pattern.compile(pattern);
+        } catch (PatternSyntaxException e) {
+            throw new ParseException(
+                    String.format("Invalid status pattern [%s]: %s", pattern, e),
+                    pos /* errorOffset */);
+        }
+    }
+
+    /**
+     * Parse a {@link CaptivePortalProbeSpec} from a {@link String}, or return a fallback spec
+     * based on the status code of the provided URL if the spec cannot be parsed.
+     */
+    @Nullable
+    public static CaptivePortalProbeSpec parseSpecOrNull(@Nullable String spec) {
+        if (spec != null) {
+            try {
+                return parseSpec(spec);
+            } catch (ParseException | MalformedURLException e) {
+                Log.e(TAG, "Invalid probe spec: " + spec, e);
+                // Fall through
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Parse a config String to build an array of {@link CaptivePortalProbeSpec}.
+     *
+     * <p>Each spec is separated by @@,@@ and follows the format for {@link #parseSpec(String)}.
+     * <p>This method does not throw but ignores any entry that could not be parsed.
+     */
+    public static CaptivePortalProbeSpec[] parseCaptivePortalProbeSpecs(String settingsVal) {
+        List<CaptivePortalProbeSpec> specs = new ArrayList<>();
+        if (settingsVal != null) {
+            for (String spec : TextUtils.split(settingsVal, SPEC_SEPARATOR)) {
+                try {
+                    specs.add(parseSpec(spec));
+                } catch (ParseException | MalformedURLException e) {
+                    Log.e(TAG, "Invalid probe spec: " + spec, e);
+                }
+            }
+        }
+
+        if (specs.isEmpty()) {
+            Log.e(TAG, String.format("could not create any validation spec from %s", settingsVal));
+        }
+        return specs.toArray(new CaptivePortalProbeSpec[specs.size()]);
+    }
+
+    /**
+     * Get the probe result from HTTP status and location header.
+     */
+    public abstract CaptivePortalProbeResult getResult(int status, @Nullable String locationHeader);
+
+    public String getEncodedSpec() {
+        return mEncodedSpec;
+    }
+
+    public URL getUrl() {
+        return mUrl;
+    }
+
+    /**
+     * Implementation of {@link CaptivePortalProbeSpec} that is based on configurable regular
+     * expressions for the HTTP status code and location header (if any). Matches indicate that
+     * the page is not a portal.
+     * This probe cannot fail: it always returns SUCCESS_CODE or PORTAL_CODE
+     */
+    private static class RegexMatchProbeSpec extends CaptivePortalProbeSpec {
+        @Nullable
+        final Pattern mStatusRegex;
+        @Nullable
+        final Pattern mLocationHeaderRegex;
+
+        RegexMatchProbeSpec(
+                String spec, URL url, Pattern statusRegex, Pattern locationHeaderRegex) {
+            super(spec, url);
+            mStatusRegex = statusRegex;
+            mLocationHeaderRegex = locationHeaderRegex;
+        }
+
+        @Override
+        public CaptivePortalProbeResult getResult(int status, String locationHeader) {
+            final boolean statusMatch = safeMatch(String.valueOf(status), mStatusRegex);
+            final boolean locationMatch = safeMatch(locationHeader, mLocationHeaderRegex);
+            final int returnCode = statusMatch && locationMatch ? SUCCESS_CODE : PORTAL_CODE;
+            return new CaptivePortalProbeResult(
+                    returnCode, locationHeader, getUrl().toString(), this);
+        }
+    }
+
+    private static boolean safeMatch(@Nullable String value, @Nullable Pattern pattern) {
+        // No value is a match ("no location header" passes the location rule for non-redirects)
+        return pattern == null || TextUtils.isEmpty(value) || pattern.matcher(value).matches();
+    }
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 619ec23..221abed 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -245,8 +245,10 @@
      * New in version 31:
      *   - New cellular network types.
      *   - Deferred job metrics.
+     * New in version 32:
+     *   - Ambient display properly output in data dump.
      */
-    static final int CHECKIN_VERSION = 31;
+    static final int CHECKIN_VERSION = 32;
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 5e23932..3017f25 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -45,6 +45,7 @@
             in String[] disallowedPackages);
     UserInfo createRestrictedProfile(String name, int parentUserHandle);
     void setUserEnabled(int userHandle);
+    void setUserAdmin(int userId);
     void evictCredentialEncryptionKey(int userHandle);
     boolean removeUser(int userHandle);
     boolean removeUserEvenWhenDisallowed(int userHandle);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 11afd21..b591851 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2087,12 +2087,33 @@
      * Also ephemeral users can be disabled to indicate that their removal is in progress and they
      * shouldn't be re-entered. Therefore ephemeral users should not be re-enabled once disabled.
      *
-     * @param userHandle the id of the profile to enable
+     * @param userId the id of the profile to enable
      * @hide
      */
-    public void setUserEnabled(@UserIdInt int userHandle) {
+    public void setUserEnabled(@UserIdInt int userId) {
         try {
-            mService.setUserEnabled(userHandle);
+            mService.setUserEnabled(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Assigns admin privileges to the user, if such a user exists.
+     *
+     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} and
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permissions.
+     *
+     * @param userHandle the id of the user to become admin
+     * @hide
+     */
+    @RequiresPermission(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.MANAGE_USERS
+    })
+    public void setUserAdmin(@UserIdInt int userHandle) {
+        try {
+            mService.setUserAdmin(userHandle);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 4d238c0..ff5502f 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.annotation.Nullable;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.hardware.vibrator.V1_0.EffectStrength;
 import android.hardware.vibrator.V1_2.Effect;
@@ -275,7 +276,12 @@
             if (uris[i] == null) {
                 continue;
             }
-            if (Uri.parse(uris[i]).equals(uri)) {
+            ContentResolver cr = context.getContentResolver();
+            Uri mappedUri = cr.uncanonicalize(Uri.parse(uris[i]));
+            if (mappedUri == null) {
+                continue;
+            }
+            if (mappedUri.equals(uri)) {
                 return get(RINGTONES[i]);
             }
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bbbcbbb..0510418 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7114,6 +7114,35 @@
         public static final String UI_NIGHT_MODE = "ui_night_mode";
 
         /**
+         * The current device UI theme mode effect SystemUI and Launcher.<br/>
+         * <b>Values:</b><br/>
+         * 0 - The mode that theme will controlled by wallpaper color.<br/>
+         * 1 - The mode that will always light theme.<br/>
+         * 2 - The mode that will always dark theme.<br/>
+         *
+         * @hide
+         */
+        public static final String THEME_MODE = "theme_mode";
+
+        /**
+         * THEME_MODE value for wallpaper mode.
+         * @hide
+         */
+        public static final int THEME_MODE_WALLPAPER = 0;
+
+        /**
+         * THEME_MODE value for light theme mode.
+         * @hide
+         */
+        public static final int THEME_MODE_LIGHT = 1;
+
+        /**
+         * THEME_MODE value for dark theme mode.
+         * @hide
+         */
+        public static final int THEME_MODE_DARK = 2;
+
+        /**
          * Whether screensavers are enabled.
          * @hide
          */
@@ -10253,6 +10282,15 @@
                 "captive_portal_other_fallback_urls";
 
         /**
+         * A list of captive portal detection specifications used in addition to the fallback URLs.
+         * Each spec has the format url@@/@@statusCodeRegex@@/@@contentRegex. Specs are separated
+         * by "@@,@@".
+         * @hide
+         */
+        public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS =
+                "captive_portal_fallback_probe_specs";
+
+        /**
          * Whether to use HTTPS for network validation. This is enabled by default and the setting
          * needs to be set to 0 to disable it. This setting is a misnomer because captive portals
          * don't actually use HTTPS, but it's consistent with the other settings.
@@ -11566,6 +11604,13 @@
         /** @hide */ public static final int EMULATE_DISPLAY_CUTOUT_ON = 1;
 
         /**
+         * A colon separated list of keys for Settings Slices.
+         *
+         * @hide
+         */
+        public static final String BLOCKED_SLICES = "blocked_slices";
+
+        /**
          * Defines global zen mode.  ZEN_MODE_OFF, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
          * or ZEN_MODE_NO_INTERRUPTIONS.
          *
diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java
index 537065d..1d5c6f1 100644
--- a/core/java/android/webkit/SslErrorHandler.java
+++ b/core/java/android/webkit/SslErrorHandler.java
@@ -36,6 +36,10 @@
 
     /**
      * Proceed with the SSL certificate.
+     * <p>
+     * It is not recommended to proceed past SSL errors and this method should
+     * generally not be used; see {@link WebViewClient#onReceivedSslError} for
+     * more information.
      */
     public void proceed() {}
 
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 4aa1c4a..95fe963 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -113,6 +113,20 @@
      * the new WebView as an argument. If the host application chooses not to
      * honor the request, it should return {@code false} from this method. The default
      * implementation of this method does nothing and hence returns {@code false}.
+     * <p>
+     * Applications should typically not allow windows to be created when the
+     * {@code isUserGesture} flag is false, as this may be an unwanted popup.
+     * <p>
+     * Applications should be careful how they display the new window: don't simply
+     * overlay it over the existing WebView as this may mislead the user about which
+     * site they are viewing. If your application displays the URL of the main page,
+     * make sure to also display the URL of the new window in a similar fashion. If
+     * your application does not display URLs, consider disallowing the creation of
+     * new windows entirely.
+     * <p class="note"><b>Note:</b> There is no trustworthy way to tell which page
+     * requested the new window: the request might originate from a third-party iframe
+     * inside the WebView.
+     *
      * @param view The WebView from which the request for a new window
      *             originated.
      * @param isDialog {@code true} if the new window should be a dialog, rather than
@@ -149,6 +163,11 @@
      * from the view system if necessary. At this point, WebCore has stopped
      * any loading in this window and has removed any cross-scripting ability
      * in javascript.
+     * <p>
+     * As with {@link #onCreateWindow}, the application should ensure that any
+     * URL or security indicator displayed is updated so that the user can tell
+     * that the page they were interacting with has been closed.
+     *
      * @param window The WebView that needs to be closed.
      */
     public void onCloseWindow(WebView window) {}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 10748ac..d7f1d6e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1019,11 +1019,21 @@
      * If {@code null}, defaults to 'text/html'.
      * <p>
      * The 'data' scheme URL formed by this method uses the default US-ASCII
-     * charset. If you need need to set a different charset, you should form a
+     * charset. If you need to set a different charset, you should form a
      * 'data' scheme URL which explicitly specifies a charset parameter in the
      * mediatype portion of the URL and call {@link #loadUrl(String)} instead.
      * Note that the charset obtained from the mediatype portion of a data URL
      * always overrides that specified in the HTML or XML document itself.
+     * <p>
+     * Content loaded using this method will have a {@code window.origin} value
+     * of {@code "null"}. This must not be considered to be a trusted origin
+     * by the application or by any JavaScript code running inside the WebView
+     * (for example, event sources in DOM event handlers or web messages),
+     * because malicious content can also create frames with a null origin. If
+     * you need to identify the main frame's origin in a trustworthy way, you
+     * should use {@link #loadDataWithBaseURL(String,String,String,String,String)
+     * loadDataWithBaseURL()} with a valid HTTP or HTTPS base URL to set the
+     * origin.
      *
      * @param data a String of data in the given encoding
      * @param mimeType the MIME type of the data, e.g. 'text/html'.
@@ -1057,6 +1067,15 @@
      * <p>
      * Note that the baseUrl is sent in the 'Referer' HTTP header when
      * requesting subresources (images, etc.) of the page loaded using this method.
+     * <p>
+     * If a valid HTTP or HTTPS base URL is not specified in {@code baseUrl}, then
+     * content loaded using this method will have a {@code window.origin} value
+     * of {@code "null"}. This must not be considered to be a trusted origin
+     * by the application or by any JavaScript code running inside the WebView
+     * (for example, event sources in DOM event handlers or web messages),
+     * because malicious content can also create frames with a null origin. If
+     * you need to identify the main frame's origin in a trustworthy way, you
+     * should use a valid HTTP or HTTPS base URL to set the origin.
      *
      * @param baseUrl the URL to use as the page's base URL. If {@code null} defaults to
      *                'about:blank'.
@@ -2056,6 +2075,13 @@
      * <p>
      * A target origin can be set as a wildcard ("*"). However this is not recommended.
      * See the page above for security issues.
+     * <p>
+     * Content loaded via {@link #loadData(String,String,String)} will not have a
+     * valid origin, and thus cannot be sent messages securely. If you need to send
+     * messages using this function, you should use
+     * {@link #loadDataWithBaseURL(String,String,String,String,String)} with a valid
+     * HTTP or HTTPS {@code baseUrl} to define a valid origin that can be used for
+     * messaging.
      *
      * @param message the WebMessage
      * @param targetOrigin the target origin.
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 6df1655..f686b66 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -368,6 +368,15 @@
      * handler.proceed(). Note that the decision may be retained for use in
      * response to future SSL errors. The default behavior is to cancel the
      * load.
+     * <p>
+     * Applications are advised not to prompt the user about SSL errors, as
+     * the user is unlikely to be able to make an informed security decision
+     * and WebView does not provide any UI for showing the details of the
+     * error in a meaningful way.
+     * <p>
+     * Application overrides of this method may display custom error pages or
+     * silently log issues, but it is strongly recommended to always call
+     * handler.cancel() and never allow proceeding past errors.
      *
      * @param view The WebView that is initiating the callback.
      * @param handler An SslErrorHandler object that will handle the user's
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 7116f3a..d135040 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -207,6 +207,8 @@
                 mAvatarView.setTranslationY(0.0f);
                 mSenderName.setAlpha(1.0f);
                 mSenderName.setTranslationY(0.0f);
+                mIsolatedMessage = null;
+                mMessages = null;
                 sInstancePool.release(MessagingGroup.this);
             }
         };
@@ -267,6 +269,11 @@
 
     @Override
     public int getMeasuredType() {
+        if (mIsolatedMessage != null) {
+            // We only want to show one group if we have an inline image, so let's return shortened
+            // to avoid displaying the other ones.
+            return MEASURED_SHORTENED;
+        }
         boolean hasNormal = false;
         for (int i = mMessageContainer.getChildCount() - 1; i >= 0; i--) {
             View child = mMessageContainer.getChildAt(i);
@@ -292,9 +299,6 @@
                 }
             }
         }
-        if (mMessageContainer.getChildCount() == 0 && mIsolatedMessage != null) {
-            return mIsolatedMessage.getMeasuredType();
-        }
         return MEASURED_NORMAL;
     }
 
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 9048f87..c7ea781 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -206,7 +206,17 @@
         final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
 
         int childTop;
-        int childLeft = centerAligned ? left + (right - left) / 2 - mTotalWidth / 2 : 0;
+        int childLeft;
+        if (centerAligned) {
+            childLeft = mPaddingLeft + left + (right - left) / 2 - mTotalWidth / 2;
+        } else {
+            childLeft = mPaddingLeft;
+            int absoluteGravity = Gravity.getAbsoluteGravity(Gravity.START, getLayoutDirection());
+            if (absoluteGravity == Gravity.RIGHT) {
+                childLeft += right - left - mTotalWidth;
+            }
+        }
+
 
         // Where bottom of child should go
         final int height = bottom - top;
@@ -216,18 +226,6 @@
 
         final int count = getChildCount();
 
-        final int layoutDirection = getLayoutDirection();
-        switch (Gravity.getAbsoluteGravity(Gravity.START, layoutDirection)) {
-            case Gravity.RIGHT:
-                childLeft += mPaddingLeft + right - left - mTotalWidth;
-                break;
-
-            case Gravity.LEFT:
-            default:
-                childLeft += mPaddingLeft;
-                break;
-        }
-
         int start = 0;
         int dir = 1;
         //In case of RTL, start drawing from the last child.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2e8c7f9..471170b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -55,7 +55,7 @@
     <!-- Width of the navigation bar when it is placed vertically on the screen in car mode -->
     <dimen name="navigation_bar_width_car_mode">96dp</dimen>
     <!-- Height of notification icons in the status bar -->
-    <dimen name="status_bar_icon_size">24dip</dimen>
+    <dimen name="status_bar_icon_size">22dip</dimen>
     <!-- Size of the giant number (unread count) in the notifications -->
     <dimen name="status_bar_content_number_size">48sp</dimen>
     <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 5b7fc6e..fc00f1b 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -132,6 +132,7 @@
                     Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS,
                     Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
                     Settings.Global.BLE_SCAN_BACKGROUND_MODE,
+                    Settings.Global.BLOCKED_SLICES,
                     Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
                     Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
                     Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
@@ -155,6 +156,7 @@
                     Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
                     Settings.Global.CAPTIVE_PORTAL_MODE,
                     Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS,
+                    Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS,
                     Settings.Global.CAPTIVE_PORTAL_SERVER,
                     Settings.Global.CAPTIVE_PORTAL_USE_HTTPS,
                     Settings.Global.CAPTIVE_PORTAL_USER_AGENT,
@@ -591,6 +593,7 @@
                  Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
                  Settings.Secure.SKIP_FIRST_USE_HINTS, // candidate?
                  Settings.Secure.SMS_DEFAULT_APPLICATION,
+                 Settings.Secure.THEME_MODE,
                  Settings.Secure.TRUST_AGENTS_INITIALIZED,
                  Settings.Secure.TV_INPUT_CUSTOM_LABELS,
                  Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 9828275..a768dd3 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -207,8 +207,9 @@
      * after  {@link Image#close Image.close()} has been called.
      * </p>
      * @return the HardwareBuffer associated with this Image or null if this Image doesn't support
-     * this feature (e.g. {@link android.media.ImageWriter ImageWriter} or
-     * {@link android.media.MediaCodec MediaCodec} don't).
+     * this feature. (Unsupported use cases include Image instances obtained through
+     * {@link android.media.MediaCodec MediaCodec}, and on versions prior to Android P,
+     * {@link android.media.ImageWriter ImageWriter}).
      */
     @Nullable
     public HardwareBuffer getHardwareBuffer() {
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 397768a..4c0153f 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -20,6 +20,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.hardware.camera2.utils.SurfaceUtils;
+import android.hardware.HardwareBuffer;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -58,12 +59,17 @@
  * </p>
  * <p>
  * If the application already has an Image from {@link ImageReader}, the
- * application can directly queue this Image into ImageWriter (via
- * {@link #queueInputImage}), potentially with zero buffer copies. For the
- * {@link ImageFormat#PRIVATE PRIVATE} format Images produced by
- * {@link ImageReader}, this is the only way to send Image data to ImageWriter,
- * as the Image data aren't accessible by the application.
+ * application can directly queue this Image into the ImageWriter (via
+ * {@link #queueInputImage}), potentially with zero buffer copies. This
+ * even works if the image format of the ImageWriter is
+ * {@link ImageFormat#PRIVATE PRIVATE}, and prior to Android P is the only
+ * way to enqueue images into such an ImageWriter. Starting in Android P
+ * private images may also be accessed through their hardware buffers
+ * (when available) through the {@link Image#getHardwareBuffer()} method.
+ * Attempting to access the planes of a private image, will return an
+ * empty array.
  * </p>
+ * <p>
  * Once new input Images are queued into an ImageWriter, it's up to the
  * downstream components (e.g. {@link ImageReader} or
  * {@link android.hardware.camera2.CameraDevice}) to consume the Images. If the
@@ -257,29 +263,26 @@
      * <p>
      * If the format of ImageWriter is {@link ImageFormat#PRIVATE PRIVATE} (
      * {@link ImageWriter#getFormat()} == {@link ImageFormat#PRIVATE}), the
-     * image buffer is inaccessible to the application, and calling this method
-     * will result in an {@link IllegalStateException}. Instead, the application
-     * should acquire images from some other component (e.g. an
+     * image buffer is accessible to the application only through the hardware
+     * buffer obtained through {@link Image#getHardwareBuffer()}. (On Android
+     * versions prior to P, dequeueing private buffers will cause an
+     * {@link IllegalStateException} to be thrown). Alternatively,
+     * the application can acquire images from some other component (e.g. an
      * {@link ImageReader}), and queue them directly to this ImageWriter via the
      * {@link ImageWriter#queueInputImage queueInputImage()} method.
      * </p>
      *
      * @return The next available input Image from this ImageWriter.
      * @throws IllegalStateException if {@code maxImages} Images are currently
-     *             dequeued, or the ImageWriter format is
-     *             {@link ImageFormat#PRIVATE PRIVATE}, or the input
-     *             {@link android.view.Surface Surface} has been abandoned by the
-     *             consumer component that provided the {@link android.view.Surface Surface}.
+     *             dequeued, or the input {@link android.view.Surface Surface}
+     *             has been abandoned by the consumer component that provided
+     *             the {@link android.view.Surface Surface}. Prior to Android
+     *             P, throws if the ImageWriter format is
+     *             {@link ImageFormat#PRIVATE PRIVATE}.
      * @see #queueInputImage
      * @see Image#close
      */
     public Image dequeueInputImage() {
-        if (mWriterFormat == ImageFormat.PRIVATE) {
-            throw new IllegalStateException(
-                    "PRIVATE format ImageWriter doesn't support this operation since the images are"
-                            + " inaccessible to the application!");
-        }
-
         if (mDequeuedImages.size() >= mMaxImages) {
             throw new IllegalStateException("Already dequeued max number of Images " + mMaxImages);
         }
@@ -743,6 +746,13 @@
         }
 
         @Override
+        public HardwareBuffer getHardwareBuffer() {
+            throwISEIfImageIsInvalid();
+
+            return nativeGetHardwareBuffer();
+        }
+
+        @Override
         public Plane[] getPlanes() {
             throwISEIfImageIsInvalid();
 
@@ -863,6 +873,8 @@
         private synchronized native int nativeGetHeight();
 
         private synchronized native int nativeGetFormat();
+
+        private synchronized native HardwareBuffer nativeGetHardwareBuffer();
     }
 
     // Native implemented ImageWriter methods.
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 392a1eb..ada91be 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2848,8 +2848,12 @@
                     mInbandTrackIndices.set(i);
                 }
 
+                if (tracks[i] == null) {
+                    Log.w(TAG, "unexpected NULL track at index " + i);
+                }
                 // newly appeared inband track
-                if (tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
+                if (tracks[i] != null
+                        && tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
                     SubtitleTrack track = mSubtitleController.addTrack(
                             tracks[i].getFormat());
                     mIndexTrackPairs.add(Pair.create(i, track));
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 11659e4..f8f7a90 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -25,11 +25,14 @@
 #include <gui/Surface.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
+#include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <private/android/AHardwareBufferHelpers.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 
 #include <stdint.h>
 #include <inttypes.h>
+#include <android/hardware_buffer_jni.h>
 
 #define IMAGE_BUFFER_JNI_ID           "mNativeBuffer"
 #define IMAGE_FORMAT_UNKNOWN          0 // This is the same value as ImageFormat#UNKNOWN.
@@ -701,6 +704,20 @@
     return static_cast<jint>(publicFmt);
 }
 
+static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
+    GraphicBuffer* buffer;
+    Image_getNativeContext(env, thiz, &buffer, NULL);
+    if (buffer == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                "Image is not initialized");
+        return NULL;
+    }
+    AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer);
+    // don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us
+    // to link against libandroid.so
+    return android_hardware_HardwareBuffer_createFromAHardwareBuffer(env, b);
+}
+
 static void Image_setFenceFd(JNIEnv* env, jobject thiz, int fenceFd) {
     ALOGV("%s:", __FUNCTION__);
     env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd));
@@ -818,10 +835,12 @@
 
 static JNINativeMethod gImageMethods[] = {
     {"nativeCreatePlanes",      "(II)[Landroid/media/ImageWriter$WriterSurfaceImage$SurfacePlane;",
-                                                              (void*)Image_createSurfacePlanes },
-    {"nativeGetWidth",         "()I",                         (void*)Image_getWidth },
-    {"nativeGetHeight",        "()I",                         (void*)Image_getHeight },
-    {"nativeGetFormat",        "()I",                         (void*)Image_getFormat },
+                                                               (void*)Image_createSurfacePlanes },
+    {"nativeGetWidth",          "()I",                         (void*)Image_getWidth },
+    {"nativeGetHeight",         "()I",                         (void*)Image_getHeight },
+    {"nativeGetFormat",         "()I",                         (void*)Image_getFormat },
+    {"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;",
+                                                               (void*)Image_getHardwareBuffer },
 };
 
 int register_android_media_ImageWriter(JNIEnv *env) {
diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index d0973f0..b8353ee 100644
--- a/packages/SettingsLib/res/values-as/arrays.xml
+++ b/packages/SettingsLib/res/values-as/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"সংযোগৰ ক্ষমতা অনুযায়ী সৰ্বোত্তম"</item>
     <item msgid="364670732877872677">"উত্তম প্ৰচেষ্টা (অভিযোজিত বিট ৰেইট)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">", সক্ৰিয়"</item>
+    <item msgid="8962366465966010158">", সক্ৰিয় (মিডিয়া)"</item>
+    <item msgid="4046665544396189228">", সক্ৰিয় (ফ\'ন)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"অফ কৰক"</item>
     <item msgid="1593289376502312923">"৬৪কে."</item>
diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml
index e186bb4..6340034 100644
--- a/packages/SettingsLib/res/values-bn/arrays.xml
+++ b/packages/SettingsLib/res/values-bn/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"সংযোগের গুণমানের জন্য অপটিমাইজ করা হয়েছে"</item>
     <item msgid="364670732877872677">"সেরা প্রচেষ্টা (অ্যাডাপ্টিভ বিট রেট)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">", চালু আছে"</item>
+    <item msgid="8962366465966010158">", চালু আছে (মিডিয়া)"</item>
+    <item msgid="4046665544396189228">", চালু আছে (ফোন)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"বন্ধ আছে"</item>
     <item msgid="1593289376502312923">"৬৪K"</item>
diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml
index 30a9339..ecf5525 100644
--- a/packages/SettingsLib/res/values-gu/arrays.xml
+++ b/packages/SettingsLib/res/values-gu/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"કનેક્શનની ગુણવત્તા માટે ઓપ્ટિમાઇઝ કર્યું"</item>
     <item msgid="364670732877872677">"ઉત્તમ પ્રયાસ (અનુકૂલનશીલ બિટ રેટ)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">", સક્રિય"</item>
+    <item msgid="8962366465966010158">", સક્રિય (મીડિયા)"</item>
+    <item msgid="4046665544396189228">", સક્રિય (ફોન)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"બંધ"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 0e34b77..d1d0157 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -140,9 +140,9 @@
   </string-array>
   <string-array name="bluetooth_audio_active_device_summaries">
     <item msgid="4862957058729193940"></item>
-    <item msgid="6481691720774549651">"चालू है"</item>
-    <item msgid="8962366465966010158">"चालू है (सिर्फ़ मीडिया के लिए)"</item>
-    <item msgid="4046665544396189228">"चालू है (सिर्फ़ फ़ोन के लिए)"</item>
+    <item msgid="6481691720774549651">", चालू है"</item>
+    <item msgid="8962366465966010158">", चालू है (सिर्फ़ मीडिया के लिए)"</item>
+    <item msgid="4046665544396189228">", चालू है (सिर्फ़ फ़ोन के लिए)"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"बंद"</item>
diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml
index 3aef4ce..b073371 100644
--- a/packages/SettingsLib/res/values-is/arrays.xml
+++ b/packages/SettingsLib/res/values-is/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"Fínstillt fyrir gæði tengingar"</item>
     <item msgid="364670732877872677">"Bestu mögulegu gæði (breytilegur bitahraði)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">", virkt"</item>
+    <item msgid="8962366465966010158">", virkt (hljóð- og myndefni)"</item>
+    <item msgid="4046665544396189228">", virkt (sími)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Slökkt"</item>
     <item msgid="1593289376502312923">"64 k"</item>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
index 037c23f..8b6b8ca 100644
--- a/packages/SettingsLib/res/values-iw/arrays.xml
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"אופטימיזציה להשגת איכות חיבור מרבית"</item>
     <item msgid="364670732877872677">"האיכות הטובה ביותר (קצב העברת נתונים מותאם)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">", פעיל"</item>
+    <item msgid="8962366465966010158">", פעיל (מדיה)"</item>
+    <item msgid="4046665544396189228">", פעיל (טלפון)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"כבוי"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml
index a66af95..75d5dcb 100644
--- a/packages/SettingsLib/res/values-kn/arrays.xml
+++ b/packages/SettingsLib/res/values-kn/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"ಸಂಪರ್ಕ ಗುಣಮಟ್ಟಕ್ಕಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</item>
     <item msgid="364670732877872677">"ಅತ್ಯುತ್ತಮ ಪ್ರಯತ್ನ (ಹೊಂದಿಸಬಹುದಾದ ಬಿಟ್ ಪ್ರಮಾಣ)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">"ಸಕ್ರಿಯ"</item>
+    <item msgid="8962366465966010158">", ಸಕ್ರಿಯ (ಮಾಧ್ಯಮ)"</item>
+    <item msgid="4046665544396189228">", ಸಕ್ರಿಯ (ಫೋನ್)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ಆಫ್"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml
index 74689e3..3337913 100644
--- a/packages/SettingsLib/res/values-ml/arrays.xml
+++ b/packages/SettingsLib/res/values-ml/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"കണക്ഷൻ നിലവാരമുയർത്താൻ ഒപ്‌റ്റിമൈസ് ചെയ്‌തു"</item>
     <item msgid="364670732877872677">"മികച്ച സംവിധാനം (അനുയോജ്യമായ ബിറ്റ് റേറ്റ്)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">", സജീവം"</item>
+    <item msgid="8962366465966010158">", സജീവ (മീഡിയ)"</item>
+    <item msgid="4046665544396189228">", സജീവമായ (ഫോൺ)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ഓഫ്"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml
index 841e564..f5bf2ea 100644
--- a/packages/SettingsLib/res/values-mr/arrays.xml
+++ b/packages/SettingsLib/res/values-mr/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"कनेक्शन गुणवत्तेसाठी ऑप्टिमाइझ केले"</item>
     <item msgid="364670732877872677">"सर्वोत्तम प्रयत्न (अनुकूल बिट रेट)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">", अॅक्टिव्ह"</item>
+    <item msgid="8962366465966010158">", अॅक्टिव्ह (मीडिया)"</item>
+    <item msgid="4046665544396189228">", अॅक्टिव्ह (फोन)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"बंद"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index 95a26d2..4ed55cc 100644
--- a/packages/SettingsLib/res/values-ne/arrays.xml
+++ b/packages/SettingsLib/res/values-ne/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"जडानको गुणस्तर सुधार्न अनुकूलन गरिएको"</item>
     <item msgid="364670732877872677">"उत्कृष्ट प्रयास (अनुकूलनीय बिट दर)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">", सक्रिय"</item>
+    <item msgid="8962366465966010158">", सक्रिय (मिडिया)"</item>
+    <item msgid="4046665544396189228">", सक्रिय (फोन)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"निष्क्रिय गर्नुहोस्"</item>
     <item msgid="1593289376502312923">"६४के"</item>
diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml
index 9da5d6e..18de8f9 100644
--- a/packages/SettingsLib/res/values-or/arrays.xml
+++ b/packages/SettingsLib/res/values-or/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"ସଂଯୋଗର ଗୁଣବତ୍ତା ପାଇଁ ଅନୁକୂଳିତ"</item>
     <item msgid="364670732877872677">"ସର୍ବୋତ୍ତମ ପ୍ରୟାସ (ଅନୁକୂଳ ବିଟ୍‌ ରେଟ୍‌)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">", ସକ୍ରିୟ"</item>
+    <item msgid="8962366465966010158">", ସକ୍ରିୟ (ମିଡିଆ)"</item>
+    <item msgid="4046665544396189228">", ସକ୍ରିୟ (ଫୋନ୍)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ଅଫ୍"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index 2b3b919..ddb40ce 100644
--- a/packages/SettingsLib/res/values-te/arrays.xml
+++ b/packages/SettingsLib/res/values-te/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"కనెక్షన్ నాణ్యత కోసం అనుకూలీకరించబడింది"</item>
     <item msgid="364670732877872677">"ఉత్తమ కృషి (అనుకూల బిట్ రేట్)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">", సక్రియంగా ఉంది"</item>
+    <item msgid="8962366465966010158">", (మీడియా) సక్రియంగా ఉంది"</item>
+    <item msgid="4046665544396189228">", (ఫోన్) సక్రియంగా ఉంది"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ఆఫ్"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml
index 6e2f904..c6eadd98 100644
--- a/packages/SettingsLib/res/values-ur/arrays.xml
+++ b/packages/SettingsLib/res/values-ur/arrays.xml
@@ -138,9 +138,12 @@
     <item msgid="4681409244565426925">"کنکشن کے معیار کیلئے بہتر بنایا گيا"</item>
     <item msgid="364670732877872677">"بہترین کوشش (اڈاپٹیو بٹ ریٹ)"</item>
   </string-array>
-    <!-- no translation found for bluetooth_audio_active_device_summaries:1 (6481691720774549651) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:2 (8962366465966010158) -->
-    <!-- no translation found for bluetooth_audio_active_device_summaries:3 (4046665544396189228) -->
+  <string-array name="bluetooth_audio_active_device_summaries">
+    <item msgid="4862957058729193940"></item>
+    <item msgid="6481691720774549651">"، فعال"</item>
+    <item msgid="8962366465966010158">"، فعال (میڈیا)"</item>
+    <item msgid="4046665544396189228">"، فعال (فون)"</item>
+  </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"آف"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index e902c92..b5d48b4 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -42,7 +42,7 @@
             android:singleLine="true"
             android:ellipsize="start"
             android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
-            android:imeOptions="actionSend" />
+            android:imeOptions="actionSend|flagNoExtractUi|flagNoFullscreen" />
 
     <FrameLayout
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c1458b2..0c7c600 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -34,10 +34,10 @@
     <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
 
     <!-- Height of the battery icon in the status bar. -->
-    <dimen name="status_bar_battery_icon_height">14.5dp</dimen>
+    <dimen name="status_bar_battery_icon_height">13.0dp</dimen>
 
     <!-- Width of the battery icon in the status bar. -->
-    <dimen name="status_bar_battery_icon_width">9.5dp</dimen>
+    <dimen name="status_bar_battery_icon_width">8.5dp</dimen>
 
     <!-- The font size for the clock in the status bar. -->
     <dimen name="status_bar_clock_size">14sp</dimen>
@@ -960,6 +960,10 @@
             add about 88dp of height to the notifications. -->
     <dimen name="smart_reply_button_max_height">100dp</dimen>
 
+    <!-- The extra height that we allow a notification with a remote input history to be taller than
+         the regular notification, when we have remote input history texts present. -->
+    <dimen name="remote_input_history_extra_height">60dp</dimen>
+
     <!-- Fingerprint Dialog values -->
     <dimen name="fingerprint_dialog_fp_icon_size">64dp</dimen>
     <dimen name="fingerprint_dialog_animation_translation_offset">350dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index beb3c53..9c9f021 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -151,7 +151,6 @@
 
     @Override
     public void start() {
-        sDependency = this;
         // TODO: Think about ways to push these creation rules out of Dependency to cut down
         // on imports.
         mProviders.put(TIME_TICK_HANDLER, () -> {
@@ -331,6 +330,8 @@
 
         // Put all dependencies above here so the factory can override them if it wants.
         SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
+
+        sDependency = this;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 139215a0..2746a71 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -22,8 +22,8 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
-import static com.android.systemui.tuner.TunablePadding.FLAG_START;
 import static com.android.systemui.tuner.TunablePadding.FLAG_END;
+import static com.android.systemui.tuner.TunablePadding.FLAG_START;
 
 import android.annotation.Dimension;
 import android.app.Fragment;
@@ -66,6 +66,7 @@
 import com.android.systemui.tuner.TunablePadding;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.leak.RotationUtils;
 
 /**
  * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
@@ -77,6 +78,9 @@
     private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
             SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
 
+    private DisplayManager mDisplayManager;
+    private DisplayManager.DisplayListener mDisplayListener;
+
     private int mRoundedDefault;
     private int mRoundedDefaultTop;
     private int mRoundedDefaultBottom;
@@ -84,7 +88,7 @@
     private View mBottomOverlay;
     private float mDensity;
     private WindowManager mWindowManager;
-    private boolean mLandscape;
+    private int mRotation;
 
     @Override
     public void start() {
@@ -104,6 +108,28 @@
         if (padding != 0) {
             setupPadding(padding);
         }
+
+        mDisplayListener = new DisplayManager.DisplayListener() {
+            @Override
+            public void onDisplayAdded(int displayId) {
+                // do nothing
+            }
+
+            @Override
+            public void onDisplayRemoved(int displayId) {
+                // do nothing
+            }
+
+            @Override
+            public void onDisplayChanged(int displayId) {
+                updateOrientation();
+            }
+        };
+
+        mRotation = -1;
+        mDisplayManager = (DisplayManager) mContext.getSystemService(
+                Context.DISPLAY_SERVICE);
+        mDisplayManager.registerDisplayListener(mDisplayListener, null);
     }
 
     private void setupDecorations() {
@@ -169,17 +195,22 @@
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
-        boolean newLanscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
-        if (newLanscape != mLandscape) {
-            mLandscape = newLanscape;
+        updateOrientation();
+    }
+
+    protected void updateOrientation() {
+        int newRotation = RotationUtils.getExactRotation(mContext);
+        if (newRotation != mRotation) {
+            mRotation = newRotation;
 
             if (mOverlay != null) {
                 updateLayoutParams();
                 updateViews();
             }
-        }
-        if (shouldDrawCutout() && mOverlay == null) {
-            setupDecorations();
+
+            if (shouldDrawCutout() && mOverlay == null) {
+                setupDecorations();
+            }
         }
     }
 
@@ -188,16 +219,28 @@
         View topRight = mOverlay.findViewById(R.id.right);
         View bottomLeft = mBottomOverlay.findViewById(R.id.left);
         View bottomRight = mBottomOverlay.findViewById(R.id.right);
-        if (mLandscape) {
-            // Flip corners
-            View tmp = topRight;
-            topRight = bottomLeft;
-            bottomLeft = tmp;
+
+        if (mRotation == RotationUtils.ROTATION_NONE) {
+            updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
+            updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90);
+            updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
+            updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
+        } else if (mRotation == RotationUtils.ROTATION_LANDSCAPE) {
+            updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
+            updateView(topRight, Gravity.BOTTOM | Gravity.LEFT, 270);
+            updateView(bottomLeft, Gravity.TOP | Gravity.RIGHT, 90);;
+            updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
+        } else if (mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
+            updateView(topLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
+            updateView(topRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
+            updateView(bottomLeft, Gravity.TOP | Gravity.LEFT, 0);
+            updateView(bottomRight, Gravity.TOP | Gravity.RIGHT, 90);
+        } else if (mRotation == RotationUtils.ROTATION_SEASCAPE) {
+            updateView(topLeft, Gravity.BOTTOM | Gravity.RIGHT, 180);
+            updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90);
+            updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
+            updateView(bottomRight, Gravity.TOP | Gravity.LEFT, 0);
         }
-        updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
-        updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90);
-        updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
-        updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
 
         updateWindowVisibilities();
     }
@@ -269,9 +312,14 @@
         }
 
         lp.setTitle("ScreenDecorOverlay");
-        lp.gravity = Gravity.TOP | Gravity.LEFT;
+        if (mRotation == RotationUtils.ROTATION_SEASCAPE
+                || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
+            lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+        } else {
+            lp.gravity = Gravity.TOP | Gravity.LEFT;
+        }
         lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        if (mLandscape) {
+        if (isLandscape(mRotation)) {
             lp.width = WRAP_CONTENT;
             lp.height = MATCH_PARENT;
         }
@@ -281,7 +329,12 @@
     private WindowManager.LayoutParams getBottomLayoutParams() {
         WindowManager.LayoutParams lp = getWindowLayoutParams();
         lp.setTitle("ScreenDecorOverlayBottom");
-        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+        if (mRotation == RotationUtils.ROTATION_SEASCAPE
+                || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
+            lp.gravity = Gravity.TOP | Gravity.LEFT;
+        } else {
+            lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+        }
         return lp;
     }
 
@@ -568,4 +621,9 @@
             return cutoutBounds;
         }
     }
+
+    private boolean isLandscape(int rotation) {
+        return rotation == RotationUtils.ROTATION_LANDSCAPE || rotation ==
+                RotationUtils.ROTATION_SEASCAPE;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 721890f..03a6398 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -56,6 +56,7 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 
 import java.util.function.Consumer;
@@ -148,5 +149,7 @@
         providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
         providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
         providers.put(SmartReplyController.class, () -> new SmartReplyController());
+        providers.put(RemoteInputQuickSettingsDisabler.class,
+                () -> new RemoteInputQuickSettingsDisabler(context));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 90140ff..f14d396 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -42,7 +42,7 @@
      * hiding the wallpaper and changing the display mode is necessary to hide
      * the black frame that's inherent to hardware specs.
      */
-    public static final int ENTER_DOZE_HIDE_WALLPAPER_DELAY = 2000;
+    public static final int ENTER_DOZE_HIDE_WALLPAPER_DELAY = 4500;
 
     private final DozeMachine.Service mDozeService;
     private final Handler mHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index cbd1ca1..b146cfc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -35,6 +35,7 @@
 import android.view.ViewTreeObserver;
 import android.widget.FrameLayout.LayoutParams;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.R.id;
@@ -43,6 +44,7 @@
 import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks {
@@ -72,6 +74,9 @@
     private float mLastQSExpansion = -1;
     private boolean mQsDisabled;
 
+    private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler =
+            Dependency.get(RemoteInputQuickSettingsDisabler.class);
+
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
             Bundle savedInstanceState) {
@@ -191,6 +196,8 @@
 
     @Override
     public void disable(int state1, int state2, boolean animate) {
+        state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
+
         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
         if (disabled == mQsDisabled) return;
         mQsDisabled = disabled;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index 32e51b3..9f82bcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -19,8 +19,10 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.view.DisplayCutout;
 import android.view.View;
 import android.widget.TextView;
 
@@ -44,9 +46,10 @@
     private boolean mPublicMode;
     private int mMaxWidth;
     private View mRootView;
-    private int mLeftCutOutInset;
-    private int mLeftInset;
+    private int mSysWinInset;
+    private int mCutOutInset;
     private Rect mIconDrawingRect = new Rect();
+    private Point mPoint;
     private Runnable mOnDrawingRectChangedListener;
 
     public HeadsUpStatusBarView(Context context) {
@@ -137,9 +140,20 @@
         int bottom = top + mIconPlaceholder.getHeight();
         mLayoutedIconRect.set(left, top, right, bottom);
         updateDrawingRect();
-        int targetPadding = mAbsoluteStartPadding + mLeftInset + mLeftCutOutInset;
+        int targetPadding = mAbsoluteStartPadding + mSysWinInset + mCutOutInset;
         if (left != targetPadding) {
-            int newPadding = targetPadding - left + getPaddingStart();
+            int start;
+            if (isLayoutRtl()) {
+                if (mPoint == null) {
+                    mPoint = new Point();
+                }
+                getDisplay().getRealSize(mPoint);
+                start = (mPoint.x - right);
+            } else {
+                start = left;
+            }
+
+            int newPadding = targetPadding - start + getPaddingStart();
             setPaddingRelative(newPadding, 0, mEndMargin, 0);
         }
         if (mFirstLayout) {
@@ -152,7 +166,11 @@
     }
 
     public void setPanelTranslation(float translationX) {
-        setTranslationX(translationX - mLeftCutOutInset);
+        if (isLayoutRtl()) {
+            setTranslationX(translationX + mCutOutInset);
+        } else {
+            setTranslationX(translationX - mCutOutInset);
+        }
         updateDrawingRect();
     }
 
@@ -167,15 +185,19 @@
 
     @Override
     protected boolean fitSystemWindows(Rect insets) {
-        mLeftInset = insets.left;
-        mLeftCutOutInset = (getRootWindowInsets().getDisplayCutout() != null)
-                ? getRootWindowInsets().getDisplayCutout().getSafeInsetLeft() : 0;
-
+        boolean isRtl = isLayoutRtl();
+        mSysWinInset = isRtl ? insets.right : insets.left;
+        DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
+        mCutOutInset = (displayCutout != null)
+                ? (isRtl ? displayCutout.getSafeInsetRight() : displayCutout.getSafeInsetLeft())
+                : 0;
         // For Double Cut Out mode, the System window navigation bar is at the right
-        // hand side of the left cut out. In this condition, mLeftInset include the left cut
-        // out width so we set mLeftCutOutInset to be 0.
-        if (mLeftInset != 0) {
-            mLeftCutOutInset = 0;
+        // side of the left cut out. In this condition, mSysWinInset include the left cut
+        // out width so we set mCutOutInset to be 0. For RTL, the condition is the same.
+        // The navigation bar is at the left side of the right cut out and include the
+        // right cut out width.
+        if (mSysWinInset != 0) {
+            mCutOutInset = 0;
         }
 
         return super.fitSystemWindows(insets);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 952c961..70dad19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -189,6 +189,7 @@
             if (mExpandedSmartReplyView != null) {
                 notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit();
             }
+            notificationMaxHeight += mExpandedWrapper.getExtraMeasureHeight();
             int size = notificationMaxHeight;
             ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
             boolean useExactly = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 419e262..e24d65a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -32,10 +32,13 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
+import android.app.Person;
 import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.notification.NotificationListenerService.Ranking;
@@ -50,6 +53,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
@@ -117,6 +121,11 @@
          */
         public Boolean mIsSystemNotification;
 
+        /**
+         * Has the user sent a reply through this Notification.
+         */
+        private boolean hasSentReply;
+
         public Entry(StatusBarNotification n) {
             this.key = n.getKey();
             this.notification = n;
@@ -298,6 +307,40 @@
             lastRemoteInputSent = NOT_LAUNCHED_YET;
             remoteInputTextWhenReset = null;
         }
+
+        public void setHasSentReply() {
+            hasSentReply = true;
+        }
+
+        public boolean isLastMessageFromReply() {
+            if (!hasSentReply) {
+                return false;
+            }
+            Bundle extras = notification.getNotification().extras;
+            CharSequence[] replyTexts = extras.getCharSequenceArray(
+                    Notification.EXTRA_REMOTE_INPUT_HISTORY);
+            if (!ArrayUtils.isEmpty(replyTexts)) {
+                return true;
+            }
+            Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+            if (messages != null && messages.length > 0) {
+                Parcelable message = messages[messages.length - 1];
+                if (message instanceof Bundle) {
+                    Notification.MessagingStyle.Message lastMessage =
+                            Notification.MessagingStyle.Message.getMessageFromBundle(
+                                    (Bundle) message);
+                    if (lastMessage != null) {
+                        Person senderPerson = lastMessage.getSenderPerson();
+                        if (senderPerson == null) {
+                            return true;
+                        }
+                        Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
+                        return Objects.equals(user, senderPerson);
+                    }
+                }
+            }
+            return false;
+        }
     }
 
     private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index d040f7c..ba265d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -18,13 +18,13 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.car.user.CarUserManagerHelper;
 import android.content.Context;
 import android.view.View;
 import android.view.ViewStub;
 
 import android.support.v7.widget.GridLayoutManager;
 
+import com.android.settingslib.users.UserManagerHelper;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.StatusBar;
 
@@ -37,7 +37,7 @@
     private final UserGridRecyclerView mUserGridView;
     private final int mShortAnimDuration;
     private final StatusBar mStatusBar;
-    private final CarUserManagerHelper mCarUserManagerHelper;
+    private final UserManagerHelper mUserManagerHelper;
     private int mCurrentForegroundUserId;
     private boolean mShowing;
 
@@ -52,7 +52,7 @@
         mUserGridView.buildAdapter();
         mUserGridView.setUserSelectionListener(this::onUserSelected);
 
-        mCarUserManagerHelper = new CarUserManagerHelper(context);
+        mUserManagerHelper = new UserManagerHelper(context);
         updateCurrentForegroundUser();
 
         mShortAnimDuration = mContainer.getResources()
@@ -84,11 +84,11 @@
     }
 
     private boolean foregroundUserChanged() {
-        return mCurrentForegroundUserId != mCarUserManagerHelper.getCurrentForegroundUserId();
+        return mCurrentForegroundUserId != mUserManagerHelper.getForegroundUserId();
     }
 
     private void updateCurrentForegroundUser() {
-        mCurrentForegroundUserId = mCarUserManagerHelper.getCurrentForegroundUserId();
+        mCurrentForegroundUserId = mUserManagerHelper.getForegroundUserId();
     }
 
     private void onUserSelected(UserGridRecyclerView.UserRecord record) {
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 dda25ec..aaf1989 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -21,7 +21,6 @@
 import android.app.AlertDialog;
 import android.app.AlertDialog.Builder;
 import android.app.Dialog;
-import android.car.user.CarUserManagerHelper;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.UserInfo;
@@ -42,6 +41,7 @@
 import androidx.car.widget.PagedListView;
 
 import com.android.internal.util.UserIcons;
+import com.android.settingslib.users.UserManagerHelper;
 import com.android.systemui.R;
 
 import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -53,16 +53,16 @@
  * One of the uses of this is for the lock screen in auto.
  */
 public class UserGridRecyclerView extends PagedListView implements
-        CarUserManagerHelper.OnUsersUpdateListener {
+        UserManagerHelper.OnUsersUpdateListener {
     private UserSelectionListener mUserSelectionListener;
     private UserAdapter mAdapter;
-    private CarUserManagerHelper mCarUserManagerHelper;
+    private UserManagerHelper mUserManagerHelper;
     private Context mContext;
 
     public UserGridRecyclerView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
-        mCarUserManagerHelper = new CarUserManagerHelper(mContext);
+        mUserManagerHelper = new UserManagerHelper(mContext);
     }
 
     /**
@@ -71,7 +71,7 @@
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
-        mCarUserManagerHelper.registerOnUsersUpdateListener(this);
+        mUserManagerHelper.registerOnUsersUpdateListener(this);
     }
 
     /**
@@ -80,7 +80,7 @@
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mCarUserManagerHelper.unregisterOnUsersUpdateListener();
+        mUserManagerHelper.unregisterOnUsersUpdateListener();
     }
 
     /**
@@ -89,7 +89,7 @@
      * @return the adapter
      */
     public void buildAdapter() {
-        List<UserRecord> userRecords = createUserRecords(mCarUserManagerHelper
+        List<UserRecord> userRecords = createUserRecords(mUserManagerHelper
                 .getAllUsers());
         mAdapter = new UserAdapter(mContext, userRecords);
         super.setAdapter(mAdapter);
@@ -103,19 +103,19 @@
                 continue;
             }
             boolean isForeground =
-                mCarUserManagerHelper.getCurrentForegroundUserId() == userInfo.id;
+                mUserManagerHelper.getForegroundUserId() == userInfo.id;
             UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */,
                     false /* isAddUser */, isForeground);
             userRecords.add(record);
         }
 
         // Add guest user record if the foreground user is not a guest
-        if (!mCarUserManagerHelper.isForegroundUserGuest()) {
+        if (!mUserManagerHelper.foregroundUserIsGuestUser()) {
             userRecords.add(addGuestUserRecord());
         }
 
         // Add add user record if the foreground user can add users
-        if (mCarUserManagerHelper.canForegroundUserAddUsers()) {
+        if (mUserManagerHelper.foregroundUserCanAddUsers()) {
             userRecords.add(addUserRecord());
         }
 
@@ -149,7 +149,7 @@
     @Override
     public void onUsersUpdate() {
         mAdapter.clearUsers();
-        mAdapter.updateUsers(createUserRecords(mCarUserManagerHelper.getAllUsers()));
+        mAdapter.updateUsers(createUserRecords(mUserManagerHelper.getAllUsers()));
         mAdapter.notifyDataSetChanged();
     }
 
@@ -213,7 +213,7 @@
 
                 // If the user selects Guest, start the guest session.
                 if (userRecord.mIsStartGuestSession) {
-                    mCarUserManagerHelper.startNewGuestSession(mGuestName);
+                    mUserManagerHelper.startNewGuestSession(mGuestName);
                     return;
                 }
 
@@ -240,14 +240,14 @@
                     return;
                 }
                 // If the user doesn't want to be a guest or add a user, switch to the user selected
-                mCarUserManagerHelper.switchToUser(userRecord.mInfo);
+                mUserManagerHelper.switchToUser(userRecord.mInfo);
             });
 
         }
 
         private Bitmap getUserRecordIcon(UserRecord userRecord) {
             if (userRecord.mIsStartGuestSession) {
-                return mCarUserManagerHelper.getGuestDefaultIcon();
+                return mUserManagerHelper.getGuestDefaultIcon();
             }
 
             if (userRecord.mIsAddUser) {
@@ -255,7 +255,7 @@
                     .getDrawable(R.drawable.car_add_circle_round));
             }
 
-            return mCarUserManagerHelper.getUserIcon(userRecord.mInfo);
+            return mUserManagerHelper.getUserIcon(userRecord.mInfo);
         }
 
         @Override
@@ -273,10 +273,7 @@
 
             @Override
             protected UserInfo doInBackground(String... userNames) {
-                // Default to create a non admin user for now. Need to add logic
-                // for user to choose whether they want to create an admin or non-admin
-                // user later.
-                return mCarUserManagerHelper.createNewNonAdminUser(userNames[0]);
+                return mUserManagerHelper.createNewUser(userNames[0]);
             }
 
             @Override
@@ -286,7 +283,7 @@
             @Override
             protected void onPostExecute(UserInfo user) {
                 if (user != null) {
-                    mCarUserManagerHelper.switchToUser(user);
+                    mUserManagerHelper.switchToUser(user);
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 2d983c4..03791c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -34,6 +34,7 @@
 import com.android.internal.util.NotificationColorUtil;
 import com.android.internal.widget.NotificationActionListLayout;
 import com.android.systemui.Dependency;
+import com.android.systemui.R;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -58,6 +59,7 @@
     private NotificationActionListLayout mActions;
     private ArraySet<PendingIntent> mCancelledPendingIntents = new ArraySet<>();
     private UiOffloadThread mUiOffloadThread;
+    private View mRemoteInputHistory;
 
     protected NotificationTemplateViewWrapper(Context ctx, View view,
             ExpandableNotificationRow row) {
@@ -146,6 +148,8 @@
         mActionsContainer = mView.findViewById(com.android.internal.R.id.actions_container);
         mActions = mView.findViewById(com.android.internal.R.id.actions);
         mReplyAction = mView.findViewById(com.android.internal.R.id.reply_icon_action);
+        mRemoteInputHistory = mView.findViewById(
+                com.android.internal.R.id.notification_material_reply_container);
         updatePendingIntentCancellations();
     }
 
@@ -329,6 +333,10 @@
         if (mActions != null) {
             extra = mActions.getExtraMeasureHeight();
         }
+        if (mRemoteInputHistory != null && mRemoteInputHistory.getVisibility() != View.GONE) {
+            extra += mRow.getContext().getResources().getDimensionPixelSize(
+                    R.dimen.remote_input_history_extra_height);
+        }
         return extra + super.getExtraMeasureHeight();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 6d78d1d..b52e324 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -16,8 +16,10 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.View;
+import android.view.WindowInsets;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
@@ -59,6 +61,7 @@
     private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener =
             (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
                     -> updatePanelTranslation();
+    Point mPoint;
 
     public HeadsUpAppearanceController(
             NotificationIconAreaController notificationIconAreaController,
@@ -121,8 +124,54 @@
         updateHeader(headsUp.getEntry());
     }
 
+    /** To count the distance from the window right boundary to scroller right boundary. The
+     * distance formula is the following:
+     *     Y = screenSize - (SystemWindow's width + Scroller.getRight())
+     * There are four modes MUST to be considered in Cut Out of RTL.
+     * No Cut Out:
+     *   Scroller + NB
+     *   NB + Scroller
+     *     => SystemWindow = NavigationBar's width
+     *     => Y = screenSize - (SystemWindow's width + Scroller.getRight())
+     * Corner Cut Out or Tall Cut Out:
+     *   cut out + Scroller + NB
+     *   NB + Scroller + cut out
+     *     => SystemWindow = NavigationBar's width
+     *     => Y = screenSize - (SystemWindow's width + Scroller.getRight())
+     * Double Cut Out:
+     *   cut out left + Scroller + (NB + cut out right)
+     *     SystemWindow = NavigationBar's width + cut out right width
+     *     => Y = screenSize - (SystemWindow's width + Scroller.getRight())
+     *   (cut out left + NB) + Scroller + cut out right
+     *     SystemWindow = NavigationBar's width + cut out left width
+     *     => Y = screenSize - (SystemWindow's width + Scroller.getRight())
+     * @return the translation X value for RTL. In theory, it should be negative. i.e. -Y
+     */
+    private int getRtlTranslation() {
+        // TODO: Corner Cut Out still need to handle.
+        if (mPoint == null) {
+            mPoint = new Point();
+        }
+
+        int realDisplaySize = 0;
+        if (mStackScroller.getDisplay() != null) {
+            mStackScroller.getDisplay().getRealSize(mPoint);
+            realDisplaySize = mPoint.x;
+        }
+
+        WindowInsets windowInset = mStackScroller.getRootWindowInsets();
+        return windowInset.getSystemWindowInsetLeft() + mStackScroller.getRight()
+                + windowInset.getSystemWindowInsetRight() - realDisplaySize;
+    }
+
     public void updatePanelTranslation() {
-        float newTranslation = mStackScroller.getLeft() + mStackScroller.getTranslationX();
+        float newTranslation;
+        if (mStackScroller.isLayoutRtl()) {
+            newTranslation = getRtlTranslation();
+        } else {
+            newTranslation = mStackScroller.getLeft();
+        }
+        newTranslation += mStackScroller.getTranslationX();
         mHeadsUpStatusBarView.setPanelTranslation(newTranslation);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index b043100..44d666e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -141,7 +141,7 @@
     }
 
     protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
-            boolean showAmbient, boolean hideDismissed) {
+            boolean showAmbient, boolean hideDismissed, boolean hideRepliedMessages) {
         if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
             return false;
         }
@@ -155,6 +155,10 @@
             return false;
         }
 
+        if (hideRepliedMessages && entry.isLastMessageFromReply()) {
+            return false;
+        }
+
         // showAmbient == show in shade but not shelf
         if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(entry)) {
             return false;
@@ -170,14 +174,15 @@
 
         updateStatusBarIcons();
         updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
-                NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */);
+                NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */,
+                false /* hideRepliedMessages */);
 
         applyNotificationIconsTint();
     }
 
-    private void updateStatusBarIcons() {
+    public void updateStatusBarIcons() {
         updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
-                false /* showAmbient */, true /* hideDismissed */);
+                false /* showAmbient */, true /* hideDismissed */, true /* hideRepliedMessages */);
     }
 
     /**
@@ -187,9 +192,11 @@
      * @param hostLayout which layout should be updated
      * @param showAmbient should ambient notification icons be shown
      * @param hideDismissed should dismissed icons be hidden
+     * @param hideRepliedMessages should messages that have been replied to be hidden
      */
     private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
-            NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed) {
+            NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed,
+            boolean hideRepliedMessages) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                 mNotificationScrollLayout.getChildCount());
 
@@ -198,7 +205,8 @@
             View view = mNotificationScrollLayout.getChildAt(i);
             if (view instanceof ExpandableNotificationRow) {
                 NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
-                if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed)) {
+                if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed,
+                        hideRepliedMessages)) {
                     toShow.add(function.apply(ent));
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 8c257fe..5dee2a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -129,13 +129,14 @@
 
     public static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5;
     public static final int MAX_STATIC_ICONS = 4;
-    private static final int MAX_DOTS = 3;
+    private static final int MAX_DOTS = 1;
 
     private boolean mIsStaticLayout = true;
     private final HashMap<View, IconState> mIconStates = new HashMap<>();
     private int mDotPadding;
     private int mStaticDotRadius;
     private int mStaticDotDiameter;
+    private int mOverflowWidth;
     private int mActualLayoutWidth = NO_VALUE;
     private float mActualPaddingEnd = NO_VALUE;
     private float mActualPaddingStart = NO_VALUE;
@@ -230,7 +231,7 @@
             int top = (int) (centerY - height / 2.0f);
             child.layout(0, top, width, top + height);
             if (i == 0) {
-                mIconSize = child.getWidth();
+                setIconSize(child.getWidth());
             }
         }
         getLocationOnScreen(mAbsolutePosition);
@@ -239,6 +240,11 @@
         }
     }
 
+    private void setIconSize(int size) {
+        mIconSize = size;
+        mOverflowWidth = mIconSize + (MAX_DOTS - 1) * (mStaticDotDiameter + mDotPadding);
+    }
+
     private void updateState() {
         resetViewStates();
         calculateIconTranslations();
@@ -391,12 +397,12 @@
             iconState.visibleState = StatusBarIconView.STATE_ICON;
 
             boolean isOverflowing =
-                    (translationX >= (noOverflowAfter ? layoutEnd - mIconSize : overflowStart));
+                    (translationX > (noOverflowAfter ? layoutEnd - mIconSize
+                            : overflowStart - mIconSize));
             if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
                 firstOverflowIndex = noOverflowAfter && !forceOverflow ? i - 1 : i;
-                mVisualOverflowStart = layoutEnd - mIconSize
-                        - 2 * (mStaticDotDiameter + mDotPadding);
-                if (forceOverflow) {
+                mVisualOverflowStart = layoutEnd - mOverflowWidth;
+                if (forceOverflow || mIsStaticLayout) {
                     mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
                 }
             }
@@ -408,7 +414,7 @@
             for (int i = firstOverflowIndex; i < childCount; i++) {
                 View view = getChildAt(i);
                 IconState iconState = mIconStates.get(view);
-                int dotWidth = mStaticDotRadius * 2 + mDotPadding;
+                int dotWidth = mStaticDotDiameter + mDotPadding;
                 iconState.xTranslation = translationX;
                 if (mNumDots < MAX_DOTS) {
                     if (mNumDots == 0 && iconState.iconAppearAmount < 0.8f) {
@@ -536,7 +542,7 @@
     }
 
     private float getMaxOverflowStart() {
-        return getLayoutEnd() - mIconSize * (2 + OVERFLOW_EARLY_AMOUNT);
+        return getLayoutEnd() - mOverflowWidth;
     }
 
     public void setChangingViewPositions(boolean changingViewPositions) {
@@ -607,7 +613,7 @@
             return 0;
         }
 
-        int collapsedPadding = mIconSize + 2 * (mStaticDotDiameter + mDotPadding);
+        int collapsedPadding = mOverflowWidth;
 
         if (collapsedPadding + getFinalTranslationX() > getWidth()) {
             collapsedPadding = getWidth() - getFinalTranslationX();
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 93e9cdf..c70f034 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -234,6 +234,7 @@
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.policy.PreviewInflater;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -399,6 +400,9 @@
     private View mPendingRemoteInputView;
     private View mPendingWorkRemoteInputView;
 
+    private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler =
+            Dependency.get(RemoteInputQuickSettingsDisabler.class);
+
     private View mReportRejectedTouch;
 
     private int mMaxAllowedKeyguardNotifications;
@@ -1759,6 +1763,8 @@
      */
     @Override
     public void disable(int state1, int state2, boolean animate) {
+        state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
+
         animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
         final int old1 = mDisabled1;
         final int diff1 = state1 ^ old1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
new file mode 100644
index 0000000..c2933e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.res.Configuration;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Let {@link RemoteInputView} to control the visibility of QuickSetting.
+ */
+public class RemoteInputQuickSettingsDisabler
+        implements ConfigurationController.ConfigurationListener {
+
+    private Context mContext;
+    @VisibleForTesting boolean mRemoteInputActive;
+    @VisibleForTesting boolean misLandscape;
+    private int mLastOrientation;
+    @VisibleForTesting CommandQueue mCommandQueue;
+
+    public RemoteInputQuickSettingsDisabler(Context context) {
+        mContext = context;
+        mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
+        mLastOrientation = mContext.getResources().getConfiguration().orientation;
+        Dependency.get(ConfigurationController.class).addCallback(this);
+    }
+
+    public int adjustDisableFlags(int state) {
+        if (mRemoteInputActive && misLandscape) {
+            state |= StatusBarManager.DISABLE2_QUICK_SETTINGS;
+        }
+
+        return state;
+    }
+
+    public void setRemoteInputActive(boolean active){
+        if(mRemoteInputActive != active){
+            mRemoteInputActive = active;
+            recomputeDisableFlags();
+        }
+    }
+
+    @Override
+    public void onConfigChanged(Configuration newConfig) {
+        if (newConfig.orientation != mLastOrientation) {
+            misLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
+            mLastOrientation = newConfig.orientation;
+            recomputeDisableFlags();
+        }
+    }
+
+    /**
+     * Reapplies the disable flags. Then the method adjustDisableFlags in this class will be invoked
+     * in {@link QSFragment#disable(int, int, boolean)} and
+     * {@link StatusBar#disable(int, int, boolean)}
+     * to modify the disable flags according to the status of mRemoteInputActive and misLandscape.
+     */
+    private void recomputeDisableFlags() {
+        mCommandQueue.recomputeDisableFlags(true);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 310f14c..4ec9ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -53,6 +53,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.NotificationData;
@@ -81,6 +82,7 @@
     private RemoteInput[] mRemoteInputs;
     private RemoteInput mRemoteInput;
     private RemoteInputController mController;
+    private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
 
     private NotificationData.Entry mEntry;
 
@@ -96,6 +98,7 @@
 
     public RemoteInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
     }
 
     @Override
@@ -151,6 +154,7 @@
         mController.removeRemoteInput(mEntry, mToken);
         mEditText.mShowImeOnInputConnection = false;
         mController.remoteInputSent(mEntry);
+        mEntry.setHasSentReply();
 
         // Tell ShortcutManager that this package has been "activated".  ShortcutManager
         // will reset the throttling for this package.
@@ -232,6 +236,9 @@
                 }
             }
         }
+
+        mRemoteInputQuickSettingsDisabler.setRemoteInputActive(false);
+
         MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
                 mEntry.notification.getPackageName());
     }
@@ -291,6 +298,9 @@
         mEditText.setSelection(mEditText.getText().length());
         mEditText.requestFocus();
         mController.addRemoteInput(mEntry, mToken);
+
+        mRemoteInputQuickSettingsDisabler.setRemoteInputActive(true);
+
         updateSendButton();
     }
 
@@ -554,6 +564,14 @@
         }
 
         @Override
+        public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+                mRemoteInputView.mRemoteInputQuickSettingsDisabler.setRemoteInputActive(false);
+            }
+            return super.dispatchKeyEvent(event);
+        }
+
+        @Override
         public boolean onCheckIsTextEditor() {
             // Stop being editable while we're being removed. During removal, we get reattached,
             // and editable views get their spellchecking state re-evaluated which is too costly
@@ -565,11 +583,6 @@
         @Override
         public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
             final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
-            //if pinned, set imeOption to keep the behavior like in portrait.
-            if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isPinned()) {
-                outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI
-                        | EditorInfo.IME_FLAG_NO_FULLSCREEN;
-            }
 
             if (mShowImeOnInputConnection && inputConnection != null) {
                 final InputMethodManager imm = InputMethodManager.getInstance();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 585787e..924aa01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -213,6 +213,7 @@
             Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
             RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, intent, results);
             RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE);
+            entry.setHasSentReply();
             try {
                 pendingIntent.send(context, 0, intent);
             } catch (PendingIntent.CanceledException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 91a4b07..db3f0d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -30,7 +30,6 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.ArrayList;
-import java.util.Collection;
 
 /**
  * A global state to track all input states for the algorithm.
@@ -72,6 +71,7 @@
     private ExpandableNotificationRow mExpandingNotification;
     private int mDarkTopPadding;
     private float mDarkAmount;
+    private boolean mAppearing;
 
     public AmbientState(Context context) {
         reload(context);
@@ -436,4 +436,12 @@
     public int getDarkTopPadding() {
         return mDarkTopPadding;
     }
+
+    public void setAppearing(boolean appearing) {
+        mAppearing = appearing;
+    }
+
+    public boolean isAppearing() {
+        return mAppearing;
+    }
 }
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 bd56d79..a2b33fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -885,7 +885,9 @@
         float appearEndPosition = getAppearEndPosition();
         float appearStartPosition = getAppearStartPosition();
         float appearFraction = 1.0f;
-        if (height >= appearEndPosition) {
+        boolean appearing = height < appearEndPosition;
+        mAmbientState.setAppearing(appearing);
+        if (!appearing) {
             translationY = 0;
             if (mShouldShowShelfOnly) {
                 stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 85f33d7..0d50f5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -478,22 +478,6 @@
                 childState.hidden = false;
             }
         }
-        // Let's hide all the views if we are not expanded. the views might otherwise be visible
-        // in the headsup area if a view was swiped away
-        if (!mIsExpanded) {
-            for (int i = 0; i < childCount; i++) {
-                boolean visible = false;
-                View child = algorithmState.visibleChildren.get(i);
-                if (child instanceof ExpandableNotificationRow) {
-                    ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                    visible = row.isHeadsUp() || row.isHeadsUpAnimatingAway();
-                }
-                if (!visible) {
-                    ExpandableViewState childState = resultState.getViewStateForView(child);
-                    childState.hidden = true;
-                }
-            }
-        }
     }
 
     private void clampHunToTop(AmbientState ambientState, ExpandableNotificationRow row,
@@ -536,6 +520,10 @@
 
         int shelfStart = ambientState.getInnerHeight()
                 - ambientState.getShelf().getIntrinsicHeight();
+        if (ambientState.isAppearing() && !child.isAboveShelf()) {
+            // Don't show none heads-up notifications while in appearing phase.
+            childViewState.yTranslation = Math.max(childViewState.yTranslation, shelfStart);
+        }
         childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart);
         if (childViewState.yTranslation >= shelfStart) {
             childViewState.hidden = !child.isExpandAnimationRunning() && !child.hasExpandingChild();
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
index ad20900..11c72c4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
@@ -23,6 +23,7 @@
     public static final int ROTATION_NONE = 0;
     public static final int ROTATION_LANDSCAPE = 1;
     public static final int ROTATION_SEASCAPE = 2;
+    public static final int ROTATION_UPSIDE_DOWN = 3;
 
     public static int getRotation(Context context) {
         Configuration config = context.getResources().getConfiguration();
@@ -36,4 +37,19 @@
         }
         return ROTATION_NONE;
     }
+
+    public static int getExactRotation(Context context) {
+        Configuration config = context.getResources().getConfiguration();
+        int rot = context.getDisplay().getRotation();
+        if (config.smallestScreenWidthDp < 600) {
+            if (rot == Surface.ROTATION_90) {
+                return ROTATION_LANDSCAPE;
+            } else if (rot == Surface.ROTATION_270) {
+                return ROTATION_SEASCAPE;
+            } else if (rot == Surface.ROTATION_180) {
+                return ROTATION_UPSIDE_DOWN;
+            }
+        }
+        return ROTATION_NONE;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index d76b7f0..03474a8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -727,6 +727,12 @@
     }
 
     protected void onStateChangedH(State state) {
+        if (mState != null && state != null
+                && mState.ringerModeInternal != state.ringerModeInternal
+                && state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
+            mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK));
+        }
+
         mState = state;
         mDynamic.clear();
         // add any new dynamic rows
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 2f05b06..f1bf31d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -36,6 +36,7 @@
 import android.content.res.Configuration;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
 import android.view.Display;
 import android.view.View;
 import android.view.WindowManager;
@@ -54,6 +55,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+@RunWithLooper
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 public class ScreenDecorationsTest extends SysuiTestCase {
@@ -98,6 +100,8 @@
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
         mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius_top, 0);
+        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius_bottom, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
new file mode 100644
index 0000000..3b47eae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RemoteInputQuickSettingsDisablerTest extends SysuiTestCase {
+
+    private CommandQueue mCommandQueue;
+    private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mCommandQueue = mock(CommandQueue.class);
+        mContext.putComponent(CommandQueue.class, mCommandQueue);
+
+        mRemoteInputQuickSettingsDisabler = new RemoteInputQuickSettingsDisabler(mContext);
+    }
+
+    @Test
+    public void shouldEnableQuickSetting_afterDeactiviate() {
+        mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
+        mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
+        assertFalse(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+    }
+
+    @Test
+    public void shouldDisableQuickSetting_afteActiviate() {
+        mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
+        mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
+        assertTrue(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+    }
+
+    @Test
+    public void testChangeToLandscape() {
+        Configuration c = new Configuration(mContext.getResources().getConfiguration());
+        c.orientation = Configuration.ORIENTATION_PORTRAIT;
+        mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
+        c.orientation = Configuration.ORIENTATION_LANDSCAPE;
+        mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
+        assertTrue(mRemoteInputQuickSettingsDisabler.misLandscape);
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+    }
+
+    @Test
+    public void testChangeToPortrait() {
+        Configuration c = new Configuration(mContext.getResources().getConfiguration());
+        c.orientation = Configuration.ORIENTATION_LANDSCAPE;
+        mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
+        c.orientation = Configuration.ORIENTATION_PORTRAIT;
+        mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
+        assertFalse(mRemoteInputQuickSettingsDisabler.misLandscape);
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 938dfca..a6fa4f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -54,6 +54,7 @@
 
     @Mock private RemoteInputController mController;
     @Mock private ShortcutManager mShortcutManager;
+    @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private BlockingQueueIntentReceiver mReceiver;
     private RemoteInputView mView;
 
@@ -61,6 +62,9 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class,
+                mRemoteInputQuickSettingsDisabler);
+
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null,
                 Handler.createAsync(Dependency.get(Dependency.BG_LOOPER)));
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index fa2a0d9..a5311b2 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -53,7 +53,6 @@
 import android.view.autofill.AutofillManager;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
-import android.widget.ScrollView;
 import android.widget.TextView;
 
 import com.android.internal.R;
@@ -208,7 +207,7 @@
             mSubTitle = info.getDescription();
             if (mSubTitle != null) {
                 writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_SUBTITLE, type);
-                final ScrollView subtitleContainer =
+                final ViewGroup subtitleContainer =
                         view.findViewById(R.id.autofill_save_custom_subtitle);
                 final TextView subtitleView = new TextView(context);
                 subtitleView.setText(mSubTitle);
@@ -361,7 +360,7 @@
             }
 
             // Finally, add the custom description to the save UI.
-            final ScrollView subtitleContainer =
+            final ViewGroup subtitleContainer =
                     saveUiView.findViewById(R.id.autofill_save_custom_subtitle);
             subtitleContainer.addView(customSubtitleView);
             subtitleContainer.setVisibility(View.VISIBLE);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f729cb8..e22a86e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4674,7 +4674,7 @@
     }
 
     private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
-        LinkProperties newLp = networkAgent.linkProperties;
+        LinkProperties newLp = new LinkProperties(networkAgent.linkProperties);
         int netId = networkAgent.network.netId;
 
         // The NetworkAgentInfo does not know whether clatd is running on its network or not. Before
@@ -4708,6 +4708,9 @@
         }
         // TODO - move this check to cover the whole function
         if (!Objects.equals(newLp, oldLp)) {
+            synchronized (networkAgent) {
+                networkAgent.linkProperties = newLp;
+            }
             notifyIfacesChangedForNetworkStats();
             notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
         }
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 2869114..5a25f48 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import android.annotation.Nullable;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -47,10 +49,18 @@
 import dalvik.system.VMRuntime;
 
 import java.io.FileDescriptor;
+import java.io.Closeable;
+import java.io.InputStream;
+import java.io.DataInputStream;
 import java.io.IOException;
+import java.io.EOFException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
+import java.util.zip.ZipFile;
+import java.util.zip.ZipException;
+import java.util.zip.ZipEntry;
+
 /**
  * <p>PinnerService pins important files for key processes in memory.</p>
  * <p>Files to pin are specified in the config_defaultPinnerServiceFiles
@@ -60,16 +70,18 @@
 public final class PinnerService extends SystemService {
     private static final boolean DEBUG = false;
     private static final String TAG = "PinnerService";
+    private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); //80MB max
+    private static final String PIN_META_FILENAME = "pinlist.meta";
+    private static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE);
 
     private final Context mContext;
-    private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<PinnedFile>();
-    private final ArrayList<PinnedFile> mPinnedCameraFiles = new ArrayList<PinnedFile>();
     private final boolean mShouldPinCamera;
 
+    /* These lists protected by PinnerService monitor lock */
+    private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<PinnedFile>();
+    private final ArrayList<PinnedFile> mPinnedCameraFiles = new ArrayList<PinnedFile>();
+
     private BinderService mBinderService;
-
-    private final long MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); //80MB max
-
     private PinnerHandler mPinnerHandler = null;
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -146,18 +158,18 @@
          // Files to pin come from the overlay and can be specified per-device config
         String[] filesToPin = mContext.getResources().getStringArray(
                 com.android.internal.R.array.config_defaultPinnerServiceFiles);
-        synchronized(this) {
-            // Continue trying to pin remaining files even if there is a failure
-            for (int i = 0; i < filesToPin.length; i++){
-                PinnedFile pf = pinFile(filesToPin[i], 0, 0, 0);
-                if (pf != null) {
-                    mPinnedFiles.add(pf);
-                    if (DEBUG) {
-                        Slog.i(TAG, "Pinned file = " + pf.mFilename);
-                    }
-                } else {
-                    Slog.e(TAG, "Failed to pin file = " + filesToPin[i]);
-                }
+        // Continue trying to pin each file even if we fail to pin some of them
+        for (String fileToPin : filesToPin) {
+            PinnedFile pf = pinFile(fileToPin,
+                                    Integer.MAX_VALUE,
+                                    /*attemptPinIntrospection=*/false);
+            if (pf == null) {
+                Slog.e(TAG, "Failed to pin file = " + fileToPin);
+                continue;
+            }
+
+            synchronized (this) {
+                mPinnedFiles.add(pf);
             }
         }
     }
@@ -166,44 +178,23 @@
      * Handler for camera pinning message
      */
     private void handlePinCamera(int userHandle) {
-        if (mShouldPinCamera) {
-            synchronized(this) {
-                boolean success = pinCamera(userHandle);
-                if (!success) {
-                    //this is not necessarily an error
-                    if (DEBUG) {
-                        Slog.v(TAG, "Failed to pin camera.");
-                    }
-                }
+        if (!mShouldPinCamera) return;
+        if (!pinCamera(userHandle)) {
+            if (DEBUG) {
+                Slog.v(TAG, "Failed to pin camera.");
             }
         }
     }
 
-    /**
-     *  determine if the camera app is already pinned by comparing the
-     *  intent resolution to the pinned files list
-     */
-    private boolean alreadyPinned(int userHandle) {
-        ApplicationInfo cameraInfo = getCameraInfo(userHandle);
-        if (cameraInfo == null ) {
-            return false;
-        }
-        for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
-            if (mPinnedCameraFiles.get(i).mFilename.equals(cameraInfo.sourceDir)) {
-                if (DEBUG) {
-                  Slog.v(TAG, "Camera is already pinned");
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-
     private void unpinCameraApp() {
-        for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
-            unpinFile(mPinnedCameraFiles.get(i));
+        ArrayList<PinnedFile> pinnedCameraFiles;
+        synchronized (this) {
+            pinnedCameraFiles = new ArrayList<>(mPinnedCameraFiles);
+            mPinnedCameraFiles.clear();
         }
-        mPinnedCameraFiles.clear();
+        for (PinnedFile pinnedFile : pinnedCameraFiles) {
+            pinnedFile.close();
+        }
     }
 
     private boolean isResolverActivity(ActivityInfo info) {
@@ -255,15 +246,19 @@
 
         //pin APK
         String camAPK = cameraInfo.sourceDir;
-        PinnedFile pf = pinFile(camAPK, 0, 0, MAX_CAMERA_PIN_SIZE);
+        PinnedFile pf = pinFile(camAPK,
+                                MAX_CAMERA_PIN_SIZE,
+                                /*attemptPinIntrospection=*/true);
         if (pf == null) {
             Slog.e(TAG, "Failed to pin " + camAPK);
             return false;
         }
         if (DEBUG) {
-            Slog.i(TAG, "Pinned " + pf.mFilename);
+            Slog.i(TAG, "Pinned " + pf.fileName);
         }
-        mPinnedCameraFiles.add(pf);
+        synchronized (this) {
+            mPinnedCameraFiles.add(pf);
+        }
 
         // determine the ABI from either ApplicationInfo or Build
         String arch = "arm";
@@ -289,11 +284,13 @@
 
         //not pinning the oat/odex is not a fatal error
         for (String file : files) {
-            pf = pinFile(file, 0, 0, MAX_CAMERA_PIN_SIZE);
+            pf = pinFile(file, MAX_CAMERA_PIN_SIZE, /*attemptPinIntrospection=*/false);
             if (pf != null) {
-                mPinnedCameraFiles.add(pf);
+                synchronized (this) {
+                    mPinnedCameraFiles.add(pf);
+                }
                 if (DEBUG) {
-                    Slog.i(TAG, "Pinned " + pf.mFilename);
+                    Slog.i(TAG, "Pinned " + pf.fileName);
                 }
             }
         }
@@ -302,70 +299,294 @@
     }
 
 
-    /** mlock length bytes of fileToPin in memory, starting at offset
-     *  length == 0 means pin from offset to end of file
-     *  maxSize == 0 means infinite
+    /** mlock length bytes of fileToPin in memory
+     *
+     * If attemptPinIntrospection is true, then treat the file to pin as a zip file and
+     * look for a "pinlist.meta" file in the archive root directory. The structure of this
+     * file is a PINLIST_META as described below:
+     *
+     * <pre>
+     *   PINLIST_META: PIN_RANGE*
+     *   PIN_RANGE: PIN_START PIN_LENGTH
+     *   PIN_START: big endian i32: offset in bytes of pin region from file start
+     *   PIN_LENGTH: big endian i32: length of pin region in bytes
+     * </pre>
+     *
+     * (We use big endian because that's what DataInputStream is hardcoded to use.)
+     *
+     * If attemptPinIntrospection is false, then we use a single implicit PIN_RANGE of (0,
+     * maxBytesToPin); that is, we attempt to pin the first maxBytesToPin bytes of the file.
+     *
+     * After we open a file, we march through the list of pin ranges and attempt to pin
+     * each one, stopping after we've pinned maxBytesToPin bytes. (We may truncate the last
+     * pinned range to fit.)  In this way, by choosing to emit certain PIN_RANGE pairs
+     * before others, file generators can express pins in priority order, making most
+     * effective use of the pinned-page quota.
+     *
+     * N.B. Each PIN_RANGE is clamped to the actual bounds of the file; all inputs have a
+     * meaningful interpretation. Also, a range locking a single byte of a page locks the
+     * whole page. Any truncated PIN_RANGE at EOF is ignored. Overlapping pinned entries
+     * are legal, but each pin of a byte counts toward the pin quota regardless of whether
+     * that byte has already been pinned, so the generator of PINLIST_META ought to ensure
+     * that ranges are non-overlapping.
+     *
+     * @param fileToPin Path to file to pin
+     * @param maxBytesToPin Maximum number of bytes to pin
+     * @param attemptPinIntrospection If true, try to open file as a
+     *   zip in order to extract the
+     * @return Pinned memory resource owner thing or null on error
      */
-    private static PinnedFile pinFile(String fileToPin, long offset, long length, long maxSize) {
-        FileDescriptor fd = new FileDescriptor();
+    private static PinnedFile pinFile(String fileToPin,
+                                      int maxBytesToPin,
+                                      boolean attemptPinIntrospection) {
+        ZipFile fileAsZip = null;
+        InputStream pinRangeStream = null;
         try {
-            fd = Os.open(fileToPin,
-                    OsConstants.O_RDONLY | OsConstants.O_CLOEXEC | OsConstants.O_NOFOLLOW,
-                    OsConstants.O_RDONLY);
-
-            StructStat sb = Os.fstat(fd);
-
-            if (offset + length > sb.st_size) {
-                Os.close(fd);
-                Slog.e(TAG, "Failed to pin file " + fileToPin +
-                        ", request extends beyond end of file.  offset + length =  "
-                        + (offset + length) + ", file length = " + sb.st_size);
-                return null;
+            if (attemptPinIntrospection) {
+                fileAsZip = maybeOpenZip(fileToPin);
             }
 
-            if (length == 0) {
-                length = sb.st_size - offset;
+            if (fileAsZip != null) {
+                pinRangeStream = maybeOpenPinMetaInZip(fileAsZip, fileToPin);
             }
 
-            if (maxSize > 0 && length > maxSize) {
-                Slog.e(TAG, "Could not pin file " + fileToPin +
-                        ", size = " + length + ", maxSize = " + maxSize);
-                Os.close(fd);
-                return null;
-            }
+            Slog.d(TAG, "pinRangeStream: " + pinRangeStream);
 
-            long address = Os.mmap(0, length, OsConstants.PROT_READ,
-                    OsConstants.MAP_PRIVATE, fd, offset);
-            Os.close(fd);
-
-            Os.mlock(address, length);
-
-            return new PinnedFile(address, length, fileToPin);
-        } catch (ErrnoException e) {
-            Slog.e(TAG, "Could not pin file " + fileToPin + " with error " + e.getMessage());
-            if(fd.valid()) {
-                try {
-                    Os.close(fd);
-                }
-                catch (ErrnoException eClose) {
-                    Slog.e(TAG, "Failed to close fd, error = " + eClose.getMessage());
-                }
-            }
-            return null;
+            PinRangeSource pinRangeSource = (pinRangeStream != null)
+                ? new PinRangeSourceStream(pinRangeStream)
+                : new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */);
+            return pinFileRanges(fileToPin, maxBytesToPin, pinRangeSource);
+        } finally {
+            safeClose(pinRangeStream);
+            safeClose(fileAsZip);  // Also closes any streams we've opened
         }
     }
 
-    private static boolean unpinFile(PinnedFile pf) {
+    /**
+     * Attempt to open a file as a zip file. On any sort of corruption, log, swallow the
+     * error, and return null.
+     */
+    private static ZipFile maybeOpenZip(String fileName) {
+        ZipFile zip = null;
         try {
-            Os.munlock(pf.mAddress, pf.mLength);
-        } catch (ErrnoException e) {
-            Slog.e(TAG, "Failed to unpin file " + pf.mFilename + " with error " + e.getMessage());
-            return false;
+            zip = new ZipFile(fileName);
+        } catch (IOException ex) {
+            Slog.w(TAG,
+                   String.format(
+                       "could not open \"%s\" as zip: pinning as blob",
+                                 fileName),
+                   ex);
         }
-        if (DEBUG) {
-            Slog.i(TAG, "Unpinned file " + pf.mFilename );
+        return zip;
+    }
+
+    /**
+     * Open a pin metadata file in the zip if one is present.
+     *
+     * @param zipFile Zip file to search
+     * @return Open input stream or null on any error
+     */
+    private static InputStream maybeOpenPinMetaInZip(ZipFile zipFile, String fileName) {
+        ZipEntry pinMetaEntry = zipFile.getEntry(PIN_META_FILENAME);
+        InputStream pinMetaStream = null;
+        if (pinMetaEntry != null) {
+            try {
+                pinMetaStream = zipFile.getInputStream(pinMetaEntry);
+            } catch (IOException ex) {
+                Slog.w(TAG,
+                       String.format("error reading pin metadata \"%s\": pinning as blob",
+                                     fileName),
+                       ex);
+            }
         }
-        return true;
+        return pinMetaStream;
+    }
+
+    private static abstract class PinRangeSource {
+        /** Retrive a range to pin.
+         *
+         * @param outPinRange Receives the pin region
+         * @return True if we filled in outPinRange or false if we're out of pin entries
+         */
+        abstract boolean read(PinRange outPinRange);
+    }
+
+    private static final class PinRangeSourceStatic extends PinRangeSource {
+        private final int mPinStart;
+        private final int mPinLength;
+        private boolean mDone = false;
+
+        PinRangeSourceStatic(int pinStart, int pinLength) {
+            mPinStart = pinStart;
+            mPinLength = pinLength;
+        }
+
+        @Override
+        boolean read(PinRange outPinRange) {
+            outPinRange.start = mPinStart;
+            outPinRange.length = mPinLength;
+            boolean done = mDone;
+            mDone = true;
+            return !done;
+        }
+    }
+
+    private static final class PinRangeSourceStream extends PinRangeSource {
+        private final DataInputStream mStream;
+        private boolean mDone = false;
+
+        PinRangeSourceStream(InputStream stream) {
+            mStream = new DataInputStream(stream);
+        }
+
+        @Override
+        boolean read(PinRange outPinRange) {
+            if (!mDone) {
+                try {
+                    outPinRange.start = mStream.readInt();
+                    outPinRange.length = mStream.readInt();
+                } catch (IOException ex) {
+                    mDone = true;
+                }
+            }
+            return !mDone;
+        }
+    }
+
+    /**
+     * Helper for pinFile.
+     *
+     * @param fileToPin Name of file to pin
+     * @param maxBytesToPin Maximum number of bytes to pin
+     * @param pinRangeSource Read PIN_RANGE entries from this stream to tell us what bytes
+     *   to pin.
+     * @return PinnedFile or null on error
+     */
+    private static PinnedFile pinFileRanges(
+        String fileToPin,
+        int maxBytesToPin,
+        PinRangeSource pinRangeSource)
+    {
+        FileDescriptor fd = new FileDescriptor();
+        long address = -1;
+        int mapSize = 0;
+
+        try {
+            int openFlags = (OsConstants.O_RDONLY |
+                             OsConstants.O_CLOEXEC |
+                             OsConstants.O_NOFOLLOW);
+            fd = Os.open(fileToPin, openFlags, 0);
+            mapSize = (int) Math.min(Os.fstat(fd).st_size, Integer.MAX_VALUE);
+            address = Os.mmap(0, mapSize,
+                              OsConstants.PROT_READ,
+                              OsConstants.MAP_SHARED,
+                              fd, /*offset=*/0);
+
+            PinRange pinRange = new PinRange();
+            int bytesPinned = 0;
+
+            // We pin at page granularity, so make sure the limit is page-aligned
+            if (maxBytesToPin % PAGE_SIZE != 0) {
+                maxBytesToPin -= maxBytesToPin % PAGE_SIZE;
+            }
+
+            while (bytesPinned < maxBytesToPin && pinRangeSource.read(pinRange)) {
+                int pinStart = pinRange.start;
+                int pinLength = pinRange.length;
+                pinStart = clamp(0, pinStart, mapSize);
+                pinLength = clamp(0, pinLength, mapSize - pinStart);
+                pinLength = Math.min(maxBytesToPin - bytesPinned, pinLength);
+
+                // mlock doesn't require the region to be page-aligned, but we snap the
+                // lock region to page boundaries anyway so that we don't under-count
+                // locking a single byte of a page as a charge of one byte even though the
+                // OS will retain the whole page. Thanks to this adjustment, we slightly
+                // over-count the pin charge of back-to-back pins touching the same page,
+                // but better that than undercounting. Besides: nothing stops pin metafile
+                // creators from making the actual regions page-aligned.
+                pinLength += pinStart % PAGE_SIZE;
+                pinStart -= pinStart % PAGE_SIZE;
+                if (pinLength % PAGE_SIZE != 0) {
+                    pinLength += PAGE_SIZE - pinLength % PAGE_SIZE;
+                }
+                pinLength = clamp(0, pinLength, maxBytesToPin - bytesPinned);
+
+                if (pinLength > 0) {
+                    if (DEBUG) {
+                        Slog.d(TAG,
+                               String.format(
+                                   "pinning at %s %s bytes of %s",
+                                   pinStart, pinLength, fileToPin));
+                    }
+                    Os.mlock(address + pinStart, pinLength);
+                }
+                bytesPinned += pinLength;
+            }
+
+            PinnedFile pinnedFile = new PinnedFile(address, mapSize, fileToPin, bytesPinned);
+            address = -1;  // Ownership transferred
+            return pinnedFile;
+        } catch (ErrnoException ex) {
+            Slog.e(TAG, "Could not pin file " + fileToPin, ex);
+            return null;
+        } finally {
+            safeClose(fd);
+            if (address >= 0) {
+                safeMunmap(address, mapSize);
+            }
+        }
+    }
+
+    private static int clamp(int min, int value, int max) {
+        return Math.max(min, Math.min(value, max));
+    }
+
+    private static void safeMunmap(long address, long mapSize) {
+        try {
+            Os.munmap(address, mapSize);
+        } catch (ErrnoException ex) {
+            Slog.w(TAG, "ignoring error in unmap", ex);
+        }
+    }
+
+    /**
+     * Close FD, swallowing irrelevant errors.
+     */
+    private static void safeClose(@Nullable FileDescriptor fd) {
+        if (fd != null && fd.valid()) {
+            try {
+                Os.close(fd);
+            } catch (ErrnoException ex) {
+                // Swallow the exception: non-EBADF errors in close(2)
+                // indicate deferred paging write errors, which we
+                // don't care about here. The underlying file
+                // descriptor is always closed.
+                if (ex.errno == OsConstants.EBADF) {
+                    throw new AssertionError(ex);
+                }
+            }
+        }
+    }
+
+    /**
+     * Close closeable thing, swallowing errors.
+     */
+    private static void safeClose(@Nullable Closeable thing) {
+        if (thing != null) {
+            try {
+                thing.close();
+            } catch (IOException ex) {
+                Slog.w(TAG, "ignoring error closing resource: " + thing, ex);
+            }
+        }
+    }
+
+    private synchronized ArrayList<PinnedFile> snapshotPinnedFiles() {
+        int nrPinnedFiles = mPinnedFiles.size() + mPinnedCameraFiles.size();
+        ArrayList<PinnedFile> pinnedFiles = new ArrayList<>(nrPinnedFiles);
+        pinnedFiles.addAll(mPinnedFiles);
+        pinnedFiles.addAll(mPinnedCameraFiles);
+        return pinnedFiles;
     }
 
     private final class BinderService extends Binder {
@@ -373,31 +594,44 @@
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
             long totalSize = 0;
-            pw.println("Pinned Files:");
-            synchronized(this) {
-                for (int i = 0; i < mPinnedFiles.size(); i++) {
-                    pw.println(mPinnedFiles.get(i).mFilename);
-                    totalSize += mPinnedFiles.get(i).mLength;
-                }
-                for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
-                    pw.println(mPinnedCameraFiles.get(i).mFilename);
-                    totalSize += mPinnedCameraFiles.get(i).mLength;
-                }
+            for (PinnedFile pinnedFile : snapshotPinnedFiles()) {
+                pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
+                totalSize += pinnedFile.bytesPinned;
             }
-            pw.println("Total size: " + totalSize);
+            pw.format("Total size: %s\n", totalSize);
         }
     }
 
-    private static class PinnedFile {
-        long mAddress;
-        long mLength;
-        String mFilename;
+    private static final class PinnedFile implements AutoCloseable {
+        private long mAddress;
+        final int mapSize;
+        final String fileName;
+        final int bytesPinned;
 
-        PinnedFile(long address, long length, String filename) {
+        PinnedFile(long address, int mapSize, String fileName, int bytesPinned) {
              mAddress = address;
-             mLength = length;
-             mFilename = filename;
+             this.mapSize = mapSize;
+             this.fileName = fileName;
+             this.bytesPinned = bytesPinned;
         }
+
+        @Override
+        public void close() {
+            if (mAddress >= 0) {
+                safeMunmap(mAddress, mapSize);
+                mAddress = -1;
+            }
+        }
+
+        @Override
+        public void finalize() {
+            close();
+        }
+    }
+
+    final static class PinRange {
+        int start;
+        int length;
     }
 
     final class PinnerHandler extends Handler {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 113f452..98bf740 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15952,7 +15952,10 @@
     }
 
     @Override
-    public void getMyMemoryState(ActivityManager.RunningAppProcessInfo outInfo) {
+    public void getMyMemoryState(ActivityManager.RunningAppProcessInfo outState) {
+        if (outState == null) {
+            throw new IllegalArgumentException("outState is null");
+        }
         enforceNotIsolatedCaller("getMyMemoryState");
 
         final int callingUid = Binder.getCallingUid();
@@ -15963,7 +15966,9 @@
             synchronized (mPidsSelfLocked) {
                 proc = mPidsSelfLocked.get(Binder.getCallingPid());
             }
-            fillInProcMemInfo(proc, outInfo, clientTargetSdk);
+            if (proc != null) {
+                fillInProcMemInfo(proc, outState, clientTargetSdk);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 4521d3a..9b9a380 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -19,7 +19,11 @@
 import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
 import static android.net.CaptivePortal.APP_RETURN_UNWANTED;
 import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
+import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC;
+import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL;
+import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK;
 
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -35,6 +39,7 @@
 import android.net.TrafficStats;
 import android.net.Uri;
 import android.net.captiveportal.CaptivePortalProbeResult;
+import android.net.captiveportal.CaptivePortalProbeSpec;
 import android.net.dns.ResolvUtil;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
@@ -63,6 +68,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
@@ -259,6 +265,8 @@
     private final URL mCaptivePortalHttpsUrl;
     private final URL mCaptivePortalHttpUrl;
     private final URL[] mCaptivePortalFallbackUrls;
+    @Nullable
+    private final CaptivePortalProbeSpec[] mCaptivePortalFallbackSpecs;
 
     @VisibleForTesting
     protected boolean mIsCaptivePortalCheckEnabled;
@@ -334,6 +342,7 @@
         mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl());
         mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(settings, context));
         mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
+        mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
 
         start();
     }
@@ -542,8 +551,12 @@
                                     sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
                                 }
                             }));
-                    intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL,
-                            mLastPortalProbeResult.detectUrl);
+                    final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
+                    intent.putExtra(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
+                    if (probeRes.probeSpec != null) {
+                        final String encodedSpec = probeRes.probeSpec.getEncodedSpec();
+                        intent.putExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
+                    }
                     intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
                             mCaptivePortalUserAgent);
                     intent.setFlags(
@@ -782,8 +795,9 @@
         private void resolveStrictModeHostname() {
             try {
                 // Do a blocking DNS resolution using the network-assigned nameservers.
+                // Do not set AI_ADDRCONFIG in ai_flags so we get all address families in advance.
                 final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(
-                        mNetwork, mPrivateDnsProviderHostname);
+                        mNetwork, mPrivateDnsProviderHostname, 0 /* aiFlags */);
                 mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips);
             } catch (UnknownHostException uhe) {
                 mPrivateDnsConfig = null;
@@ -882,23 +896,47 @@
     }
 
     private URL[] makeCaptivePortalFallbackUrls() {
-        String separator = ",";
-        String firstUrl = mSettings.getSetting(mContext,
-                Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL);
-        String joinedUrls = firstUrl + separator + mSettings.getSetting(mContext,
-                Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, DEFAULT_OTHER_FALLBACK_URLS);
-        List<URL> urls = new ArrayList<>();
-        for (String s : joinedUrls.split(separator)) {
-            URL u = makeURL(s);
-            if (u == null) {
-                continue;
+        try {
+            String separator = ",";
+            String firstUrl = mSettings.getSetting(mContext,
+                    Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL);
+            String joinedUrls = firstUrl + separator + mSettings.getSetting(mContext,
+                    Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS,
+                    DEFAULT_OTHER_FALLBACK_URLS);
+            List<URL> urls = new ArrayList<>();
+            for (String s : joinedUrls.split(separator)) {
+                URL u = makeURL(s);
+                if (u == null) {
+                    continue;
+                }
+                urls.add(u);
             }
-            urls.add(u);
+            if (urls.isEmpty()) {
+                Log.e(TAG, String.format("could not create any url from %s", joinedUrls));
+            }
+            return urls.toArray(new URL[urls.size()]);
+        } catch (Exception e) {
+            // Don't let a misconfiguration bootloop the system.
+            Log.e(TAG, "Error parsing configured fallback URLs", e);
+            return new URL[0];
         }
-        if (urls.isEmpty()) {
-            Log.e(TAG, String.format("could not create any url from %s", joinedUrls));
+    }
+
+    private CaptivePortalProbeSpec[] makeCaptivePortalFallbackProbeSpecs() {
+        try {
+            final String settingsValue = mSettings.getSetting(
+                    mContext, Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null);
+            // Probe specs only used if configured in settings
+            if (TextUtils.isEmpty(settingsValue)) {
+                return null;
+            }
+
+            return CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs(settingsValue);
+        } catch (Exception e) {
+            // Don't let a misconfiguration bootloop the system.
+            Log.e(TAG, "Error parsing configured fallback probe specs", e);
+            return null;
         }
-        return urls.toArray(new URL[urls.size()]);
     }
 
     private String getCaptivePortalUserAgent() {
@@ -915,6 +953,15 @@
         return mCaptivePortalFallbackUrls[idx];
     }
 
+    private CaptivePortalProbeSpec nextFallbackSpec() {
+        if (ArrayUtils.isEmpty(mCaptivePortalFallbackSpecs)) {
+            return null;
+        }
+        // Randomly change spec without memory. Also randomize the first attempt.
+        final int idx = Math.abs(new Random().nextInt()) % mCaptivePortalFallbackSpecs.length;
+        return mCaptivePortalFallbackSpecs[idx];
+    }
+
     @VisibleForTesting
     protected CaptivePortalProbeResult isCaptivePortal() {
         if (!mIsCaptivePortalCheckEnabled) {
@@ -985,7 +1032,7 @@
         // unnecessary resolution.
         final String host = (proxy != null) ? proxy.getHost() : url.getHost();
         sendDnsProbe(host);
-        return sendHttpProbe(url, probeType);
+        return sendHttpProbe(url, probeType, null);
     }
 
     /** Do a DNS resolution of the given server. */
@@ -1021,7 +1068,8 @@
      * @return a CaptivePortalProbeResult inferred from the HTTP response.
      */
     @VisibleForTesting
-    protected CaptivePortalProbeResult sendHttpProbe(URL url, int probeType) {
+    protected CaptivePortalProbeResult sendHttpProbe(URL url, int probeType,
+            @Nullable CaptivePortalProbeSpec probeSpec) {
         HttpURLConnection urlConnection = null;
         int httpResponseCode = CaptivePortalProbeResult.FAILED_CODE;
         String redirectUrl = null;
@@ -1093,7 +1141,12 @@
             TrafficStats.setThreadStatsTag(oldTag);
         }
         logValidationProbe(probeTimer.stop(), probeType, httpResponseCode);
-        return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString());
+
+        if (probeSpec == null) {
+            return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString());
+        } else {
+            return probeSpec.getResult(httpResponseCode, redirectUrl);
+        }
     }
 
     private CaptivePortalProbeResult sendParallelHttpProbes(
@@ -1156,11 +1209,12 @@
         if (httpsResult.isPortal() || httpsResult.isSuccessful()) {
             return httpsResult;
         }
-        // If a fallback url exists, use a fallback probe to try again portal detection.
-        URL fallbackUrl = nextFallbackUrl();
+        // If a fallback method exists, use it to retry portal detection.
+        // If we have new-style probe specs, use those. Otherwise, use the fallback URLs.
+        final CaptivePortalProbeSpec probeSpec = nextFallbackSpec();
+        final URL fallbackUrl = (probeSpec != null) ? probeSpec.getUrl() : nextFallbackUrl();
         if (fallbackUrl != null) {
-            CaptivePortalProbeResult result =
-                    sendHttpProbe(fallbackUrl, ValidationProbeEvent.PROBE_FALLBACK);
+            CaptivePortalProbeResult result = sendHttpProbe(fallbackUrl, PROBE_FALLBACK, probeSpec);
             if (result.isPortal()) {
                 return result;
             }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5bd7c0b..ab482bb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2706,6 +2706,9 @@
     private void normalizePoliciesNL(NetworkPolicy[] policies) {
         mNetworkPolicy.clear();
         for (NetworkPolicy policy : policies) {
+            if (policy == null) {
+                continue;
+            }
             // When two normalized templates conflict, prefer the most
             // restrictive policy
             policy.template = NetworkTemplate.normalize(policy.template, mMergedSubscriberIds);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e2e6f6d..8f7fa1b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -821,6 +821,7 @@
                         }
                     }
                     r.setVisibility(true, nv.rank, nv.count);
+                    maybeRecordInterruptionLocked(r);
                     nv.recycle();
                 }
                 // Note that we might receive this event after notifications
@@ -1928,11 +1929,12 @@
 
     @GuardedBy("mNotificationLock")
     protected void maybeRecordInterruptionLocked(NotificationRecord r) {
-        if (r.isInterruptive()) {
+        if (r.isInterruptive() && !r.hasRecordedInterruption()) {
             mAppUsageStats.reportInterruptiveNotification(r.sbn.getPackageName(),
                     r.getChannel().getId(),
                     getRealUserId(r.sbn.getUserId()));
             logRecentLocked(r);
+            r.setRecordedInterruption(true);
         }
     }
 
@@ -2669,6 +2671,7 @@
                                 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
                                 reportSeen(r);
                                 r.setSeen();
+                                maybeRecordInterruptionLocked(r);
                             }
                         }
                     }
@@ -4440,7 +4443,7 @@
                     if (index < 0) {
                         mNotificationList.add(r);
                         mUsageStats.registerPostedByApp(r);
-                        r.setInterruptive(isVisuallyInterruptive(null, r));
+                        r.setInterruptive(true);
                     } else {
                         old = mNotificationList.get(index);
                         mNotificationList.set(index, r);
@@ -4449,7 +4452,7 @@
                         notification.flags |=
                                 old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
                         r.isUpdate = true;
-                        r.setInterruptive(isVisuallyInterruptive(old, r));
+                        r.setTextChanged(isVisuallyInterruptive(old, r));
                     }
 
                     mNotificationsByKey.put(n.getKey(), r);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 2aec3ea..0f3f44e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -158,6 +158,8 @@
     private final NotificationStats mStats;
     private int mUserSentiment;
     private boolean mIsInterruptive;
+    private boolean mTextChanged;
+    private boolean mRecordedInterruption;
     private int mNumberOfSmartRepliesAdded;
     private boolean mHasSeenSmartReplies;
     /**
@@ -839,6 +841,9 @@
     /** Mark the notification as seen by the user. */
     public void setSeen() {
         mStats.setSeen();
+        if (mTextChanged) {
+            mIsInterruptive = true;
+        }
     }
 
     public void setAuthoritativeRank(int authoritativeRank) {
@@ -935,6 +940,18 @@
         mIsInterruptive = interruptive;
     }
 
+    public void setTextChanged(boolean textChanged) {
+        mTextChanged = textChanged;
+    }
+
+    public void setRecordedInterruption(boolean recorded) {
+        mRecordedInterruption = recorded;
+    }
+
+    public boolean hasRecordedInterruption() {
+        return mRecordedInterruption;
+    }
+
     public boolean isInterruptive() {
         return mIsInterruptive;
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f4853f3..df679ae 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24384,6 +24384,9 @@
         if (info.targetSdkVersion < Build.VERSION_CODES.O) {
             return false;
         }
+        if (isInstantApp(packageName, userId)) {
+            return false;
+        }
         String appOpPermission = Manifest.permission.REQUEST_INSTALL_PACKAGES;
         String[] packagesDeclaringPermission = getAppOpPermissionPackages(appOpPermission);
         if (!ArrayUtils.contains(packagesDeclaringPermission, packageName)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index dd0a5d2..06c56a0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -873,9 +873,8 @@
             throw new SecurityException("MANAGE_USERS permission is required to start intent "
                     + "after disabling quiet mode.");
         }
-        final boolean hasModifyQuietModePermission = ActivityManager.checkComponentPermission(
-                Manifest.permission.MODIFY_QUIET_MODE,
-                callingUid, -1, true) == PackageManager.PERMISSION_GRANTED;
+        final boolean hasModifyQuietModePermission = hasPermissionGranted(
+                Manifest.permission.MODIFY_QUIET_MODE, callingUid);
         if (hasModifyQuietModePermission) {
             return;
         }
@@ -1002,6 +1001,30 @@
         }
     }
 
+    @Override
+    public void setUserAdmin(int userId) {
+        checkManageUserAndAcrossUsersFullPermission("set user admin");
+
+        synchronized (mPackagesLock) {
+            UserInfo info;
+            synchronized (mUsersLock) {
+                info = getUserInfoLU(userId);
+            }
+            if (info == null || info.isAdmin()) {
+                // Exit if no user found with that id, or the user is already an Admin.
+                return;
+            }
+
+            info.flags ^= UserInfo.FLAG_ADMIN;
+            writeUserLP(getUserDataLU(info.id));
+        }
+
+        // Remove non-admin restrictions.
+        // Keep synchronized with createUserEvenWhenDisallowed.
+        setUserRestriction(UserManager.DISALLOW_SMS, false, userId);
+        setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, false, userId);
+    }
+
     /**
      * Evicts a user's CE key by stopping and restarting the user.
      *
@@ -1122,8 +1145,8 @@
                 hasManageUsersPermission()) {
             return;
         }
-        if (ActivityManager.checkComponentPermission(Manifest.permission.INTERACT_ACROSS_USERS,
-                Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
+        if (!hasPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS,
+                Binder.getCallingUid())) {
             throw new SecurityException("You need INTERACT_ACROSS_USERS or MANAGE_USERS permission "
                     + "to: check " + name);
         }
@@ -1801,17 +1824,26 @@
      */
     private static final void checkManageUserAndAcrossUsersFullPermission(String message) {
         final int uid = Binder.getCallingUid();
-        if (uid != Process.SYSTEM_UID && uid != 0
-                && ActivityManager.checkComponentPermission(
-                Manifest.permission.MANAGE_USERS,
-                uid, -1, true) != PackageManager.PERMISSION_GRANTED
-                && ActivityManager.checkComponentPermission(
-                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException(
-                    "You need MANAGE_USERS and INTERACT_ACROSS_USERS_FULL permission to: "
-                            + message);
+
+        if (uid == Process.SYSTEM_UID || uid == 0) {
+            // System UID or root's UID are granted privilege.
+            return;
         }
+
+        if (hasPermissionGranted(Manifest.permission.MANAGE_USERS, uid)
+                && hasPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)) {
+            // Apps with both permissions are granted privilege.
+            return;
+        }
+
+        throw new SecurityException(
+                "You need MANAGE_USERS and INTERACT_ACROSS_USERS_FULL permission to: " + message);
+    }
+
+    private static boolean hasPermissionGranted(String permission, int uid) {
+        return ActivityManager.checkComponentPermission(
+                permission, uid, /* owningUid = */-1, /* exported = */ true) ==
+                PackageManager.PERMISSION_GRANTED;
     }
 
     /**
@@ -1872,9 +1904,7 @@
         final int callingUid = Binder.getCallingUid();
         return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
                 || callingUid == Process.ROOT_UID
-                || ActivityManager.checkComponentPermission(
-                        android.Manifest.permission.MANAGE_USERS,
-                        callingUid, -1, true) == PackageManager.PERMISSION_GRANTED;
+                || hasPermissionGranted(android.Manifest.permission.MANAGE_USERS, callingUid);
     }
 
     /**
@@ -1886,12 +1916,8 @@
         final int callingUid = Binder.getCallingUid();
         return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
                 || callingUid == Process.ROOT_UID
-                || ActivityManager.checkComponentPermission(
-                        android.Manifest.permission.MANAGE_USERS,
-                        callingUid, -1, true) == PackageManager.PERMISSION_GRANTED
-                || ActivityManager.checkComponentPermission(
-                        android.Manifest.permission.CREATE_USERS,
-                        callingUid, -1, true) == PackageManager.PERMISSION_GRANTED;
+                || hasPermissionGranted(android.Manifest.permission.MANAGE_USERS, callingUid)
+                || hasPermissionGranted(android.Manifest.permission.CREATE_USERS, callingUid);
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d911761..61400da 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -234,7 +234,6 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.DisplayCutout;
-import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IApplicationToken;
@@ -2292,6 +2291,11 @@
                     public void onTrustedChanged() {
                         mWindowManagerFuncs.notifyKeyguardTrustedChanged();
                     }
+
+                    @Override
+                    public void onShowingChanged() {
+                        mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged();
+                    }
                 });
         mScreenshotHelper = new ScreenshotHelper(mContext);
     }
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 33eafb9..02dd6e6 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -660,6 +660,11 @@
          * abort animations that have no timeout, in case they got stuck.
          */
         void triggerAnimationFailsafe();
+
+        /**
+         * The keyguard showing state has changed
+         */
+        void onKeyguardShowingAndNotOccludedChanged();
     }
 
     /** Window has been added to the screen. */
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index e56caf8..1cba1c7 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -94,6 +94,7 @@
     public void onShowingStateChanged(boolean showing) {
         mIsShowing = showing;
 
+        mCallback.onShowingChanged();
         try {
             mKeystoreService.onKeyguardVisibilityChanged(showing, mCurrentUserId);
         } catch (RemoteException e) {
@@ -132,6 +133,7 @@
 
     public interface StateCallback {
         void onTrustedChanged();
+        void onShowingChanged();
     }
 
     public void dump(String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/updates/ApnDbInstallReceiver.java b/services/core/java/com/android/server/updates/ApnDbInstallReceiver.java
index 3b6f9d6..8d53143 100644
--- a/services/core/java/com/android/server/updates/ApnDbInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/ApnDbInstallReceiver.java
@@ -29,7 +29,7 @@
             "update_db");
 
     public ApnDbInstallReceiver() {
-        super("/data/misc/", "apns-conf.xml", "metadata/", "version");
+        super("/data/misc/apns/", "apns-conf.xml", "metadata/", "version");
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index a7c203d..2886126 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -51,6 +51,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
@@ -75,6 +76,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
 import android.service.wallpaper.IWallpaperService;
@@ -336,6 +338,102 @@
         }
     }
 
+    /**
+     * Observes changes of theme settings. It will check whether to call
+     * notifyWallpaperColorsChanged by the current theme and updated theme.
+     * The light theme and dark theme are controlled by the hint values in Wallpaper colors,
+     * threrfore, if light theme mode is chosen, HINT_SUPPORTS_DARK_THEME in hint will be
+     * removed and then notify listeners.
+     */
+    private class ThemeSettingsObserver extends ContentObserver {
+
+        public ThemeSettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        public void startObserving(Context context) {
+            context.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.THEME_MODE),
+                    false,
+                    this);
+        }
+
+        public void stopObserving(Context context) {
+            context.getContentResolver().unregisterContentObserver(this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            onThemeSettingsChanged();
+        }
+    }
+
+    /**
+     * Check whether to call notifyWallpaperColorsChanged. Assumed that the theme mode
+     * was wallpaper theme mode and dark wallpaper was set, therefoe, the theme was dark.
+     * Then theme mode changing to dark theme mode, however, theme should not update since
+     * theme was dark already.
+     */
+    private boolean needUpdateLocked(WallpaperColors colors, int themeMode) {
+        if (colors == null) {
+            return false;
+        }
+
+        if (themeMode == mThemeMode) {
+            return false;
+        }
+
+        boolean result = true;
+        boolean supportDarkTheme =
+                (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
+        switch (themeMode) {
+            case Settings.Secure.THEME_MODE_WALLPAPER:
+                if (mThemeMode == Settings.Secure.THEME_MODE_LIGHT) {
+                    result = supportDarkTheme;
+                } else {
+                    result = !supportDarkTheme;
+                }
+                break;
+            case Settings.Secure.THEME_MODE_LIGHT:
+                if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER) {
+                    result = supportDarkTheme;
+                }
+                break;
+            case Settings.Secure.THEME_MODE_DARK:
+                if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER) {
+                    result = !supportDarkTheme;
+                }
+                break;
+            default:
+                Slog.w(TAG, "unkonwn theme mode " + themeMode);
+                return false;
+        }
+        mThemeMode = themeMode;
+        return result;
+    }
+
+    void onThemeSettingsChanged() {
+        WallpaperData wallpaper;
+        synchronized (mLock) {
+            wallpaper = mWallpaperMap.get(mCurrentUserId);
+            int updatedThemeMode = Settings.Secure.getInt(
+                    mContext.getContentResolver(), Settings.Secure.THEME_MODE,
+                    Settings.Secure.THEME_MODE_WALLPAPER);
+
+            if (DEBUG) {
+                Slog.v(TAG, "onThemeSettingsChanged, mode = " + updatedThemeMode);
+            }
+
+            if (!needUpdateLocked(wallpaper.primaryColors, updatedThemeMode)) {
+                return;
+            }
+        }
+
+        if (wallpaper != null) {
+            notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM);
+        }
+    }
+
     void notifyLockWallpaperChanged() {
         final IWallpaperManagerCallback cb = mKeyguardListener;
         if (cb != null) {
@@ -413,6 +511,7 @@
                 }
                 userAllColorListeners.finishBroadcast();
             }
+            wallpaperColors = getThemeColorsLocked(wallpaperColors);
         }
 
         final int count = colorListeners.size();
@@ -481,6 +580,40 @@
     }
 
     /**
+     * We can easily change theme by modified colors hint. This function will check
+     * current theme mode and return the WallpaperColors fit current theme mode.
+     * If color need modified, it will return a copied WallpaperColors which
+     * its ColorsHint is modified to fit current theme mode.
+     *
+     * @param colors a wallpaper primary colors representation
+     */
+    private WallpaperColors getThemeColorsLocked(WallpaperColors colors) {
+        if (colors == null) {
+            Slog.w(TAG, "Cannot get theme colors because WallpaperColors is null.");
+            return null;
+        }
+
+        int colorHints = colors.getColorHints();
+        boolean supportDarkTheme = (colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
+        if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER ||
+                (mThemeMode == Settings.Secure.THEME_MODE_LIGHT && !supportDarkTheme) ||
+                (mThemeMode == Settings.Secure.THEME_MODE_DARK && supportDarkTheme)) {
+            return colors;
+        }
+
+        WallpaperColors themeColors = new WallpaperColors(colors.getPrimaryColor(),
+                colors.getSecondaryColor(), colors.getTertiaryColor());
+
+        if (mThemeMode == Settings.Secure.THEME_MODE_LIGHT) {
+            colorHints &= ~WallpaperColors.HINT_SUPPORTS_DARK_THEME;
+        } else if (mThemeMode == Settings.Secure.THEME_MODE_DARK) {
+            colorHints |= WallpaperColors.HINT_SUPPORTS_DARK_THEME;
+        }
+        themeColors.setColorHints(colorHints);
+        return themeColors;
+    }
+
+    /**
      * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
      * for display.
      */
@@ -676,6 +809,7 @@
     final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
     int mCurrentUserId = UserHandle.USER_NULL;
     boolean mInAmbientMode;
+    int mThemeMode;
 
     static class WallpaperData {
 
@@ -734,6 +868,7 @@
         long lastDiedTime;
         boolean wallpaperUpdating;
         WallpaperObserver wallpaperObserver;
+        ThemeSettingsObserver themeSettingsObserver;
 
         /**
          * List of callbacks registered they should each be notified when the wallpaper is changed.
@@ -1279,6 +1414,10 @@
                 wallpaper.wallpaperObserver.stopWatching();
                 wallpaper.wallpaperObserver = null;
             }
+            if (wallpaper.themeSettingsObserver != null) {
+                wallpaper.themeSettingsObserver.stopObserving(mContext);
+                wallpaper.themeSettingsObserver = null;
+            }
         }
     }
 
@@ -1362,6 +1501,13 @@
                 systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
                 systemWallpaper.wallpaperObserver.startWatching();
             }
+            if (systemWallpaper.themeSettingsObserver == null) {
+                systemWallpaper.themeSettingsObserver = new ThemeSettingsObserver(null);
+                systemWallpaper.themeSettingsObserver.startObserving(mContext);
+            }
+            mThemeMode = Settings.Secure.getInt(
+                    mContext.getContentResolver(), Settings.Secure.THEME_MODE,
+                    Settings.Secure.THEME_MODE_WALLPAPER);
             switchWallpaper(systemWallpaper, reply);
         }
 
@@ -1835,7 +1981,7 @@
         }
 
         synchronized (mLock) {
-            return wallpaperData.primaryColors;
+            return getThemeColorsLocked(wallpaperData.primaryColors);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 50d0d0a..a709c55 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -157,10 +157,18 @@
     }
 
     WindowState computeFocusedWindow() {
+        // While the keyguard is showing, we must focus anything besides the main display.
+        // Otherwise we risk input not going to the keyguard when the user expects it to.
+        final boolean forceDefaultDisplay = mService.isKeyguardShowingAndNotOccluded();
+
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             final DisplayContent dc = mChildren.get(i);
             final WindowState win = dc.findFocusedWindow();
             if (win != null) {
+                if (forceDefaultDisplay && !dc.isDefaultDisplay) {
+                    EventLog.writeEvent(0x534e4554, "71786287", win.mOwnerUid, "");
+                    continue;
+                }
                 return win;
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index cb64142..f42f855 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2846,6 +2846,11 @@
         mH.sendEmptyMessage(H.ANIMATION_FAILSAFE);
     }
 
+    @Override
+    public void onKeyguardShowingAndNotOccludedChanged() {
+        mH.sendEmptyMessage(H.RECOMPUTE_FOCUS);
+    }
+
     /**
      * Starts deferring layout passes. Useful when doing multiple changes but to optimize
      * performance, only one layout pass should be done. This can be called multiple times, and
@@ -4626,6 +4631,7 @@
         public static final int SET_HAS_OVERLAY_UI = 58;
         public static final int SET_RUNNING_REMOTE_ANIMATION = 59;
         public static final int ANIMATION_FAILSAFE = 60;
+        public static final int RECOMPUTE_FOCUS = 61;
 
         /**
          * Used to denote that an integer field in a message will not be used.
@@ -5052,6 +5058,13 @@
                     }
                 }
                 break;
+                case RECOMPUTE_FOCUS: {
+                    synchronized (mWindowMap) {
+                        updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+                                true /* updateInputWindows */);
+                    }
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG_WM, "handleMessage: exit");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index dac85b3..c797d8d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2298,8 +2298,8 @@
                     if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
                             "setAnimationLocked: setting mFocusMayChange true");
                     mService.mFocusMayChange = true;
-                    setDisplayLayoutNeeded();
                 }
+                setDisplayLayoutNeeded();
                 // Window is no longer visible -- make sure if we were waiting
                 // for it to be displayed before enabling the display, that
                 // we allow the display to be enabled now.
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 70287db..d247960 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -644,6 +644,11 @@
                 ? null : wallpaperTarget;
         final ArraySet<AppWindowToken> openingApps = mService.mOpeningApps;
         final ArraySet<AppWindowToken> closingApps = mService.mClosingApps;
+        final AppWindowToken topOpeningApp = getTopApp(mService.mOpeningApps,
+                false /* ignoreHidden */);
+        final AppWindowToken topClosingApp = getTopApp(mService.mClosingApps,
+                true /* ignoreHidden */);
+
         boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps);
         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                 "New wallpaper target=" + wallpaperTarget
@@ -677,13 +682,15 @@
                         "New transit: " + AppTransition.appTransitionToString(transit));
             } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty()
                     && !openingApps.contains(oldWallpaper.mAppToken)
-                    && closingApps.contains(oldWallpaper.mAppToken)) {
+                    && closingApps.contains(oldWallpaper.mAppToken)
+                    && topClosingApp == oldWallpaper.mAppToken) {
                 // We are transitioning from an activity with a wallpaper to one without.
                 transit = TRANSIT_WALLPAPER_CLOSE;
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
                         + AppTransition.appTransitionToString(transit));
             } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()
                     && openingApps.contains(wallpaperTarget.mAppToken)
+                    && topOpeningApp == wallpaperTarget.mAppToken
                     && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) {
                 // We are transitioning from an activity without
                 // a wallpaper to now showing the wallpaper
@@ -748,6 +755,31 @@
         return false;
     }
 
+    /**
+     * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to
+     * compare z-order.
+     *
+     * @param apps The list of apps to search.
+     * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}.
+     * @return The top {@link AppWindowToken}.
+     */
+    private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) {
+        int topPrefixOrderIndex = Integer.MIN_VALUE;
+        AppWindowToken topApp = null;
+        for (int i = apps.size() - 1; i >= 0; i--) {
+            final AppWindowToken app = apps.valueAt(i);
+            if (ignoreHidden && app.isHidden()) {
+                continue;
+            }
+            final int prefixOrderIndex = app.getPrefixOrderIndex();
+            if (prefixOrderIndex > topPrefixOrderIndex) {
+                topPrefixOrderIndex = prefixOrderIndex;
+                topApp = app;
+            }
+        }
+        return topApp;
+    }
+
     private void processApplicationsAnimatingInPlace(int transit) {
         if (transit == TRANSIT_TASK_IN_PLACE) {
             // Find the focused window
diff --git a/services/net/java/android/net/dns/ResolvUtil.java b/services/net/java/android/net/dns/ResolvUtil.java
index a2a6615..d9d4b96 100644
--- a/services/net/java/android/net/dns/ResolvUtil.java
+++ b/services/net/java/android/net/dns/ResolvUtil.java
@@ -16,6 +16,8 @@
 
 package android.net.dns;
 
+import static android.system.OsConstants.AI_ADDRCONFIG;
+
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.system.GaiException;
@@ -41,12 +43,17 @@
 
     public static InetAddress[] blockingResolveAllLocally(Network network, String name)
             throws UnknownHostException {
+        // Use AI_ADDRCONFIG by default
+        return blockingResolveAllLocally(network, name, AI_ADDRCONFIG);
+    }
+
+    public static InetAddress[] blockingResolveAllLocally(
+            Network network, String name, int aiFlags) throws UnknownHostException  {
         final StructAddrinfo hints = new StructAddrinfo();
-        // Unnecessary, but expressly no AI_ADDRCONFIG.
-        hints.ai_flags = 0;
-        // Fetch all IP addresses at once to minimize re-resolution.
+        hints.ai_flags = aiFlags;
+        // Other hints identical to the default Inet6AddressImpl implementation
         hints.ai_family = OsConstants.AF_UNSPEC;
-        hints.ai_socktype = OsConstants.SOCK_DGRAM;
+        hints.ai_socktype = OsConstants.SOCK_STREAM;
 
         final Network networkForResolv = getNetworkWithUseLocalNameserversFlag(network);
 
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index b4dc941..17babe9 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -1628,6 +1628,20 @@
         }
     }
 
+    /**
+     * Test that policy set of {null, NetworkPolicy, null} does not crash and restores the valid
+     * NetworkPolicy.
+     */
+    @Test
+    public void testSetNetworkPolicies_withNullPolicies_doesNotThrow() {
+        NetworkPolicy[] policies = new NetworkPolicy[3];
+        policies[1] = buildDefaultFakeMobilePolicy();
+        setNetworkPolicies(policies);
+
+        assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes,
+                true);
+    }
+
     private SubscriptionPlan buildMonthlyDataPlan(ZonedDateTime start, long limitBytes) {
         return SubscriptionPlan.Builder
                 .createRecurringMonthly(start)
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index dd9a8ab..7bcb571 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -170,6 +170,31 @@
     }
 
     @MediumTest
+    public void testSetUserAdmin() throws Exception {
+        UserInfo userInfo = createUser("SecondaryUser", /*flags=*/ 0);
+
+        // Assert user is not admin and has SMS and calls restrictions.
+        assertFalse(userInfo.isAdmin());
+        assertTrue(mUserManager.hasUserRestriction(UserManager.DISALLOW_SMS,
+                userInfo.getUserHandle()));
+        assertTrue(mUserManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
+                userInfo.getUserHandle()));
+
+        // Assign admin privileges.
+        mUserManager.setUserAdmin(userInfo.id);
+
+        // Refresh.
+        userInfo = mUserManager.getUserInfo(userInfo.id);
+
+        // Verify user became admin and SMS and call restrictions are lifted.
+        assertTrue(userInfo.isAdmin());
+        assertFalse(mUserManager.hasUserRestriction(UserManager.DISALLOW_SMS,
+                userInfo.getUserHandle()));
+        assertFalse(mUserManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
+                userInfo.getUserHandle()));
+    }
+
+    @MediumTest
     public void testGetProfileParent() throws Exception {
         final int primaryUserId = mUserManager.getPrimaryUser().id;
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 845095a..ac196f9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -329,6 +329,21 @@
         assertEquals(window1, sWm.mRoot.computeFocusedWindow());
     }
 
+    @Test
+    public void testKeyguard_preventsSecondaryDisplayFocus() throws Exception {
+        final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR,
+                sWm.getDefaultDisplayContentLocked(), "keyguard");
+        assertEquals(keyguard, sWm.mRoot.computeFocusedWindow());
+
+        // Add a window to a second display, and it should be focused
+        final DisplayContent dc = createNewDisplay();
+        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+        assertEquals(win, sWm.mRoot.computeFocusedWindow());
+
+        mWmRule.getWindowManagerPolicy().keyguardShowingAndNotOccluded = true;
+        assertEquals(keyguard, sWm.mRoot.computeFocusedWindow());
+    }
+
     /**
      * This tests setting the maximum ui width on a display.
      */
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 765b3d5..50f3fbe 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -51,6 +51,7 @@
     private final Supplier<WindowManagerService> mWmSupplier;
 
     int rotationToReport = 0;
+    boolean keyguardShowingAndNotOccluded = false;
 
     private Runnable mRunnableWhenAddingSplashScreen;
 
@@ -338,7 +339,7 @@
 
     @Override
     public boolean isKeyguardLocked() {
-        return false;
+        return keyguardShowingAndNotOccluded;
     }
 
     @Override
@@ -358,7 +359,7 @@
 
     @Override
     public boolean isKeyguardShowingAndNotOccluded() {
-        return false;
+        return keyguardShowingAndNotOccluded;
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
index 32bfda32..d91079e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -59,6 +59,7 @@
 public class WindowManagerServiceRule implements TestRule {
 
     private WindowManagerService mService;
+    private TestWindowManagerPolicy mPolicy;
 
     @Override
     public Statement apply(Statement base, Description description) {
@@ -108,7 +109,7 @@
                 }
 
                 mService = WindowManagerService.main(context, ims, true, false,
-                        false, new TestWindowManagerPolicy(
+                        false, mPolicy = new TestWindowManagerPolicy(
                                 WindowManagerServiceRule.this::getWindowManagerService));
 
                 mService.onInitReady();
@@ -132,6 +133,7 @@
                 waitUntilWindowManagerHandlersIdle();
                 removeServices();
                 mService = null;
+                mPolicy = null;
             }
         };
     }
@@ -140,6 +142,10 @@
         return mService;
     }
 
+    public TestWindowManagerPolicy getWindowManagerPolicy() {
+        return mPolicy;
+    }
+
     public void waitUntilWindowManagerHandlersIdle() {
         final WindowManagerService wm = getWindowManagerService();
         if (wm != null) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 3dde7f1..41b4718 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -91,10 +91,12 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
 import android.provider.Settings.Secure;
 import android.service.notification.Adjustment;
+import android.service.notification.INotificationListener;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
 import android.service.notification.NotifyingApp;
@@ -160,6 +162,8 @@
     @Mock
     private NotificationUsageStats mUsageStats;
     @Mock
+    private UsageStatsManagerInternal mAppUsageStats;
+    @Mock
     private AudioManager mAudioManager;
     @Mock
     ActivityManager mActivityManager;
@@ -276,7 +280,7 @@
                     mPackageManager, mPackageManagerClient, mockLightsManager,
                     mListeners, mAssistants, mConditionProviders,
                     mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
-                    mGroupHelper, mAm, mock(UsageStatsManagerInternal.class),
+                    mGroupHelper, mAm, mAppUsageStats,
                     mock(DevicePolicyManagerInternal.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
@@ -3019,4 +3023,46 @@
 
         assertEquals(true, mService.canUseManagedServices("d"));
     }
+
+    @Test
+    public void testOnNotificationVisibilityChanged_triggersInterruptionUsageStat() {
+        final NotificationRecord r = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, true);
+        r.setTextChanged(true);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationVisibilityChanged(new NotificationVisibility[]
+                {NotificationVisibility.obtain(r.getKey(), 1, 1, true)},
+                new NotificationVisibility[]{});
+
+        verify(mAppUsageStats).reportInterruptiveNotification(anyString(), anyString(), anyInt());
+    }
+
+    @Test
+    public void testSetNotificationsShownFromListener_triggersInterruptionUsageStat()
+            throws RemoteException {
+        final NotificationRecord r = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, true);
+        r.setTextChanged(true);
+        mService.addNotification(r);
+
+        mBinderService.setNotificationsShownFromListener(null, new String[] {r.getKey()});
+
+        verify(mAppUsageStats).reportInterruptiveNotification(anyString(), anyString(), anyInt());
+    }
+
+    @Test
+    public void testMybeRecordInterruptionLocked_doesNotRecordTwice()
+            throws RemoteException {
+        final NotificationRecord r = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, true);
+        r.setInterruptive(true);
+        mService.addNotification(r);
+
+        mService.maybeRecordInterruptionLocked(r);
+        mService.maybeRecordInterruptionLocked(r);
+
+        verify(mAppUsageStats, times(1)).reportInterruptiveNotification(
+                anyString(), anyString(), anyInt());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index e3289ab..c1868e7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -602,4 +602,45 @@
         record.setIsAppImportanceLocked(false);
         assertEquals(false, record.getIsAppImportanceLocked());
     }
+
+    @Test
+    public void testIsInterruptive_textChanged_notSeen() {
+        StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
+                false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, null /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertEquals(false, record.isInterruptive());
+
+        record.setTextChanged(true);
+        assertEquals(false, record.isInterruptive());
+    }
+
+    @Test
+    public void testIsInterruptive_textChanged_seen() {
+        StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
+                false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, null /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertEquals(false, record.isInterruptive());
+
+        record.setTextChanged(true);
+        record.setSeen();
+        assertEquals(true, record.isInterruptive());
+    }
+
+    @Test
+    public void testIsInterruptive_textNotChanged_seen() {
+        StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
+                false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, null /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertEquals(false, record.isInterruptive());
+
+        record.setTextChanged(false);
+        record.setSeen();
+        assertEquals(false, record.isInterruptive());
+    }
 }
diff --git a/tests/net/java/android/net/captiveportal/CaptivePortalProbeSpecTest.java b/tests/net/java/android/net/captiveportal/CaptivePortalProbeSpecTest.java
new file mode 100644
index 0000000..40a8b3e
--- /dev/null
+++ b/tests/net/java/android/net/captiveportal/CaptivePortalProbeSpecTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.net.captiveportal;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CaptivePortalProbeSpecTest {
+
+    @Test
+    public void testGetResult_Regex() throws MalformedURLException, ParseException {
+        // 2xx status or 404, with an empty (match everything) location regex
+        CaptivePortalProbeSpec statusRegexSpec = CaptivePortalProbeSpec.parseSpec(
+                "http://www.google.com@@/@@2[0-9]{2}|404@@/@@");
+
+        // 404, or 301/302 redirect to some HTTPS page under google.com
+        CaptivePortalProbeSpec redirectSpec = CaptivePortalProbeSpec.parseSpec(
+                "http://google.com@@/@@404|30[12]@@/@@https://([0-9a-z]+\\.)*google\\.com.*");
+
+        assertSuccess(statusRegexSpec.getResult(200, null));
+        assertSuccess(statusRegexSpec.getResult(299, "qwer"));
+        assertSuccess(statusRegexSpec.getResult(404, null));
+        assertSuccess(statusRegexSpec.getResult(404, ""));
+
+        assertPortal(statusRegexSpec.getResult(300, null));
+        assertPortal(statusRegexSpec.getResult(399, "qwer"));
+        assertPortal(statusRegexSpec.getResult(500, null));
+
+        assertSuccess(redirectSpec.getResult(404, null));
+        assertSuccess(redirectSpec.getResult(404, ""));
+        assertSuccess(redirectSpec.getResult(301, "https://www.google.com"));
+        assertSuccess(redirectSpec.getResult(301, "https://www.google.com/test?q=3"));
+        assertSuccess(redirectSpec.getResult(302, "https://google.com/test?q=3"));
+
+        assertPortal(redirectSpec.getResult(299, "https://google.com/test?q=3"));
+        assertPortal(redirectSpec.getResult(299, ""));
+        assertPortal(redirectSpec.getResult(499, null));
+        assertPortal(redirectSpec.getResult(301, "http://login.portal.example.com/loginpage"));
+        assertPortal(redirectSpec.getResult(302, "http://www.google.com/test?q=3"));
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseSpec_Empty() throws MalformedURLException, ParseException {
+        CaptivePortalProbeSpec.parseSpec("");
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseSpec_Null() throws MalformedURLException, ParseException {
+        CaptivePortalProbeSpec.parseSpec(null);
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseSpec_MissingParts() throws MalformedURLException, ParseException {
+        CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123");
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseSpec_TooManyParts() throws MalformedURLException, ParseException {
+        CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@456@@/@@extra");
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseSpec_InvalidStatusRegex() throws MalformedURLException, ParseException {
+        CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@unmatched(parenthesis@@/@@456");
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseSpec_InvalidLocationRegex() throws MalformedURLException, ParseException {
+        CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@unmatched[[]bracket");
+    }
+
+    @Test(expected = MalformedURLException.class)
+    public void testParseSpec_EmptyURL() throws MalformedURLException, ParseException {
+        CaptivePortalProbeSpec.parseSpec("@@/@@123@@/@@123");
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseSpec_NoParts() throws MalformedURLException, ParseException {
+        CaptivePortalProbeSpec.parseSpec("invalid");
+    }
+
+    @Test(expected = MalformedURLException.class)
+    public void testParseSpec_RegexInvalidUrl() throws MalformedURLException, ParseException {
+        CaptivePortalProbeSpec.parseSpec("notaurl@@/@@123@@/@@123");
+    }
+
+    @Test
+    public void testParseSpecOrNull_UsesSpec() {
+        final String specUrl = "http://google.com/probe";
+        final String redirectUrl = "https://google.com/probe";
+        CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(
+                specUrl + "@@/@@302@@/@@" + redirectUrl);
+        assertEquals(specUrl, spec.getUrl().toString());
+
+        assertPortal(spec.getResult(302, "http://portal.example.com"));
+        assertSuccess(spec.getResult(302, redirectUrl));
+    }
+
+    @Test
+    public void testParseSpecOrNull_UsesFallback() throws MalformedURLException {
+        CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(null);
+        assertNull(spec);
+
+        spec = CaptivePortalProbeSpec.parseSpecOrNull("");
+        assertNull(spec);
+
+        spec = CaptivePortalProbeSpec.parseSpecOrNull("@@/@@ @@/@@ @@/@@");
+        assertNull(spec);
+
+        spec = CaptivePortalProbeSpec.parseSpecOrNull("invalid@@/@@123@@/@@456");
+        assertNull(spec);
+    }
+
+    @Test
+    public void testParseSpecOrUseStatusCodeFallback_EmptySpec() throws MalformedURLException {
+        CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull("");
+        assertNull(spec);
+    }
+
+    private void assertIsStatusSpec(CaptivePortalProbeSpec spec) {
+        assertSuccess(spec.getResult(204, null));
+        assertSuccess(spec.getResult(204, "1234"));
+
+        assertPortal(spec.getResult(200, null));
+        assertPortal(spec.getResult(301, null));
+        assertPortal(spec.getResult(302, "1234"));
+        assertPortal(spec.getResult(399, ""));
+
+        assertFailed(spec.getResult(404, null));
+        assertFailed(spec.getResult(500, "1234"));
+    }
+
+    private void assertPortal(CaptivePortalProbeResult result) {
+        assertTrue(result.isPortal());
+    }
+
+    private void assertSuccess(CaptivePortalProbeResult result) {
+        assertTrue(result.isSuccessful());
+    }
+
+    private void assertFailed(CaptivePortalProbeResult result) {
+        assertTrue(result.isFailed());
+    }
+}