Merge "Fix a bug of not deleting previously added config files on disk"
diff --git a/Android.bp b/Android.bp
index 05fb3c0..03abf75 100644
--- a/Android.bp
+++ b/Android.bp
@@ -425,6 +425,7 @@
         "media/java/android/media/IAudioFocusDispatcher.aidl",
         "media/java/android/media/IAudioRoutesObserver.aidl",
         "media/java/android/media/IAudioService.aidl",
+        "media/java/android/media/IAudioServerStateDispatcher.aidl",
         "media/java/android/media/IMediaHTTPConnection.aidl",
         "media/java/android/media/IMediaHTTPService.aidl",
         "media/java/android/media/IMediaResourceMonitor.aidl",
diff --git a/Android.mk b/Android.mk
index f420820..3b8d6a8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -804,7 +804,7 @@
     -Iexternal/protobuf/src
 LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \
     store_unknown_fields = true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart
+LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := \
     $(call all-proto-files-under, core/proto) \
     $(call all-proto-files-under, libs/incident/proto/android/os)
diff --git a/api/current.txt b/api/current.txt
index 3edd2c0..63a40e5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -40731,8 +40731,8 @@
     method public final void setActive();
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConnectionCapabilities(int);
-    method public final void setConnectionElapsedTime(long);
     method public final void setConnectionProperties(int);
+    method public final void setConnectionStartElapsedRealTime(long);
     method public final void setConnectionTime(long);
     method public final void setDialing();
     method public final void setDisconnected(android.telecom.DisconnectCause);
diff --git a/api/system-current.txt b/api/system-current.txt
index 953941f..39cbe90 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -437,6 +437,19 @@
     field public static final int STATE_USER_UNMANAGED = 0; // 0x0
   }
 
+  public class SystemUpdatePolicy implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR;
+    field public static final int TYPE_PAUSE = 4; // 0x4
+  }
+
+  public static class SystemUpdatePolicy.InstallationOption {
+    method public long getEffectiveTime();
+    method public int getType();
+  }
+
 }
 
 package android.app.backup {
@@ -2524,12 +2537,15 @@
 
   public class AudioManager {
     method public deprecated int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
+    method public void clearAudioServerStateCallback();
     method public int dispatchAudioFocusChange(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy);
+    method public boolean isAudioServerRunning();
     method public boolean isHdmiSystemAudioSupported();
     method public int registerAudioPolicy(android.media.audiopolicy.AudioPolicy);
     method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
     method public deprecated int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
     method public int requestAudioFocus(android.media.AudioFocusRequest, android.media.audiopolicy.AudioPolicy);
+    method public void setAudioServerStateCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioServerStateCallback);
     method public void setFocusRequestResult(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy);
     method public void unregisterAudioPolicyAsync(android.media.audiopolicy.AudioPolicy);
     field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
@@ -2537,6 +2553,12 @@
     field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
   }
 
+  public static abstract class AudioManager.AudioServerStateCallback {
+    ctor public AudioManager.AudioServerStateCallback();
+    method public void onAudioServerDown();
+    method public void onAudioServerUp();
+  }
+
   public final class AudioPlaybackConfiguration implements android.os.Parcelable {
     method public int getClientPid();
     method public int getClientUid();
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index ce3a4b9..45fb5da 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -268,7 +268,9 @@
     int field = getSimpleField(key);
     for (const auto& value : mValues) {
         if (value.mField.getField() == field) {
-            if (value.mValue.getType() == INT) {
+            if (value.mValue.getType() == LONG) {
+                return value.mValue.long_value;
+            } else if (value.mValue.getType() == INT) {
                 return value.mValue.int_value;
             } else {
                 *err = BAD_TYPE;
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 293b1a8..cb731c5 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -59,7 +59,6 @@
 
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t bucketStartTimeNs = 10000000000;
-    uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t durationTimeNs = 2 * 1000;
@@ -95,7 +94,6 @@
 
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t bucketStartTimeNs = 10000000000;
-    uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
 
@@ -129,7 +127,6 @@
 
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t bucketStartTimeNs = 10000000000;
-    uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
 
@@ -162,7 +159,6 @@
 
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t bucketStartTimeNs = 10000000000;
-    uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t durationTimeNs = 2 * 1000;
@@ -210,7 +206,6 @@
 
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t bucketStartTimeNs = 10000000000;
-    uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t durationTimeNs = 2 * 1000;
@@ -253,7 +248,6 @@
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
-    uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t durationTimeNs = 2 * 1000;
@@ -296,7 +290,6 @@
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
-    uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
 
@@ -338,7 +331,6 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
-    uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
 
@@ -408,7 +400,6 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
-    uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
 
@@ -421,15 +412,15 @@
     tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
     EXPECT_TRUE(tracker.mStarted.empty());
-    EXPECT_EQ(10LL, tracker.mDuration);
+    EXPECT_EQ(10LL, tracker.mDuration); // 10ns
 
     EXPECT_EQ(0u, tracker.mStarted.size());
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey());
     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
-    EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
+    EXPECT_EQ((long long)(52ULL * NS_PER_SEC),  // (10s + 1s + 1ns + 20ns) - 10ns + 40s, rounded up
               (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
-    // The alarm is set to fire at 51s, and when it does, an anomaly would be declared. However,
+    // The alarm is set to fire at 52s, and when it does, an anomaly would be declared. However,
     // because this is a unit test, the alarm won't actually fire at all. Since the alarm fails
     // to fire in time, the anomaly is instead caught when noteStop is called, at around 71s.
     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets);
@@ -460,7 +451,6 @@
     ConditionKey conkey;
     conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
-    uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
diff --git a/core/java/android/app/admin/FreezeInterval.java b/core/java/android/app/admin/FreezeInterval.java
index 7acdfc8..de5e21a 100644
--- a/core/java/android/app/admin/FreezeInterval.java
+++ b/core/java/android/app/admin/FreezeInterval.java
@@ -84,6 +84,10 @@
         }
     }
 
+    boolean after(LocalDate localDate) {
+        return mStartDay > dayOfYearDisregardLeapYear(localDate);
+    }
+
     /**
      * Instantiate the current interval to real calendar dates, given a calendar date
      * {@code now}. If the interval contains now, the returned calendar dates should be the
@@ -161,7 +165,7 @@
      *     3. At most one wrapped Interval remains, and it will be at the end of the list
      * @hide
      */
-    private static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) {
+    protected static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) {
         boolean[] taken = new boolean[DAYS_IN_YEAR];
         // First convert the intervals into flat array
         for (FreezeInterval interval : intervals) {
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index 05d3fd9..47b3a81 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -21,6 +21,7 @@
 import static org.xmlpull.v1.XmlPullParser.TEXT;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -33,9 +34,15 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 /**
@@ -103,6 +110,19 @@
      */
     public static final int TYPE_POSTPONE = 3;
 
+    /**
+     * Incoming system updates (including security updates) should be blocked. This flag is not
+     * exposed to third-party apps (and any attempt to set it will raise exceptions). This is used
+     * to represent the current installation option type to the privileged system update clients,
+     * for example to indicate OTA freeze is currently in place or when system is outside a daily
+     * maintenance window.
+     *
+     * @see InstallationOption
+     * @hide
+     */
+    @SystemApi
+    public static final int TYPE_PAUSE = 4;
+
     private static final String KEY_POLICY_TYPE = "policy_type";
     private static final String KEY_INSTALL_WINDOW_START = "install_window_start";
     private static final String KEY_INSTALL_WINDOW_END = "install_window_end";
@@ -460,6 +480,30 @@
         return null;
     }
 
+    /**
+     * Returns time (in milliseconds) until the start of the next freeze period, assuming now
+     * is not within a freeze period.
+     */
+    private long timeUntilNextFreezePeriod(long now) {
+        List<FreezeInterval> sortedPeriods = FreezeInterval.canonicalizeIntervals(mFreezePeriods);
+        LocalDate nowDate = millisToDate(now);
+        LocalDate nextFreezeStart = null;
+        for (FreezeInterval interval : sortedPeriods) {
+            if (interval.after(nowDate)) {
+                nextFreezeStart = interval.toCurrentOrFutureRealDates(nowDate).first;
+                break;
+            } else if (interval.contains(nowDate)) {
+                throw new IllegalArgumentException("Given date is inside a freeze period");
+            }
+        }
+        if (nextFreezeStart == null) {
+            // If no interval is after now, then it must be the one that starts at the beginning
+            // of next year
+            nextFreezeStart = sortedPeriods.get(0).toCurrentOrFutureRealDates(nowDate).first;
+        }
+        return dateToMillis(nextFreezeStart) - now;
+    }
+
     /** @hide */
     public void validateFreezePeriods() {
         FreezeInterval.validatePeriods(mFreezePeriods);
@@ -472,6 +516,134 @@
                 prevPeriodEnd, now);
     }
 
+    /**
+     * An installation option represents how system update clients should act on incoming system
+     * updates and how long this action is valid for, given the current system update policy. Its
+     * action could be one of the following
+     * <ul>
+     * <li> {@code TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and without
+     * user intervention as soon as they become available.
+     * <li> {@code TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days
+     * <li> {@code TYPE_PAUSE} system updates should be postponed indefinitely until further notice
+     * </ul>
+     *
+     * The effective time measures how long this installation option is valid for from the queried
+     * time, in milliseconds.
+     *
+     * This is an internal API for system update clients.
+     * @hide
+     */
+    @SystemApi
+    public static class InstallationOption {
+        private final int mType;
+        private long mEffectiveTime;
+
+        InstallationOption(int type, long effectiveTime) {
+            this.mType = type;
+            this.mEffectiveTime = effectiveTime;
+        }
+
+        public int getType() {
+            return mType;
+        }
+
+        public long getEffectiveTime() {
+            return mEffectiveTime;
+        }
+
+        /** @hide */
+        protected void limitEffectiveTime(long otherTime) {
+            mEffectiveTime = Long.min(mEffectiveTime, otherTime);
+        }
+    }
+
+    /**
+     * Returns the installation option at the specified time, under the current
+     * {@code SystemUpdatePolicy} object. This is a convenience method for system update clients
+     * so they can instantiate this policy at any given time and find out what to do with incoming
+     * system updates, without the need of examining the overall policy structure.
+     *
+     * Normally the system update clients will query the current installation option by calling this
+     * method with the current timestamp, and act on the returned option until its effective time
+     * lapses. It can then query the latest option using a new timestamp. It should also listen
+     * for {@code DevicePolicyManager#ACTION_SYSTEM_UPDATE_POLICY_CHANGED} broadcast, in case the
+     * whole policy is updated.
+     *
+     * @param when At what time the intallation option is being queried, specified in number of
+           milliseonds since the epoch.
+     * @see InstallationOption
+     * @hide
+     */
+    @SystemApi
+    public InstallationOption getInstallationOptionAt(long when) {
+        LocalDate whenDate = millisToDate(when);
+        Pair<LocalDate, LocalDate> current = getCurrentFreezePeriod(whenDate);
+        if (current != null) {
+            return new InstallationOption(TYPE_PAUSE,
+                    dateToMillis(roundUpLeapDay(current.second).plusDays(1)) - when);
+        }
+        // We are not within a freeze period, query the underlying policy.
+        // But also consider the start of the next freeze period, which might
+        // reduce the effective time of the current installation option
+        InstallationOption option = getInstallationOptionRegardlessFreezeAt(when);
+        if (mFreezePeriods.size() > 0) {
+            option.limitEffectiveTime(timeUntilNextFreezePeriod(when));
+        }
+        return option;
+    }
+
+    private InstallationOption getInstallationOptionRegardlessFreezeAt(long when) {
+        if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
+            return new InstallationOption(mPolicyType, Long.MAX_VALUE);
+        } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+            Calendar query = Calendar.getInstance();
+            query.setTimeInMillis(when);
+            // Calculate the number of milliseconds since midnight of the time specified by when
+            long whenMillis = TimeUnit.HOURS.toMillis(query.get(Calendar.HOUR_OF_DAY))
+                    + TimeUnit.MINUTES.toMillis(query.get(Calendar.MINUTE))
+                    + TimeUnit.SECONDS.toMillis(query.get(Calendar.SECOND))
+                    + query.get(Calendar.MILLISECOND);
+            long windowStartMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowStart);
+            long windowEndMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowEnd);
+            final long dayInMillis = TimeUnit.DAYS.toMillis(1);
+
+            if ((windowStartMillis <= whenMillis && whenMillis <= windowEndMillis)
+                    || ((windowStartMillis > windowEndMillis)
+                    && (windowStartMillis <= whenMillis || whenMillis <= windowEndMillis))) {
+                return new InstallationOption(TYPE_INSTALL_AUTOMATIC,
+                        (windowEndMillis - whenMillis + dayInMillis) % dayInMillis);
+            } else {
+                return new InstallationOption(TYPE_PAUSE,
+                        (windowStartMillis - whenMillis + dayInMillis) % dayInMillis);
+            }
+        } else {
+            throw new RuntimeException("Unknown policy type");
+        }
+    }
+
+    private static LocalDate roundUpLeapDay(LocalDate date) {
+        if (date.isLeapYear() && date.getMonthValue() == 2 && date.getDayOfMonth() == 28) {
+            return date.plusDays(1);
+        } else {
+            return date;
+        }
+    }
+
+    /** Convert a timestamp since epoch to a LocalDate using default timezone, truncating
+     * the hour/min/seconds part.
+     */
+    private static LocalDate millisToDate(long when) {
+        return Instant.ofEpochMilli(when).atZone(ZoneId.systemDefault()).toLocalDate();
+    }
+
+    /**
+     * Returns the timestamp since epoch of a LocalDate, assuming the time is 00:00:00.
+     */
+    private static long dateToMillis(LocalDate when) {
+        return LocalDateTime.of(when, LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant()
+                .toEpochMilli();
+    }
+
     @Override
     public String toString() {
         return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d, "
@@ -480,11 +652,13 @@
                 mFreezePeriods.stream().map(n -> n.toString()).collect(Collectors.joining(",")));
     }
 
+    @SystemApi
     @Override
     public int describeContents() {
         return 0;
     }
 
+    @SystemApi
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mPolicyType);
@@ -499,6 +673,7 @@
         }
     }
 
+    @SystemApi
     public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR =
             new Parcelable.Creator<SystemUpdatePolicy>() {
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index aa8faf8..07a9911 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2070,6 +2070,15 @@
             "android.hardware.sensor.hifi_sensors";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports a hardware mechanism for invoking an assist gesture.
+     * @see android.provider.Settings.Secure#ASSIST_GESTURE_ENABLED
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_ASSIST_GESTURE = "android.hardware.sensor.assist";
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has a telephony radio with data
      * communication support.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fef6495..8ab8361 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12134,6 +12134,12 @@
          * @hide
          */
         public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog";
+
+        /**
+         * If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
+         * @hide
+         */
+        public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
     }
 
     /**
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index d66322c..171d4d9 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -94,7 +94,7 @@
     private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true;
     private static final boolean DEFAULT_ALLOW_SCREEN_ON = true;
 
-    private static final int XML_VERSION = 2;
+    public static final int XML_VERSION = 3;
     public static final String ZEN_TAG = "zen";
     private static final String ZEN_ATT_VERSION = "version";
     private static final String ZEN_ATT_USER = "user";
@@ -145,6 +145,7 @@
     public int user = UserHandle.USER_SYSTEM;
     public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
     public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
+    public int version;
 
     public ZenRule manualRule;
     public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
@@ -431,6 +432,7 @@
         String tag = parser.getName();
         if (!ZEN_TAG.equals(tag)) return null;
         final ZenModeConfig rt = new ZenModeConfig();
+        rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
         rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
             tag = parser.getName();
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index bb9e391..7bae28a 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -21,6 +21,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pools;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -46,7 +47,7 @@
     public final Rect boundsInScreen = new Rect();
     public List<IBinder> childTokens;
     public CharSequence title;
-    public int accessibilityIdOfAnchor = View.NO_ID;
+    public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
     public boolean inPictureInPicture;
 
     private WindowInfo() {
@@ -105,7 +106,7 @@
         parcel.writeInt(focused ? 1 : 0);
         boundsInScreen.writeToParcel(parcel, flags);
         parcel.writeCharSequence(title);
-        parcel.writeInt(accessibilityIdOfAnchor);
+        parcel.writeLong(accessibilityIdOfAnchor);
         parcel.writeInt(inPictureInPicture ? 1 : 0);
 
         if (childTokens != null && !childTokens.isEmpty()) {
@@ -142,7 +143,7 @@
         focused = (parcel.readInt() == 1);
         boundsInScreen.readFromParcel(parcel);
         title = parcel.readCharSequence();
-        accessibilityIdOfAnchor = parcel.readInt();
+        accessibilityIdOfAnchor = parcel.readLong();
         inPictureInPicture = (parcel.readInt() == 1);
 
         final boolean hasChildren = (parcel.readInt() == 1);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1c5e871..c0a9666 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -63,6 +63,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -2344,7 +2345,7 @@
          *
          * @hide
          */
-        public int accessibilityIdOfAnchor = -1;
+        public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
 
         /**
          * The window title isn't kept in sync with what is displayed in the title bar, so we
@@ -2538,7 +2539,7 @@
             out.writeInt(hasManualSurfaceInsets ? 1 : 0);
             out.writeInt(preservePreviousSurfaceInsets ? 1 : 0);
             out.writeInt(needsMenuKey);
-            out.writeInt(accessibilityIdOfAnchor);
+            out.writeLong(accessibilityIdOfAnchor);
             TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
             out.writeInt(mColorMode);
             out.writeLong(hideTimeoutMilliseconds);
@@ -2594,7 +2595,7 @@
             hasManualSurfaceInsets = in.readInt() != 0;
             preservePreviousSurfaceInsets = in.readInt() != 0;
             needsMenuKey = in.readInt();
-            accessibilityIdOfAnchor = in.readInt();
+            accessibilityIdOfAnchor = in.readLong();
             accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             mColorMode = in.readInt();
             hideTimeoutMilliseconds = in.readLong();
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 4a181b2..44adbb2 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -49,6 +49,7 @@
     public static String USB = "USB";
     public static String FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
     public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
+    public static String SYSTEM_CHANGES = "SYSTEM_CHANGES";
 
     public static void createAll(Context context) {
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -152,6 +153,11 @@
                 .build());
         channelsList.add(heavyWeightChannel);
 
+        NotificationChannel systemChanges = new NotificationChannel(SYSTEM_CHANGES,
+                context.getString(R.string.notification_channel_system_changes),
+                NotificationManager.IMPORTANCE_LOW);
+        channelsList.add(systemChanges);
+
         nm.createNotificationChannels(channelsList);
     }
 
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
index 65615c0..444049e 100644
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
@@ -78,10 +78,11 @@
                 final long userTimeUs = Long.parseLong(splitter.next(), 10);
                 final long systemTimeUs = Long.parseLong(splitter.next(), 10);
 
+                boolean notifyCallback = false;
+                long userTimeDeltaUs = userTimeUs;
+                long systemTimeDeltaUs = systemTimeUs;
                 // Only report if there is a callback and if this is not the first read.
                 if (callback != null && mLastTimeReadUs != 0) {
-                    long userTimeDeltaUs = userTimeUs;
-                    long systemTimeDeltaUs = systemTimeUs;
                     int index = mLastUserTimeUs.indexOfKey(uid);
                     if (index >= 0) {
                         userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
@@ -114,12 +115,13 @@
                         }
                     }
 
-                    if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) {
-                        callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
-                    }
+                    notifyCallback = (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0);
                 }
                 mLastUserTimeUs.put(uid, userTimeUs);
                 mLastSystemTimeUs.put(uid, systemTimeUs);
+                if (notifyCallback) {
+                    callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
+                }
             }
         } catch (IOException e) {
             Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index e69a360..fac6b23 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -53,8 +53,8 @@
     public static final int DISABLE_VERIFIER = 1 << 9;
     /** Only use oat files located in /system. Otherwise use dex/jar/apk . */
     public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
-    /** Do not enfore hidden API access restrictions. */
-    public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
+    /** Do enfore hidden API access restrictions. */
+    public static final int ENABLE_HIDDEN_API_CHECKS = 1 << 11;
     /** Force generation of native debugging information for backtraces. */
     public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
 
@@ -160,9 +160,6 @@
      */
     public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
-        // SystemServer is always allowed to use hidden APIs.
-        runtimeFlags |= DISABLE_HIDDEN_API_CHECKS;
-
         VM_HOOKS.preFork();
         // Resets nice priority for zygote process.
         resetNicePriority();
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 74802c8..9c89976 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -98,10 +98,6 @@
 
     private static final String SOCKET_NAME_ARG = "--socket-name=";
 
-    /* Dexopt flag to disable hidden API access checks when dexopting SystemServer.
-     * Must be kept in sync with com.android.server.pm.Installer. */
-    private static final int DEXOPT_DISABLE_HIDDEN_API_CHECKS = 1 << 10;
-
     /**
      * Used to pre-load resources.
      */
@@ -569,10 +565,7 @@
             if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                 final String packageName = "*";
                 final String outputPath = null;
-                // Dexopt with a flag which lifts restrictions on hidden API usage.
-                // Offending methods would otherwise be re-verified at runtime and
-                // we want to avoid the performance overhead of that.
-                final int dexFlags = DEXOPT_DISABLE_HIDDEN_API_CHECKS;
+                final int dexFlags = 0;
                 final String compilerFilter = systemServerFilter;
                 final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
                 final String seInfo = null;
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index f2b7ab2..52ee9e8 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -426,10 +426,11 @@
     optional SettingProto show_first_crash_dialog = 349 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto show_restart_in_crash_dialog = 351 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto show_mute_in_crash_dialog = 352 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingsProto show_zen_upgrade_notification = 354  [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     // Please insert fields in the same order as in
     // frameworks/base/core/java/android/provider/Settings.java.
-    // Next tag = 354;
+    // Next tag = 355;
 }
 
 message SecureSettingsProto {
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ec81df7..0efb6f9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4854,4 +4854,11 @@
 
     <!-- Notification action for editing a screenshot (drawing on it, cropping it, etc) -->
     <string name="screenshot_edit">Edit</string>
+
+    <!-- Title for the notification channel notifying user of settings system changes (i.e. Do Not Disturb has changed). [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_system_changes">System changes</string>
+    <!-- Title of notification indicating do not disturb settings have changed when upgrading to P -->
+    <string name="zen_upgrade_notification_title">Do Not Disturb has changed</string>
+    <!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
+    <string name="zen_upgrade_notification_content">Tap to check your behavior settings for interruptions</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index af25398..5a9dc7f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1716,6 +1716,7 @@
   <java-symbol type="string" name="bugreport_status" />
   <java-symbol type="string" name="bugreport_title" />
   <java-symbol type="string" name="faceunlock_multiple_failures" />
+  <java-symbol type="string" name="global_actions" />
   <java-symbol type="string" name="global_action_power_off" />
   <java-symbol type="string" name="global_action_restart" />
   <java-symbol type="string" name="global_actions_airplane_mode_off_status" />
@@ -3102,6 +3103,7 @@
   <java-symbol type="string" name="notification_channel_retail_mode" />
   <java-symbol type="string" name="notification_channel_usb" />
   <java-symbol type="string" name="notification_channel_heavy_weight_app" />
+  <java-symbol type="string" name="notification_channel_system_changes" />
   <java-symbol type="string" name="config_defaultAutofillService" />
   <java-symbol type="string" name="config_defaultTextClassifierService" />
 
@@ -3257,4 +3259,7 @@
 
   <!-- For Wear devices -->
   <java-symbol type="array" name="config_wearActivityModeRadios" />
+
+  <java-symbol type="string" name="zen_upgrade_notification_title" />
+  <java-symbol type="string" name="zen_upgrade_notification_content" />
 </resources>
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index a446088..7849a2a 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,7 +18,7 @@
 -->
 
 <!-- Default configuration for zen mode.  See android.service.notification.ZenModeConfig. -->
-<zen version="2">
+<zen version="3">
     <allow alarms="true" media_system_other="true" calls="false" messages="false" reminders="false"
            events="false" />
 </zen>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index a0b6297..c36026c 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -354,6 +354,7 @@
                     Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
                     Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
                     Settings.Global.SHOW_TEMPERATURE_WARNING,
+                    Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION,
                     Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
                     Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
                     Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 0be54ec..9ff964b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -65,6 +66,8 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
 
 /**
  * AudioManager provides access to volume and ringer mode control.
@@ -4864,6 +4867,114 @@
         }
     }
 
+
+    /**
+     * @hide
+     * Abstract class to receive event notification about audioserver process state.
+     */
+    @SystemApi
+    public abstract static class AudioServerStateCallback {
+        public void onAudioServerDown() { }
+        public void onAudioServerUp() { }
+    }
+
+    private Executor mAudioServerStateExec;
+    private AudioServerStateCallback mAudioServerStateCb;
+    private final Object mAudioServerStateCbLock = new Object();
+
+    private final IAudioServerStateDispatcher mAudioServerStateDispatcher =
+            new IAudioServerStateDispatcher.Stub() {
+        @Override
+        public void dispatchAudioServerStateChange(boolean state) {
+            Executor exec;
+            AudioServerStateCallback cb;
+
+            synchronized (mAudioServerStateCbLock) {
+                exec = mAudioServerStateExec;
+                cb = mAudioServerStateCb;
+            }
+
+            if ((exec == null) || (cb == null)) {
+                return;
+            }
+            if (state) {
+                exec.execute(() -> cb.onAudioServerUp());
+            } else {
+                exec.execute(() -> cb.onAudioServerDown());
+            }
+        }
+    };
+
+    /**
+     * @hide
+     * Registers a callback for notification of audio server state changes.
+     * @param executor {@link Executor} to handle the callbacks
+     * @param stateCallback the callback to receive the audio server state changes
+     *        To remove the callabck, pass a null reference for both executor and stateCallback.
+     */
+    @SystemApi
+    public void setAudioServerStateCallback(@NonNull Executor executor,
+            @NonNull AudioServerStateCallback stateCallback) {
+        if (stateCallback == null) {
+            throw new IllegalArgumentException("Illegal null AudioServerStateCallback");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException(
+                    "Illegal null Executor for the AudioServerStateCallback");
+        }
+
+        synchronized (mAudioServerStateCbLock) {
+            if (mAudioServerStateCb != null) {
+                throw new IllegalStateException(
+                    "setAudioServerStateCallback called with already registered callabck");
+            }
+            final IAudioService service = getService();
+            try {
+                service.registerAudioServerStateDispatcher(mAudioServerStateDispatcher);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mAudioServerStateExec = executor;
+            mAudioServerStateCb = stateCallback;
+        }
+    }
+
+    /**
+     * @hide
+     * Unregisters the callback for notification of audio server state changes.
+     */
+    @SystemApi
+    public void clearAudioServerStateCallback() {
+        synchronized (mAudioServerStateCbLock) {
+            if (mAudioServerStateCb != null) {
+                final IAudioService service = getService();
+                try {
+                    service.unregisterAudioServerStateDispatcher(
+                            mAudioServerStateDispatcher);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            mAudioServerStateExec = null;
+            mAudioServerStateCb = null;
+        }
+    }
+
+    /**
+     * @hide
+     * Checks if native audioservice is running or not.
+     * @return true if native audioservice runs, false otherwise.
+     */
+    @SystemApi
+    public boolean isAudioServerRunning() {
+        final IAudioService service = getService();
+        try {
+            return service.isAudioServerRunning();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     //---------------------------------------------------------
     // Inner classes
     //--------------------
diff --git a/media/java/android/media/IAudioServerStateDispatcher.aidl b/media/java/android/media/IAudioServerStateDispatcher.aidl
new file mode 100644
index 0000000..2bc90ea
--- /dev/null
+++ b/media/java/android/media/IAudioServerStateDispatcher.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.media;
+
+/**
+ * AIDL for the AudioService to signal audio server state changes
+ *
+ * {@hide}
+ */
+oneway interface IAudioServerStateDispatcher {
+
+    void dispatchAudioServerStateChange(boolean state);
+
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 4c37014..05ba4c3 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -24,10 +24,12 @@
 import android.media.AudioRoutesInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
+import android.media.IAudioServerStateDispatcher;
 import android.media.IPlaybackConfigDispatcher;
 import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
 import android.media.IVolumeController;
+import android.media.IVolumeController;
 import android.media.PlayerBase;
 import android.media.VolumePolicy;
 import android.media.audiopolicy.AudioPolicyConfig;
@@ -208,6 +210,12 @@
     oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
             in IAudioPolicyCallback pcb);
 
+    void registerAudioServerStateDispatcher(IAudioServerStateDispatcher asd);
+
+    oneway void unregisterAudioServerStateDispatcher(IAudioServerStateDispatcher asd);
+
+    boolean isAudioServerRunning();
+
     // WARNING: read warning at top of file, new methods that need to be used by native
     // code via IAudioManager.h need to be added to the top section.
 }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index b8184a0..b8d01c4 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -119,7 +119,8 @@
     private final ISession mBinder;
     private final CallbackStub mCbStub;
 
-    private CallbackMessageHandler mCallbackHandler;
+    // Do not change the name of mCallback. Support lib accesses this by using reflection.
+    private CallbackMessageHandler mCallback;
     private VolumeProvider mVolumeProvider;
     private PlaybackState mPlaybackState;
 
@@ -194,13 +195,13 @@
      */
     public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
         synchronized (mLock) {
-            if (mCallbackHandler != null) {
+            if (mCallback != null) {
                 // We're updating the callback, clear the session from the old one.
-                mCallbackHandler.mCallback.mSession = null;
-                mCallbackHandler.removeCallbacksAndMessages(null);
+                mCallback.mCallback.mSession = null;
+                mCallback.removeCallbacksAndMessages(null);
             }
             if (callback == null) {
-                mCallbackHandler = null;
+                mCallback = null;
                 return;
             }
             if (handler == null) {
@@ -209,7 +210,7 @@
             callback.mSession = this;
             CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
                     callback);
-            mCallbackHandler = msgHandler;
+            mCallback = msgHandler;
         }
     }
 
@@ -634,8 +635,8 @@
 
     private void postToCallback(int what, Object obj, Bundle extras) {
         synchronized (mLock) {
-            if (mCallbackHandler != null) {
-                mCallbackHandler.post(what, obj, extras);
+            if (mCallback != null) {
+                mCallback.post(what, obj, extras);
             }
         }
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1551b8e..6cf5eef 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1123,6 +1123,9 @@
         dumpSetting(s, p,
                 Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG,
                 GlobalSettingsProto.SHOW_MUTE_IN_CRASH_DIALOG);
+        dumpSetting(s, p,
+                Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION,
+                GlobalSettingsProto.SHOW_ZEN_UPGRADE_NOTIFICATION);
 
         // Please insert new settings using the same order as in Settings.Global.
     }
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..2d62a80
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..a52e5b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step.png
new file mode 100644
index 0000000..d13c730
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step_dark.png
new file mode 100644
index 0000000..31c602e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..ddbcb07
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..ce91fcbb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..c606a58
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step_dark.png
new file mode 100644
index 0000000..c2a5fef
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index 8078c41..2470947 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -41,6 +41,15 @@
         android:scaleType="centerInside"
         />
     <com.android.systemui.statusbar.policy.KeyButtonView
+        android:id="@+id/rotate_suggestion"
+        android:layout_width="@dimen/navigation_extra_key_width"
+        android:layout_height="match_parent"
+        android:layout_marginEnd="2dp"
+        android:visibility="invisible"
+        android:scaleType="centerInside"
+        android:contentDescription="@string/accessibility_rotate_button"
+        />
+    <com.android.systemui.statusbar.policy.KeyButtonView
         android:id="@+id/accessibility_button"
         android:layout_width="@dimen/navigation_extra_key_width"
         android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3f6c85f..d4eba45 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -884,7 +884,7 @@
     <dimen name="nav_quick_scrub_track_thickness">2dp</dimen>
 
     <!-- Home button padding for sizing -->
-    <dimen name="home_padding">15dp</dimen>
+    <dimen name="home_padding">16dp</dimen>
 
     <!-- Smart reply button -->
     <dimen name="smart_reply_button_corner_radius">24dip</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index dc082a9..8c59e75 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1751,15 +1751,13 @@
     <string-array name="nav_bar_buttons">
         <item>Clipboard</item>
         <item>Keycode</item>
-        <item>Keyboard switcher</item>
-        <item>Rotation suggestion</item>
+        <item>Rotate confirm, keyboard switcher</item>
         <item>None</item>
     </string-array>
     <string-array name="nav_bar_button_values" translatable="false">
         <item>clipboard</item>
         <item>key</item>
         <item>menu_ime</item>
-        <item>rotate</item>
         <item>space</item>
     </string-array>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 7f382ac..e200a7f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -23,6 +23,14 @@
     void onBind(in ISystemUiProxy sysUiProxy);
 
     /**
+     * Called once immediately prior to the first onMotionEvent() call, providing a hint to the
+     * target the initial source of the subsequent motion events.
+     *
+     * @param downHitTarget is one of the {@link NavigationBarCompat.HitTarget}s
+     */
+    void onPreMotionEvent(int downHitTarget);
+
+    /**
      * Proxies motion events from the nav bar in SystemUI to the OverviewProxyService. The sender
      * guarantees the following order of events:
      *
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 62bd72f..138910c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -316,6 +316,17 @@
     }
 
     /**
+     * Cancels the remote recents animation started from {@link #startRecentsActivity}.
+     */
+    public void cancelRecentsAnimation() {
+        try {
+            ActivityManager.getService().cancelRecentsAnimation();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to cancel recents animation", e);
+        }
+    }
+
+    /**
      * Starts a task from Recents.
      *
      * @see {@link #startActivityFromRecentsAsync(TaskKey, ActivityOptions, int, int, Consumer, Handler)}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
new file mode 100644
index 0000000..f622d4a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public class NavigationBarCompat {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({HIT_TARGET_NONE, HIT_TARGET_BACK, HIT_TARGET_HOME})
+    public @interface HitTarget{}
+
+    public static final int HIT_TARGET_NONE = 0;
+    public static final int HIT_TARGET_BACK = 1;
+    public static final int HIT_TARGET_HOME = 2;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index c28b7ee..1aea5e7 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1369,6 +1369,7 @@
             mListView = findViewById(android.R.id.list);
             mHardwareLayout = HardwareUiLayout.get(mListView);
             mHardwareLayout.setOutsideTouchListener(view -> dismiss());
+            setTitle(R.string.global_actions);
         }
 
         private void updateList() {
@@ -1464,20 +1465,6 @@
         }
 
         @Override
-        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
-                for (int i = 0; i < mAdapter.getCount(); ++i) {
-                    CharSequence label =
-                            mAdapter.getItem(i).getLabelForAccessibility(getContext());
-                    if (label != null) {
-                        event.getText().add(label);
-                    }
-                }
-            }
-            return super.dispatchPopulateAccessibilityEvent(event);
-        }
-
-        @Override
         public void onColorsChanged(ColorExtractor extractor, int which) {
             if (mKeyguardShowing) {
                 if ((WallpaperManager.FLAG_LOCK & which) != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index eb5619b..0876507 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -308,6 +308,10 @@
         }
     }
 
+    public void setRippleAllowed(boolean allowed) {
+        mBackgroundNormal.setPressedAllowed(allowed);
+    }
+
     private boolean handleTouchEventDimmed(MotionEvent event) {
         if (mNeedsDimming && !mDimmed) {
             // We're actually dimmed, but our content isn't dimmable, let's ensure we have a ripple
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index b3f68d3..2723df7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -370,14 +370,6 @@
         mNotificationInflater.inflateNotificationViews();
     }
 
-    @Override
-    public void setPressed(boolean pressed) {
-        if (isOnKeyguard() || mEntry.notification.getNotification().contentIntent == null) {
-            // We're dropping the ripple if we have a collapse / launch animation
-            super.setPressed(pressed);
-        }
-    }
-
     public void onNotificationUpdated() {
         for (NotificationContentView l : mLayouts) {
             l.onNotificationUpdated(mEntry);
@@ -407,6 +399,7 @@
 
         showBlockingHelper(mEntry.userSentiment ==
                 NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+        updateRippleAllowed();
     }
 
     @VisibleForTesting
@@ -1805,6 +1798,13 @@
                 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
             }
         }
+        updateRippleAllowed();
+    }
+
+    private void updateRippleAllowed() {
+        boolean allowed = isOnKeyguard()
+                || mEntry.notification.getNotification().contentIntent == null;
+        setRippleAllowed(allowed);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index ab89a52..0ff4dde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -28,6 +28,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -50,6 +51,7 @@
     private boolean mExpandAnimationRunning;
     private float mActualWidth;
     private int mDrawableAlpha = 255;
+    private boolean mIsPressedAllowed;
 
     public NotificationBackgroundView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -94,13 +96,7 @@
 
     @Override
     protected void drawableStateChanged() {
-        drawableStateChanged(mBackground);
-    }
-
-    private void drawableStateChanged(Drawable d) {
-        if (d != null && d.isStateful()) {
-            d.setState(getDrawableState());
-        }
+        setState(getDrawableState());
     }
 
     @Override
@@ -177,7 +173,13 @@
     }
 
     public void setState(int[] drawableState) {
-        mBackground.setState(drawableState);
+        if (mBackground != null && mBackground.isStateful()) {
+            if (!mIsPressedAllowed) {
+                drawableState = ArrayUtils.removeInt(drawableState,
+                        com.android.internal.R.attr.state_pressed);
+            }
+            mBackground.setState(drawableState);
+        }
     }
 
     public void setRippleColor(int color) {
@@ -258,4 +260,8 @@
         }
         invalidate();
     }
+
+    public void setPressedAllowed(boolean allowed) {
+        mIsPressedAllowed = allowed;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 1239a9e..72938c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -114,8 +114,6 @@
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
 
-    private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
-
     protected NavigationBarView mNavigationBarView = null;
     protected AssistManager mAssistManager;
 
@@ -152,7 +150,7 @@
     private RotationLockController mRotationLockController;
     private TaskStackListenerImpl mTaskStackListener;
 
-    private final Runnable mRemoveRotationProposal = () -> setRotateSuggestionButtonState(false);
+    private final Runnable mRemoveRotationProposal = () -> safeSetRotationButtonState(false);
     private Animator mRotateShowAnimator;
     private Animator mRotateHideAnimator;
 
@@ -361,22 +359,32 @@
         // rotate button if shown.
 
         if (!isValid) {
-            setRotateSuggestionButtonState(false);
+            safeSetRotationButtonState(false);
             return;
         }
 
         if (rotation == mWindowManager.getDefaultDisplay().getRotation()) {
             // Use this as a signal to remove any current suggestions
             getView().getHandler().removeCallbacks(mRemoveRotationProposal);
-            setRotateSuggestionButtonState(false);
+            safeSetRotationButtonState(false);
         } else {
             mLastRotationSuggestion = rotation; // Remember rotation for click
-            setRotateSuggestionButtonState(true);
+            safeSetRotationButtonState(true);
             rescheduleRotationTimeout(false);
             mMetricsLogger.visible(MetricsEvent.ROTATION_SUGGESTION_SHOWN);
         }
     }
 
+    private void safeSetRotationButtonState(boolean vis) {
+        if (mNavigationBarView != null) mNavigationBarView.setRotateSuggestionButtonState(vis);
+    }
+
+    private void safeSetRotationButtonState(boolean vis, boolean force) {
+        if (mNavigationBarView != null) {
+            mNavigationBarView.setRotateSuggestionButtonState(vis, force);
+        }
+    }
+
     private void rescheduleRotationTimeout(final boolean reasonHover) {
         // May be called due to a new rotation proposal or a change in hover state
         if (reasonHover) {
@@ -402,84 +410,6 @@
         return 6000;
     }
 
-    public void setRotateSuggestionButtonState(final boolean visible) {
-        setRotateSuggestionButtonState(visible, false);
-    }
-
-    public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) {
-        ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
-        final boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
-
-        // Rerun a show animation to indicate change but don't rerun a hide animation
-        if (!visible && !currentlyVisible) return;
-
-        View currentView = rotBtn.getCurrentView();
-        if (currentView == null) return;
-
-        KeyButtonDrawable kbd = rotBtn.getImageDrawable();
-        if (kbd == null) return;
-
-        AnimatedVectorDrawable animIcon = null;
-        if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
-            animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
-        }
-
-        if (visible) { // Appear and change
-            rotBtn.setVisibility(View.VISIBLE);
-            mNavigationBarView.notifySubtreeAccessibilityStateChangedIfNeeded();
-
-            if (skipAnim) {
-                currentView.setAlpha(1f);
-                return;
-            }
-
-            // Start a new animation if running
-            if (mRotateShowAnimator != null) mRotateShowAnimator.pause();
-            if (mRotateHideAnimator != null) mRotateHideAnimator.pause();
-
-            ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha",
-                    0f, 1f);
-            appearFade.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
-            appearFade.setInterpolator(Interpolators.LINEAR);
-            mRotateShowAnimator = appearFade;
-            appearFade.start();
-
-            // Run the rotate icon's animation if it has one
-            if (animIcon != null) {
-                animIcon.reset();
-                animIcon.start();
-            }
-
-        } else { // Hide
-
-            if (skipAnim) {
-                rotBtn.setVisibility(View.INVISIBLE);
-                mNavigationBarView.notifySubtreeAccessibilityStateChangedIfNeeded();
-                return;
-            }
-
-            // Don't start any new hide animations if one is running
-            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
-            // Pause any active show animations but don't reset the AVD to avoid jumps
-            if (mRotateShowAnimator != null) mRotateShowAnimator.pause();
-
-            ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
-                    0f);
-            fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
-            fadeOut.setInterpolator(Interpolators.LINEAR);
-            fadeOut.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    rotBtn.setVisibility(View.INVISIBLE);
-                    mNavigationBarView.notifySubtreeAccessibilityStateChangedIfNeeded();
-                }
-            });
-
-            mRotateHideAnimator = fadeOut;
-            fadeOut.start();
-        }
-    }
-
     // Injected from StatusBar at creation.
     public void setCurrentSysuiVisibility(int systemUiVisibility) {
         mSystemUiVisibility = systemUiVisibility;
@@ -892,7 +822,7 @@
                     if (shouldOverrideUserLockPrefs(rotation)) {
                         mRotationLockController.setRotationLockedAtAngle(true, rotation);
                     }
-                    setRotateSuggestionButtonState(false, true);
+                    safeSetRotationButtonState(false, true);
                 }
 
                 if (mNavigationBarView != null
@@ -928,22 +858,22 @@
 
         @Override
         public void onTaskStackChanged() {
-            setRotateSuggestionButtonState(false);
+            safeSetRotationButtonState(false);
         }
 
         @Override
         public void onTaskRemoved(int taskId) {
-            setRotateSuggestionButtonState(false);
+            safeSetRotationButtonState(false);
         }
 
         @Override
         public void onTaskMovedToFront(int taskId) {
-            setRotateSuggestionButtonState(false);
+            safeSetRotationButtonState(false);
         }
 
         @Override
         public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
-            setRotateSuggestionButtonState(false);
+            safeSetRotationButtonState(false);
         }
     }
 
@@ -960,6 +890,7 @@
                 PixelFormat.TRANSLUCENT);
         lp.token = new Binder();
         lp.setTitle("NavigationBar");
+        lp.accessibilityTitle = context.getString(R.string.nav_bar);
         lp.windowAnimations = 0;
 
         View navigationBarView = LayoutInflater.from(context).inflate(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index d15c771..63f2ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -131,6 +131,9 @@
             mNavigationBarView.requestUnbufferedDispatch(event);
             event.transform(mTransformGlobalMatrix);
             try {
+                if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                    overviewProxy.onPreMotionEvent(mNavigationBarView.getDownHitTarget());
+                }
                 overviewProxy.onMotionEvent(event);
                 if (DEBUG_OVERVIEW_PROXY) {
                     Log.d(TAG_OPS, "Send MotionEvent: " + event.toString());
@@ -146,8 +149,8 @@
     }
 
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        int action = event.getAction();
-        switch (action & MotionEvent.ACTION_MASK) {
+        int action = event.getActionMasked();
+        switch (action) {
             case MotionEvent.ACTION_DOWN: {
                 mTouchDownX = (int) event.getX();
                 mTouchDownY = (int) event.getY();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 9d20e4e..9894235 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -57,13 +57,12 @@
     public static final String NAV_BAR_LEFT = "sysui_nav_bar_left";
     public static final String NAV_BAR_RIGHT = "sysui_nav_bar_right";
 
-    public static final String MENU_IME = "menu_ime";
+    public static final String MENU_IME_ROTATE = "menu_ime";
     public static final String BACK = "back";
     public static final String HOME = "home";
     public static final String RECENT = "recent";
     public static final String NAVSPACE = "space";
     public static final String CLIPBOARD = "clipboard";
-    public static final String ROTATE = "rotate";
     public static final String KEY = "key";
     public static final String LEFT = "left";
     public static final String RIGHT = "right";
@@ -317,10 +316,10 @@
         View v = null;
         String button = extractButton(buttonSpec);
         if (LEFT.equals(button)) {
-            String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, ROTATE);
+            String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, NAVSPACE);
             button = extractButton(s);
         } else if (RIGHT.equals(button)) {
-            String s = Dependency.get(TunerService.class).getValue(NAV_BAR_RIGHT, MENU_IME);
+            String s = Dependency.get(TunerService.class).getValue(NAV_BAR_RIGHT, MENU_IME_ROTATE);
             button = extractButton(s);
         }
         // Let plugins go first so they can override a standard view if they want.
@@ -334,14 +333,12 @@
             v = inflater.inflate(R.layout.back, parent, false);
         } else if (RECENT.equals(button)) {
             v = inflater.inflate(R.layout.recent_apps, parent, false);
-        } else if (MENU_IME.equals(button)) {
+        } else if (MENU_IME_ROTATE.equals(button)) {
             v = inflater.inflate(R.layout.menu_ime, parent, false);
         } else if (NAVSPACE.equals(button)) {
             v = inflater.inflate(R.layout.nav_key_space, parent, false);
         } else if (CLIPBOARD.equals(button)) {
             v = inflater.inflate(R.layout.clipboard, parent, false);
-        } else if (ROTATE.equals(button)) {
-            v = inflater.inflate(R.layout.rotate_suggestion, parent, false);
         } else if (button.startsWith(KEY)) {
             String uri = extractImage(button);
             int code = extractKeycode(button);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index af0afbd..53dc814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -16,6 +16,13 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.view.MotionEvent.ACTION_DOWN;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.LayoutTransition;
 import android.animation.LayoutTransition.TransitionListener;
 import android.animation.ObjectAnimator;
@@ -29,6 +36,7 @@
 import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteException;
@@ -49,6 +57,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.DockedStackExistsListener;
+import com.android.systemui.Interpolators;
 import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
@@ -57,6 +66,7 @@
 import com.android.systemui.plugins.statusbar.phone.NavGesture;
 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
 import com.android.systemui.recents.RecentsOnboarding;
+import com.android.systemui.shared.system.NavigationBarCompat;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.policy.DeadZone;
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -70,6 +80,8 @@
     final static boolean DEBUG = false;
     final static String TAG = "StatusBar/NavBarView";
 
+    final static int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
+
     // slippery nav bar when everything is disabled, e.g. during setup
     final static boolean SLIPPERY_WHEN_DISABLED = true;
 
@@ -85,9 +97,15 @@
     boolean mShowMenu;
     boolean mShowAccessibilityButton;
     boolean mLongClickableAccessibilityButton;
+    boolean mShowRotateButton;
     int mDisabledFlags = 0;
     int mNavigationIconHints = 0;
 
+    private @NavigationBarCompat.HitTarget int mDownHitTarget = HIT_TARGET_NONE;
+    private Rect mHomeButtonBounds = new Rect();
+    private Rect mBackButtonBounds = new Rect();
+    private int[] mTmpPosition = new int[2];
+
     private KeyButtonDrawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
     private KeyButtonDrawable mBackCarModeIcon, mBackLandCarModeIcon;
     private KeyButtonDrawable mBackAltCarModeIcon, mBackAltLandCarModeIcon;
@@ -127,6 +145,8 @@
     private RecentsOnboarding mRecentsOnboarding;
     private NotificationPanelView mPanelView;
 
+    private Animator mRotateHideAnimator;
+
     private class NavTransitionListener implements TransitionListener {
         private boolean mBackTransitioning;
         private boolean mHomeAppearing;
@@ -217,6 +237,9 @@
         mShowAccessibilityButton = false;
         mLongClickableAccessibilityButton = false;
 
+        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+        mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
+
         mConfiguration = new Configuration();
         mConfiguration.updateFrom(context.getResources().getConfiguration());
         updateIcons(context, Configuration.EMPTY, mConfiguration);
@@ -232,9 +255,6 @@
                 new ButtonDispatcher(R.id.accessibility_button));
         mButtonDispatchers.put(R.id.rotate_suggestion,
                 new ButtonDispatcher(R.id.rotate_suggestion));
-
-        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
-        mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
     }
 
     public BarTransitions getBarTransitions() {
@@ -275,6 +295,18 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        switch (event.getActionMasked()) {
+            case ACTION_DOWN:
+                int x = (int) event.getX();
+                int y = (int) event.getY();
+                mDownHitTarget = HIT_TARGET_NONE;
+                if (mBackButtonBounds.contains(x, y)) {
+                    mDownHitTarget = HIT_TARGET_BACK;
+                } else if (mHomeButtonBounds.contains(x, y)) {
+                    mDownHitTarget = HIT_TARGET_HOME;
+                }
+                break;
+        }
         return mGestureHelper.onInterceptTouchEvent(event);
     }
 
@@ -286,6 +318,10 @@
         return super.onTouchEvent(event);
     }
 
+    public @NavigationBarCompat.HitTarget int getDownHitTarget() {
+        return mDownHitTarget;
+    }
+
     public void abortCurrentGesture() {
         getHomeButton().abortCurrentGesture();
     }
@@ -355,14 +391,23 @@
         }
         if (oldConfig.densityDpi != newConfig.densityDpi
                 || oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {
-            mBackIcon = getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark);
+            final boolean proxyAvailable = mOverviewProxyService.getProxy() != null;
+            mBackIcon = proxyAvailable
+                    ? getDrawable(ctx, R.drawable.ic_sysbar_back_quick_step,
+                            R.drawable.ic_sysbar_back_quick_step_dark)
+                    : getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark);
             mBackLandIcon = mBackIcon;
-            mBackAltIcon = getDrawable(ctx,
-                    R.drawable.ic_sysbar_back_ime, R.drawable.ic_sysbar_back_ime_dark);
+            mBackAltIcon = proxyAvailable
+                    ? getDrawable(ctx, R.drawable.ic_sysbar_back_ime_quick_step,
+                            R.drawable.ic_sysbar_back_ime_quick_step_dark)
+                    : getDrawable(ctx, R.drawable.ic_sysbar_back_ime,
+                            R.drawable.ic_sysbar_back_ime_dark);
             mBackAltLandIcon = mBackAltIcon;
 
-            mHomeDefaultIcon = getDrawable(ctx,
-                    R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
+            mHomeDefaultIcon = proxyAvailable
+                    ? getDrawable(ctx, R.drawable.ic_sysbar_home_quick_step,
+                            R.drawable.ic_sysbar_home_quick_step_dark)
+                    : getDrawable(ctx, R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
             mRecentIcon = getDrawable(ctx,
                     R.drawable.ic_sysbar_recent, R.drawable.ic_sysbar_recent_dark);
             mMenuIcon = getDrawable(ctx, R.drawable.ic_sysbar_menu, R.drawable.ic_sysbar_menu_dark);
@@ -462,22 +507,25 @@
             getHomeButton().setImageDrawable(mHomeDefaultIcon);
         }
 
-        // The Accessibility button always overrides the appearance of the IME switcher
+        // Update IME button visibility, a11y and rotate button always overrides the appearance
         final boolean showImeButton =
-                !mShowAccessibilityButton && ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN)
-                        != 0);
+                !mShowAccessibilityButton &&
+                !mShowRotateButton &&
+                ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
         getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
         getImeSwitchButton().setImageDrawable(mImeIcon);
 
-        // Update menu button in case the IME state has changed.
+        // Update menu button, visibility logic in method
         setMenuVisibility(mShowMenu, true);
         getMenuButton().setImageDrawable(mMenuIcon);
 
+        // Update rotate button, visibility altered by a11y button logic
+        getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
+
+        // Update a11y button, visibility logic in state method
         setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton);
         getAccessibilityButton().setImageDrawable(mAccessibilityIcon);
 
-        getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
-
         setDisabledFlags(mDisabledFlags, true);
 
         mBarTransitions.reapplyDarkIntensity();
@@ -621,8 +669,10 @@
 
         mShowMenu = show;
 
-        // Only show Menu if IME switcher and Accessibility button not shown.
-        final boolean shouldShow = mShowMenu && !mShowAccessibilityButton &&
+        // Only show Menu if IME switcher, rotate and Accessibility buttons are not shown.
+        final boolean shouldShow = mShowMenu &&
+                !mShowAccessibilityButton &&
+                !mShowRotateButton &&
                 ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0);
 
         getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
@@ -632,15 +682,96 @@
         mShowAccessibilityButton = visible;
         mLongClickableAccessibilityButton = longClickable;
         if (visible) {
-            // Accessibility button overrides Menu and IME switcher buttons.
+            // Accessibility button overrides Menu, IME switcher and rotate buttons.
             setMenuVisibility(false, true);
             getImeSwitchButton().setVisibility(View.INVISIBLE);
+            setRotateSuggestionButtonState(false, true);
         }
 
         getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
         getAccessibilityButton().setLongClickable(longClickable);
     }
 
+    public void setRotateSuggestionButtonState(final boolean visible) {
+        setRotateSuggestionButtonState(visible, false);
+    }
+
+    public void setRotateSuggestionButtonState(final boolean visible, final boolean force) {
+        ButtonDispatcher rotBtn = getRotateSuggestionButton();
+        final boolean currentlyVisible = mShowRotateButton;
+
+        // Rerun a show animation to indicate change but don't rerun a hide animation
+        if (!visible && !currentlyVisible) return;
+
+        View currentView = rotBtn.getCurrentView();
+        if (currentView == null) return;
+
+        KeyButtonDrawable kbd = rotBtn.getImageDrawable();
+        if (kbd == null) return;
+
+        AnimatedVectorDrawable animIcon = null;
+        if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
+            animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
+        }
+
+        if (visible) { // Appear and change, cannot force
+            setRotateButtonVisibility(true);
+
+            // Stop any currently running hide animations
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+                mRotateHideAnimator.pause();
+            }
+
+            // Reset the alpha if any has changed due to hide animation
+            currentView.setAlpha(1f);
+
+            // Run the rotate icon's animation if it has one
+            if (animIcon != null) {
+                animIcon.reset();
+                animIcon.start();
+            }
+
+        } else { // Hide
+            if (force) {
+                // If a hide animator is running stop it and instantly make invisible
+                if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+                    mRotateHideAnimator.pause();
+                }
+                setRotateButtonVisibility(false);
+                return;
+            }
+
+            // Don't start any new hide animations if one is running
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
+
+            ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
+                    0f);
+            fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
+            fadeOut.setInterpolator(Interpolators.LINEAR);
+            fadeOut.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    setRotateButtonVisibility(false);
+                }
+            });
+
+            mRotateHideAnimator = fadeOut;
+            fadeOut.start();
+        }
+    }
+
+    private void setRotateButtonVisibility(final boolean visible) {
+        // Never show if a11y is visible
+        final boolean adjVisible = visible && !mShowAccessibilityButton;
+        final int vis = adjVisible ? View.VISIBLE : View.INVISIBLE;
+
+        getRotateSuggestionButton().setVisibility(vis);
+        mShowRotateButton = visible;
+
+        // Hide/restore other button visibility, if necessary
+        setNavigationIconHints(mNavigationIconHints, true);
+    }
+
     @Override
     public void onFinishInflate() {
         mNavigationInflaterView = (NavigationBarInflaterView) findViewById(
@@ -666,6 +797,8 @@
         setSlippery(!isConnected);
         setDisabledFlags(mDisabledFlags, true);
         setUpSwipeUpOnboarding(isConnected);
+        updateIcons(getContext(), Configuration.EMPTY, mConfiguration);
+        setNavigationIconHints(mNavigationIconHints, true);
     }
 
     @Override
@@ -677,9 +810,23 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
+        updateButtonLocationOnScreen(getBackButton(), mBackButtonBounds);
+        updateButtonLocationOnScreen(getHomeButton(), mHomeButtonBounds);
         mGestureHelper.onLayout(changed, left, top, right, bottom);
     }
 
+    private void updateButtonLocationOnScreen(ButtonDispatcher button, Rect buttonBounds) {
+        View view = button.getCurrentView();
+        if (view == null) {
+            buttonBounds.setEmpty();
+            return;
+        }
+        view.getLocationInWindow(mTmpPosition);
+        buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
+                mTmpPosition[0] + view.getMeasuredWidth(),
+                mTmpPosition[1] + view.getMeasuredHeight());
+    }
+
     private void updateRotatedViews() {
         mRotatedViews[Surface.ROTATION_0] =
                 mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index dc0835e..bb2f597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -53,6 +53,7 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
 import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
 import static com.android.systemui.OverviewProxyService.TAG_OPS;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
 
 /**
  * Class to detect gestures on the navigation bar and implement quick scrub and switch.
@@ -92,7 +93,6 @@
     private final Handler mHandler = new Handler();
     private final Interpolator mQuickScrubEndInterpolator = new DecelerateInterpolator();
     private final Rect mTrackRect = new Rect();
-    private final Rect mHomeButtonRect = new Rect();
     private final Paint mTrackPaint = new Paint();
     private final int mScrollTouchSlop;
     private final OverviewProxyService mOverviewEventSender;
@@ -138,7 +138,7 @@
             @Override
             public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
                 if (!isQuickScrubEnabled() || mQuickScrubActive || !mAllowQuickSwitch ||
-                        !mHomeButtonRect.contains(mTouchDownX, mTouchDownY)) {
+                        mNavigationBarView.getDownHitTarget() != HIT_TARGET_HOME) {
                     return false;
                 }
                 float velocityX = mIsRTL ? -velX : velX;
@@ -226,7 +226,8 @@
             case MotionEvent.ACTION_DOWN: {
                 int x = (int) event.getX();
                 int y = (int) event.getY();
-                if (isQuickScrubEnabled() && mHomeButtonRect.contains(x, y)) {
+                if (isQuickScrubEnabled()
+                        && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME) {
                     mTouchDownX = x;
                     mTouchDownY = y;
                     homeButton.setDelayTouchFeedback(true);
@@ -346,17 +347,6 @@
             x2 = x1 + width / 2 - mTrackPadding;
         }
         mTrackRect.set(x1, y1, x2, y2);
-
-        // Get the touch rect of the home button location
-        View homeView = mNavigationBarView.getHomeButton().getCurrentView();
-        if (homeView != null) {
-            int[] globalHomePos = homeView.getLocationOnScreen();
-            int[] globalNavBarPos = mNavigationBarView.getLocationOnScreen();
-            int homeX = globalHomePos[0] - globalNavBarPos[0];
-            int homeY = globalHomePos[1] - globalNavBarPos[1];
-            mHomeButtonRect.set(homeX, homeY, homeX + homeView.getMeasuredWidth(),
-                    homeY + homeView.getMeasuredHeight());
-        }
     }
 
     @Override
@@ -381,7 +371,7 @@
     }
 
     boolean isQuickScrubEnabled() {
-        return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", false);
+        return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true);
     }
 
     private void startQuickScrub() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1438763..94ebc1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -138,7 +138,8 @@
     protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
     protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
 
-    private float mFraction;
+    // Assuming the shade is expanded during initialization
+    private float mExpansionFraction = 1f;
 
     private boolean mDarkenWhileDragging;
     protected boolean mAnimateChange;
@@ -166,6 +167,7 @@
     private boolean mScreenBlankingCallbackCalled;
     private Callback mCallback;
     private boolean mWallpaperSupportsAmbientMode;
+    private boolean mScreenOn;
 
     // Scrim blanking callbacks
     private Choreographer.FrameCallback mPendingFrameCallback;
@@ -251,6 +253,7 @@
         mCurrentBehindTint = state.getBehindTint();
         mCurrentInFrontAlpha = state.getFrontAlpha();
         mCurrentBehindAlpha = state.getBehindAlpha();
+        applyExpansionToAlpha();
 
         // Cancel blanking transitions that were pending before we requested a new state
         if (mPendingFrameCallback != null) {
@@ -362,45 +365,50 @@
      * @param fraction From 0 to 1 where 0 means collapse and 1 expanded.
      */
     public void setPanelExpansion(float fraction) {
-        if (mFraction != fraction) {
-            mFraction = fraction;
+        if (mExpansionFraction != fraction) {
+            mExpansionFraction = fraction;
 
-            if (mState == ScrimState.UNLOCKED) {
-                // Darken scrim as you pull down the shade when unlocked
-                float behindFraction = getInterpolatedFraction();
-                behindFraction = (float) Math.pow(behindFraction, 0.8f);
-                mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
-                mCurrentInFrontAlpha = 0;
-            } else if (mState == ScrimState.KEYGUARD) {
-                if (mUpdatePending) {
-                    return;
-                }
+            if (!(mState == ScrimState.UNLOCKED || mState == ScrimState.KEYGUARD)) {
+                return;
+            }
 
-                // Either darken of make the scrim transparent when you
-                // pull down the shade
-                float interpolatedFract = getInterpolatedFraction();
-                if (mDarkenWhileDragging) {
-                    mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
-                            mScrimBehindAlphaKeyguard, interpolatedFract);
-                    mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
-                } else {
-                    mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
-                            interpolatedFract);
-                    mCurrentInFrontAlpha = 0;
-                }
-            } else {
+            applyExpansionToAlpha();
+
+            if (mUpdatePending) {
                 return;
             }
 
             if (mPinnedHeadsUpCount != 0) {
                 updateHeadsUpScrim(false);
             }
-
             updateScrim(false /* animate */, mScrimInFront, mCurrentInFrontAlpha);
             updateScrim(false /* animate */, mScrimBehind, mCurrentBehindAlpha);
         }
     }
 
+    private void applyExpansionToAlpha() {
+        if (mState == ScrimState.UNLOCKED) {
+            // Darken scrim as you pull down the shade when unlocked
+            float behindFraction = getInterpolatedFraction();
+            behindFraction = (float) Math.pow(behindFraction, 0.8f);
+            mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
+            mCurrentInFrontAlpha = 0;
+        } else if (mState == ScrimState.KEYGUARD) {
+            // Either darken of make the scrim transparent when you
+            // pull down the shade
+            float interpolatedFract = getInterpolatedFraction();
+            if (mDarkenWhileDragging) {
+                mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
+                        mScrimBehindAlphaKeyguard, interpolatedFract);
+                mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
+            } else {
+                mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
+                        interpolatedFract);
+                mCurrentInFrontAlpha = 0;
+            }
+        }
+    }
+
     /**
      * Keyguard and shade scrim opacity varies according to how many notifications are visible.
      * @param notificationCount Number of visible notifications.
@@ -496,7 +504,7 @@
     }
 
     private float getInterpolatedFraction() {
-        float frac = mFraction;
+        float frac = mExpansionFraction;
         // let's start this 20% of the way down the screen
         frac = frac * 1.2f - 0.2f;
         if (frac <= 0) {
@@ -785,10 +793,11 @@
 
             // Setting power states can happen after we push out the frame. Make sure we
             // stay fully opaque until the power state request reaches the lower levels.
+            final int delay = mScreenOn ? 16 : 500;
             if (DEBUG) {
-                Log.d(TAG, "Waiting for the screen to turn on...");
+                Log.d(TAG, "Fading out scrims with delay: " + delay);
             }
-            getHandler().postDelayed(mBlankingTransitionRunnable, 500);
+            getHandler().postDelayed(mBlankingTransitionRunnable, delay);
         };
         doOnTheNextFrame(mPendingFrameCallback);
     }
@@ -825,7 +834,7 @@
         } else {
             alpha = 1.0f - mTopHeadsUpDragAmount;
         }
-        float expandFactor = (1.0f - mFraction);
+        float expandFactor = (1.0f - mExpansionFraction);
         expandFactor = Math.max(expandFactor, 0.0f);
         return alpha * expandFactor;
     }
@@ -904,6 +913,7 @@
      * Interrupts blanking transitions once the display notifies that it's already on.
      */
     public void onScreenTurnedOn() {
+        mScreenOn = true;
         final Handler handler = getHandler();
         if (handler.hasCallbacks(mBlankingTransitionRunnable)) {
             if (DEBUG) {
@@ -914,6 +924,10 @@
         }
     }
 
+    public void onScreenTurnedOff() {
+        mScreenOn = false;
+    }
+
     public interface Callback {
         default void onStart() {
         }
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 458518c..3b63d6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4413,6 +4413,7 @@
         @Override
         public void onScreenTurnedOff() {
             mFalsingManager.onScreenOff();
+            mScrimController.onScreenTurnedOff();
             // If we pulse in from AOD, we turn the screen off first. However, updatingIsKeyguard
             // in that case destroys the HeadsUpManager state, so don't do it in that case.
             if (!isPulsing()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index c30f633..948f524 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -106,6 +106,7 @@
         mLp.gravity = Gravity.TOP;
         mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
         mLp.setTitle("StatusBar");
+        mLp.accessibilityTitle = mContext.getString(R.string.status_bar);
         mLp.packageName = mContext.getPackageName();
         mStatusBarView = statusBarView;
         mBarHeight = barHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 1da50ad..503a1b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -36,6 +36,7 @@
 public class StatusIconContainer extends AlphaOptimizedLinearLayout {
 
     private static final String TAG = "StatusIconContainer";
+    private static final boolean DEBUG = false;
     private static final int MAX_ICONS = 5;
     private static final int MAX_DOTS = 3;
 
@@ -94,7 +95,7 @@
         int childCount = getChildCount();
         // Underflow === don't show content until that index
         int firstUnderflowIndex = -1;
-        android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
+        if (DEBUG) android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
 
         //TODO: Dots
         for (int i = childCount - 1; i >= 0; i--) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 9d1c1e8..98bebec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -52,6 +52,7 @@
 import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
 
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
@@ -269,11 +270,7 @@
                     if (doIt) {
                         // If there was a pending remote recents animation, then we need to
                         // cancel the animation now before we handle the button itself
-                        try {
-                            ActivityManager.getService().cancelRecentsAnimation();
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Could not cancel recents animation", e);
-                        }
+                        ActivityManagerWrapper.getInstance().cancelRecentsAnimation();
                         sendEvent(KeyEvent.ACTION_UP, 0);
                         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
                     } else {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
index 45abd45..eb0c89b 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
@@ -18,7 +18,7 @@
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_END;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_START;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_IMAGE_DELIM;
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.MENU_IME;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.MENU_IME_ROTATE;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAVSPACE;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_LEFT;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_RIGHT;
@@ -29,24 +29,14 @@
 
 import android.annotation.Nullable;
 import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.res.Resources;
 import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.FontMetricsInt;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.v14.preference.PreferenceFragment;
-import android.support.v7.preference.DropDownPreference;
 import android.support.v7.preference.ListPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
-import android.support.v7.preference.Preference.OnPreferenceClickListener;
-import android.support.v7.preference.PreferenceCategory;
 import android.text.SpannableStringBuilder;
 import android.text.style.ImageSpan;
 import android.util.Log;
@@ -56,7 +46,6 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.NavigationBarInflaterView;
 import com.android.systemui.tuner.TunerService.Tunable;
 
 import java.util.ArrayList;
@@ -100,7 +89,7 @@
         addPreferencesFromResource(R.xml.nav_bar_tuner);
         bindLayout((ListPreference) findPreference(LAYOUT));
         bindButton(NAV_BAR_LEFT, NAVSPACE, LEFT);
-        bindButton(NAV_BAR_RIGHT, MENU_IME, RIGHT);
+        bindButton(NAV_BAR_RIGHT, MENU_IME_ROTATE, RIGHT);
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 43e16db..4702793 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -180,6 +180,7 @@
 
     @Test
     public void transitionToUnlocked() {
+        mScrimController.setPanelExpansion(0f);
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be transparent
@@ -197,6 +198,7 @@
     public void transitionToUnlockedFromAod() {
         // Simulate unlock with fingerprint
         mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.setPanelExpansion(0f);
         mScrimController.finishAnimationsImmediately();
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         // Immediately tinted after the transition starts
@@ -324,6 +326,23 @@
         verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
     }
 
+    @Test
+    public void testConservesExpansionOpacityAfterTransition() {
+        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.setPanelExpansion(0.5f);
+        mScrimController.finishAnimationsImmediately();
+
+        final float expandedAlpha = mScrimBehind.getViewAlpha();
+
+        mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
+        mScrimController.finishAnimationsImmediately();
+        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.finishAnimationsImmediately();
+
+        Assert.assertEquals("Scrim expansion opacity wasn't conserved when transitioning back",
+                expandedAlpha, mScrimBehind.getViewAlpha(), 0.01f);
+    }
+
     private void assertScrimTint(ScrimView scrimView, boolean tinted) {
         final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
         final String name = scrimView == mScrimInFront ? "front" : "back";
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 08fdb97..608970f 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -204,6 +204,10 @@
     // Package: android
     NOTE_USB_TETHER = 47;
 
+    // Inform that DND settings have changed on OS upgrade
+    // Package: android
+    NOTE_ZEN_UPGRADE = 48;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
@@ -253,6 +257,7 @@
 
     // Notify the user about public volume state changes..
     // Package: com.android.systemui
+
     NOTE_STORAGE_PUBLIC = 0x53505542;  // 1397773634
 
     // Notify the user about private volume state changes.
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 6747be3..5e5eacb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -64,6 +64,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.NoSuchElementException;
 
 /**
  * Since phone process can be restarted, this class provides a centralized place
@@ -90,6 +91,8 @@
 
         IBinder binder;
 
+        TelephonyRegistryDeathRecipient deathRecipient;
+
         IPhoneStateListener callback;
         IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
 
@@ -251,6 +254,21 @@
         }
     };
 
+    private class TelephonyRegistryDeathRecipient implements IBinder.DeathRecipient {
+
+        private final IBinder binder;
+
+        TelephonyRegistryDeathRecipient(IBinder binder) {
+            this.binder = binder;
+        }
+
+        @Override
+        public void binderDied() {
+            if (DBG) log("binderDied " + binder);
+            remove(binder);
+        }
+    }
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -378,23 +396,14 @@
             }
         }
 
-        Record r;
 
         synchronized (mRecords) {
             // register
-            find_and_add: {
-                IBinder b = callback.asBinder();
-                final int N = mRecords.size();
-                for (int i = 0; i < N; i++) {
-                    r = mRecords.get(i);
-                    if (b == r.binder) {
-                        break find_and_add;
-                    }
-                }
-                r = new Record();
-                r.binder = b;
-                mRecords.add(r);
-                if (DBG) log("listen oscl: add new record");
+            IBinder b = callback.asBinder();
+            Record r = add(b);
+
+            if (r == null) {
+                return;
             }
 
             r.onSubscriptionsChangedListenerCallback = callback;
@@ -496,20 +505,11 @@
 
             synchronized (mRecords) {
                 // register
-                Record r;
-                find_and_add: {
-                    IBinder b = callback.asBinder();
-                    final int N = mRecords.size();
-                    for (int i = 0; i < N; i++) {
-                        r = mRecords.get(i);
-                        if (b == r.binder) {
-                            break find_and_add;
-                        }
-                    }
-                    r = new Record();
-                    r.binder = b;
-                    mRecords.add(r);
-                    if (DBG) log("listen: add new record");
+                IBinder b = callback.asBinder();
+                Record r = add(b);
+
+                if (r == null) {
+                    return;
                 }
 
                 r.callback = callback;
@@ -697,16 +697,57 @@
         return record.canReadPhoneState ? mCallIncomingNumber[phoneId] : "";
     }
 
+    private Record add(IBinder binder) {
+        Record r;
+
+        synchronized (mRecords) {
+            final int N = mRecords.size();
+            for (int i = 0; i < N; i++) {
+                r = mRecords.get(i);
+                if (binder == r.binder) {
+                    // Already existed.
+                    return r;
+                }
+            }
+            r = new Record();
+            r.binder = binder;
+            r.deathRecipient = new TelephonyRegistryDeathRecipient(binder);
+
+            try {
+                binder.linkToDeath(r.deathRecipient, 0);
+            } catch (RemoteException e) {
+                if (VDBG) log("LinkToDeath remote exception sending to r=" + r + " e=" + e);
+                // Binder already died. Return null.
+                return null;
+            }
+
+            mRecords.add(r);
+            if (DBG) log("add new record");
+        }
+
+        return r;
+    }
+
     private void remove(IBinder binder) {
         synchronized (mRecords) {
             final int recordCount = mRecords.size();
             for (int i = 0; i < recordCount; i++) {
-                if (mRecords.get(i).binder == binder) {
+                Record r = mRecords.get(i);
+                if (r.binder == binder) {
                     if (DBG) {
-                        Record r = mRecords.get(i);
-                        log("remove: binder=" + binder + "r.callingPackage" + r.callingPackage
-                                + "r.callback" + r.callback);
+                        log("remove: binder=" + binder + " r.callingPackage " + r.callingPackage
+                                + " r.callback " + r.callback);
                     }
+
+                    if (r.deathRecipient != null) {
+                        try {
+                            binder.unlinkToDeath(r.deathRecipient, 0);
+                        } catch (NoSuchElementException e) {
+                            if (VDBG) log("UnlinkToDeath NoSuchElementException sending to r="
+                                    + r + " e=" + e);
+                        }
+                    }
+
                     mRecords.remove(i);
                     return;
                 }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 14404f5..5fc4373 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1095,7 +1095,7 @@
                     active.mNumActive++;
                 }
                 r.isForeground = true;
-                StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.userId, r.shortName,
+                StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
                         StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
             }
             r.postNotification();
@@ -1112,7 +1112,7 @@
                     decActiveForegroundAppLocked(smap, r);
                 }
                 r.isForeground = false;
-                StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.userId, r.shortName,
+                StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
                         StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
                 if (r.app != null) {
                     mAm.updateLruProcessLocked(r.app, false, null);
@@ -2538,7 +2538,7 @@
         cancelForegroundNotificationLocked(r);
         if (r.isForeground) {
             decActiveForegroundAppLocked(smap, r);
-            StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.userId, r.shortName,
+            StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
                     StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c958b67..f230b12 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4090,10 +4090,10 @@
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
             }
 
-            if (app.info.isAllowedToUseHiddenApi()) {
-                // This app is allowed to use undocumented and private APIs. Set
-                // up its runtime with the appropriate flag.
-                runtimeFlags |= Zygote.DISABLE_HIDDEN_API_CHECKS;
+            if (!app.info.isAllowedToUseHiddenApi()) {
+                // This app is not allowed to use undocumented and private APIs.
+                // Set up its runtime with the appropriate flag.
+                runtimeFlags |= Zygote.ENABLE_HIDDEN_API_CHECKS;
             }
 
             String invokeWith = null;
@@ -4371,7 +4371,7 @@
                 "updateUsageStats: comp=" + component + "res=" + resumed);
         final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
-            component.userId, component.realActivity.getPackageName(),
+            component.app.uid, component.realActivity.getPackageName(),
             component.realActivity.getShortClassName(), resumed ?
                         StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND :
                         StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND);
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index db4e09f..6dcf041 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -28,7 +28,9 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.os.Trace;
+import android.util.Slog;
 import android.view.IRecentsAnimationRunner;
 import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
 import com.android.server.wm.WindowManagerService;
@@ -63,6 +65,7 @@
         mHandler = new Handler(mStackSupervisor.mLooper);
         mWindowManager = wm;
         mUserController = userController;
+
         mCancelAnimationRunnable = () -> {
             // The caller has not finished the animation in a predefined amount of time, so
             // force-cancel the animation
@@ -73,13 +76,33 @@
     void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
             ComponentName recentsComponent, int recentsUid) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
+
+        if (!mWindowManager.canStartRecentsAnimation()) {
+            notifyAnimationCancelBeforeStart(recentsAnimationRunner);
+            return;
+        }
+
+        // If the existing home activity is already on top, then cancel
+        ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
+        final boolean hasExistingHomeActivity = homeActivity != null;
+        if (hasExistingHomeActivity) {
+            final ActivityDisplay display = homeActivity.getDisplay();
+            mRestoreHomeBehindStack = display.getStackAboveHome();
+            if (mRestoreHomeBehindStack == null) {
+                notifyAnimationCancelBeforeStart(recentsAnimationRunner);
+                return;
+            }
+        }
+
         mWindowManager.deferSurfaceLayout();
         try {
-            // Cancel the previous recents animation if necessary
-            mWindowManager.cancelRecentsAnimation();
 
-            final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null;
-            if (!hasExistingHomeActivity) {
+            final ActivityDisplay display;
+            if (hasExistingHomeActivity) {
+                // Move the home activity into place for the animation if it is not already top most
+                display = homeActivity.getDisplay();
+                display.moveHomeStackBehindBottomMostVisibleStack();
+            } else {
                 // No home activity
                 final ActivityOptions opts = ActivityOptions.makeBasic();
                 opts.setLaunchActivityType(ACTIVITY_TYPE_HOME);
@@ -95,25 +118,20 @@
                         .execute();
                 mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
 
+                homeActivity = mStackSupervisor.getHomeActivity();
+                display = homeActivity.getDisplay();
+
                 // TODO: Maybe wait for app to draw in this particular case?
             }
 
-            final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
-            final ActivityDisplay display = homeActivity.getDisplay();
-
-            // Save the initial position of the home activity stack to be restored to after the
-            // animation completes
-            mRestoreHomeBehindStack = hasExistingHomeActivity
-                    ? display.getStackAboveHome()
-                    : null;
-
-            // Move the home activity into place for the animation
-            display.moveHomeStackBehindBottomMostVisibleStack();
-
             // Mark the home activity as launch-behind to bump its visibility for the
             // duration of the gesture that is driven by the recents component
             homeActivity.mLaunchTaskBehind = true;
 
+            // Post a timeout for the animation. This needs to happen before initializing the
+            // recents animation on the WM side since we may decide to cancel the animation there
+            mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
+
             // Fetch all the surface controls and pass them to the client to get the animation
             // started
             mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this,
@@ -122,9 +140,6 @@
             // If we updated the launch-behind state, update the visibility of the activities after
             // we fetch the visible tasks to be controlled by the animation
             mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-
-            // Post a timeout for the animation
-            mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
         } finally {
             mWindowManager.continueSurfaceLayout();
             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -178,4 +193,15 @@
             });
         }
     }
+
+    /**
+     * Called only when the animation should be canceled prior to starting.
+     */
+    private void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
+        try {
+            recentsAnimationRunner.onAnimationCanceled();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to cancel recents animation before start", e);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e95608f..fffe7dc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -70,6 +70,7 @@
 import android.media.AudioSystem;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
+import android.media.IAudioServerStateDispatcher;
 import android.media.IAudioService;
 import android.media.IPlaybackConfigDispatcher;
 import android.media.IRecordingConfigDispatcher;
@@ -242,6 +243,7 @@
     private static final int MSG_INDICATE_SYSTEM_READY = 26;
     private static final int MSG_ACCESSORY_PLUG_MEDIA_UNMUTE = 27;
     private static final int MSG_NOTIFY_VOL_EVENT = 28;
+    private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 29;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -390,6 +392,8 @@
             case AudioSystem.AUDIO_STATUS_SERVER_DIED:
                 sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED,
                         SENDMSG_NOOP, 0, 0, null, 0);
+                sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
+                        SENDMSG_QUEUE, 0, 0, null, 0);
                 break;
             default:
                 break;
@@ -1000,6 +1004,21 @@
         onIndicateSystemReady();
         // indicate the end of reconfiguration phase to audio HAL
         AudioSystem.setParameters("restarting=false");
+
+        sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
+                SENDMSG_QUEUE, 1, 0, null, 0);
+    }
+
+    private void onDispatchAudioServerStateChange(boolean state) {
+        synchronized (mAudioServerStateListeners) {
+            for (AsdProxy asdp : mAudioServerStateListeners.values()) {
+                try {
+                    asdp.callback().dispatchAudioServerStateChange(state);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Could not call dispatchAudioServerStateChange()", e);
+                }
+            }
+        }
     }
 
     private void createAudioSystemThread() {
@@ -5089,6 +5108,10 @@
                     onAudioServerDied();
                     break;
 
+                case MSG_DISPATCH_AUDIO_SERVER_STATE:
+                    onDispatchAudioServerStateChange(msg.arg1 == 1);
+                    break;
+
                 case MSG_UNLOAD_SOUND_EFFECTS:
                     onUnloadSoundEffects();
                     break;
@@ -7341,6 +7364,77 @@
 
 
     //======================
+    // Audioserver state displatch
+    //======================
+    private class AsdProxy implements IBinder.DeathRecipient {
+        private final IAudioServerStateDispatcher mAsd;
+
+        AsdProxy(IAudioServerStateDispatcher asd) {
+            mAsd = asd;
+        }
+
+        public void binderDied() {
+            synchronized (mAudioServerStateListeners) {
+                mAudioServerStateListeners.remove(mAsd.asBinder());
+            }
+        }
+
+        IAudioServerStateDispatcher callback() {
+            return mAsd;
+        }
+    }
+
+    private HashMap<IBinder, AsdProxy> mAudioServerStateListeners =
+            new HashMap<IBinder, AsdProxy>();
+
+    private void checkMonitorAudioServerStatePermission() {
+        if (!(mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_PHONE_STATE) ==
+                PackageManager.PERMISSION_GRANTED ||
+              mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_AUDIO_ROUTING) ==
+                PackageManager.PERMISSION_GRANTED)) {
+            throw new SecurityException("Not allowed to monitor audioserver state");
+        }
+    }
+
+    public void registerAudioServerStateDispatcher(IAudioServerStateDispatcher asd) {
+        checkMonitorAudioServerStatePermission();
+        synchronized (mAudioServerStateListeners) {
+            if (mAudioServerStateListeners.containsKey(asd.asBinder())) {
+                Slog.w(TAG, "Cannot re-register audio server state dispatcher");
+                return;
+            }
+            AsdProxy asdp = new AsdProxy(asd);
+            try {
+                asd.asBinder().linkToDeath(asdp, 0/*flags*/);
+            } catch (RemoteException e) {
+
+            }
+            mAudioServerStateListeners.put(asd.asBinder(), asdp);
+        }
+    }
+
+    public void unregisterAudioServerStateDispatcher(IAudioServerStateDispatcher asd) {
+        checkMonitorAudioServerStatePermission();
+        synchronized (mAudioServerStateListeners) {
+            AsdProxy asdp = mAudioServerStateListeners.remove(asd.asBinder());
+            if (asdp == null) {
+                Slog.w(TAG, "Trying to unregister unknown audioserver state dispatcher for pid "
+                        + Binder.getCallingPid() + " / uid " + Binder.getCallingUid());
+                return;
+            } else {
+                asd.asBinder().unlinkToDeath(asdp, 0/*flags*/);
+            }
+        }
+    }
+
+    public boolean isAudioServerRunning() {
+        checkMonitorAudioServerStatePermission();
+        return (AudioSystem.checkAudioFlinger() == AudioSystem.AUDIO_STATUS_OK);
+    }
+
+    //======================
     // misc
     //======================
     private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies =
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b877184..21d86c4 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -29,6 +29,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
@@ -76,6 +77,7 @@
 import android.view.KeyEvent;
 import android.view.ViewConfiguration;
 
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemService;
 import com.android.server.Watchdog;
@@ -180,9 +182,8 @@
 
         updateUser();
 
+        registerPackageBroadcastReceivers();
         // TODO(jaewan): Query per users
-        // TODO(jaewan): Add listener to know changes in list of services.
-        //               Refer TvInputManagerService.registerBroadcastReceivers()
         buildMediaSessionService2List();
     }
 
@@ -437,12 +438,74 @@
         mHandler.postSessionsChanged(session.getUserId());
     }
 
+    private void registerPackageBroadcastReceivers() {
+        // TODO(jaewan): Only consider changed packages when building session service list
+        //               when we make this multi-user aware. At that time,
+        //               use PackageMonitor.getChangingUserId() to know which user has changed.
+        IntentFilter filter = new IntentFilter();
+        filter.addDataScheme("package");
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
+        filter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
+        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+
+        getContext().registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final int changeUserId = intent.getIntExtra(
+                        Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                if (changeUserId == UserHandle.USER_NULL) {
+                    Log.w(TAG, "Intent broadcast does not contain user handle: "+ intent);
+                    return;
+                }
+                // Check if the package is replacing (i.e. reinstalling)
+                final boolean isReplacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                // TODO(jaewan): Add multi-user support with this.
+                // final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+
+                if (DEBUG) {
+                    Log.d(TAG, "Received change in packages, intent=" + intent);
+                }
+                switch (intent.getAction()) {
+                    case Intent.ACTION_PACKAGE_ADDED:
+                    case Intent.ACTION_PACKAGE_REMOVED:
+                    case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+                    case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+                        if (isReplacing) {
+                            // Ignore if the package(s) are replacing. In that case, followings will
+                            // happen in order.
+                            //    1. ACTION_PACKAGE_REMOVED with isReplacing=true
+                            //    2. ACTION_PACKAGE_ADDED with isReplacing=true
+                            //    3. ACTION_PACKAGE_REPLACED
+                            //    (Note that ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE and
+                            //     ACTION_EXTERNAL_APPLICATIONS_AVAILABLE will be also called with
+                            //     isReplacing=true for both ASEC hosted packages and packages in
+                            //     external storage)
+                            // Since we only want to update session service list once, ignore
+                            // actions above when replacing.
+                            // Replacing will be handled only once with the ACTION_PACKAGE_REPLACED.
+                            break;
+                        }
+                        // pass-through
+                    case Intent.ACTION_PACKAGE_CHANGED:
+                    case Intent.ACTION_PACKAGES_SUSPENDED:
+                    case Intent.ACTION_PACKAGES_UNSUSPENDED:
+                    case Intent.ACTION_PACKAGE_REPLACED:
+                        buildMediaSessionService2List();
+                }
+            }
+        }, UserHandle.ALL, filter, null, BackgroundThread.getHandler());
+    }
+
     private void buildMediaSessionService2List() {
         if (DEBUG) {
             Log.d(TAG, "buildMediaSessionService2List");
         }
-
-        // TODO(jaewan): Query per users.
+        // TODO(jaewan): Also query for managed profile users.
         // TODO(jaewan): Similar codes are also at the updatable. Can't we share codes?
         PackageManager manager = getContext().getPackageManager();
         List<ResolveInfo> services = new ArrayList<>();
@@ -458,9 +521,13 @@
             services.addAll(sessionServices);
         }
         synchronized (mLock) {
-            mSessions.clear();
-            if (services == null) {
-                return;
+            // List to keep the session services that need be removed because they don't exist
+            // in the 'services' above.
+            List<MediaSession2Record> removeCandidates = new ArrayList<>();
+            for (int i = 0; i < mSessions.size(); i++) {
+                if (mSessions.get(i).getToken().getType() != TYPE_SESSION) {
+                    removeCandidates.add(mSessions.get(i));
+                }
             }
             for (int i = 0; i < services.size(); i++) {
                 if (services.get(i) == null || services.get(i).serviceInfo == null) {
@@ -475,17 +542,35 @@
                 } catch (NameNotFoundException e) {
                     continue;
                 }
-
+                SessionToken2 token;
                 try {
-                    SessionToken2 token = new SessionToken2(getContext(),
+                    token = new SessionToken2(getContext(),
                             serviceInfo.packageName, serviceInfo.name, uid);
+                } catch (IllegalArgumentException e) {
+                    Log.w(TAG, "Invalid session service", e);
+                    continue;
+                }
+                boolean found = false;
+                for (int j = 0; j < mSessions.size(); j++) {
+                    if (token.equals(mSessions.get(j).getToken())) {
+                        // If the token already exists, keep it in the mSessions.
+                        removeCandidates.remove(mSessions.get(j));
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    // New session service is found.
                     MediaSession2Record record = new MediaSession2Record(getContext(),
                             token, mSessionDestroyedListener);
                     mSessions.add(record);
-                } catch (IllegalArgumentException e) {
-                    Log.d(TAG, "Invalid session service", e);
                 }
             }
+            for (int i = 0; i < removeCandidates.size(); i++) {
+                removeCandidates.get(i).onSessionDestroyed();
+                mSessions.remove(removeCandidates.get(i));
+            }
+            removeCandidates.clear();
         }
         if (DEBUG) {
             Log.d(TAG, "Found " + mSessions.size() + " session services");
@@ -1503,12 +1588,14 @@
             return tokens;
         }
 
+        // TODO(jaewan): Protect this API with permission
         @Override
         public void addSessionTokensListener(ISessionTokensListener listener, int userId,
                 String packageName) {
             // TODO(jaewan): Implement.
         }
 
+        // TODO(jaewan): Protect this API with permission
         @Override
         public void removeSessionTokensListener(ISessionTokensListener listener) {
             // TODO(jaewan): Implement
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 7e3b551..44b83d8 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -18,8 +18,10 @@
 
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
+import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -44,6 +46,7 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.ConditionProviderService;
@@ -61,6 +64,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
 import com.android.server.LocalServices;
 
 import libcore.io.IoUtils;
@@ -89,6 +94,7 @@
     private final H mHandler;
     private final SettingsObserver mSettingsObserver;
     @VisibleForTesting protected final AppOpsManager mAppOps;
+    @VisibleForTesting protected final NotificationManager mNotificationManager;
     protected ZenModeConfig mDefaultConfig;
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final ZenModeFiltering mFiltering;
@@ -112,12 +118,14 @@
 
     protected String mDefaultRuleEveryNightName;
     protected String mDefaultRuleEventsName;
+    @VisibleForTesting protected boolean mIsBootComplete;
 
     public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
         mContext = context;
         mHandler = new H(looper);
         addCallback(mMetrics);
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mNotificationManager =  context.getSystemService(NotificationManager.class);
 
         mDefaultConfig = new ZenModeConfig();
         setDefaultZenRules(mContext);
@@ -197,6 +205,8 @@
         mHandler.postMetricsTimer();
         cleanUpZenRules();
         evaluateZenMode("onSystemReady", true);
+        mIsBootComplete = true;
+        showZenUpgradeNotification(mZenMode);
     }
 
     public void onUserSwitched(int user) {
@@ -612,6 +622,10 @@
             throws XmlPullParserException, IOException {
         final ZenModeConfig config = ZenModeConfig.readXml(parser);
         if (config != null) {
+            if (config.version < ZenModeConfig.XML_VERSION) {
+                Settings.Global.putInt(mContext.getContentResolver(),
+                        Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
+            }
             if (forRestore) {
                 //TODO: http://b/22388012
                 if (config.user != UserHandle.USER_SYSTEM) {
@@ -755,8 +769,10 @@
         return Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, Global.ZEN_MODE_OFF);
     }
 
-    private void setZenModeSetting(int zen) {
+    @VisibleForTesting
+    protected void setZenModeSetting(int zen) {
         Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zen);
+        showZenUpgradeNotification(zen);
     }
 
     private int getPreviousRingerModeSetting() {
@@ -1139,6 +1155,41 @@
         }
     }
 
+    private void showZenUpgradeNotification(int zen) {
+        final boolean showNotification = mIsBootComplete
+                && zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                && Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
+
+        if (showNotification) {
+            mNotificationManager.notify(TAG, SystemMessage.NOTE_ZEN_UPGRADE,
+                    createZenUpgradeNotification());
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+        }
+    }
+
+    @VisibleForTesting
+    protected Notification createZenUpgradeNotification() {
+        Intent intent = new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS)
+                .setPackage("com.android.settings")
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final Bundle extras = new Bundle();
+        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+                mContext.getResources().getString(R.string.global_action_settings));
+        return new Notification.Builder(mContext, SystemNotificationChannels.SYSTEM_CHANGES)
+                .setSmallIcon(R.drawable.ic_settings)
+                .setContentTitle(mContext.getResources().getString(
+                        R.string.zen_upgrade_notification_title))
+                .setContentText(mContext.getResources().getString(
+                        R.string.zen_upgrade_notification_content))
+                .setAutoCancel(true)
+                .setLocalOnly(true)
+                .addExtras(extras)
+                .setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0, null))
+                .build();
+    }
+
     private final class Metrics extends Callback {
         private static final String COUNTER_PREFIX = "dnd_mode_";
         private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b79caca..1746dd1 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -63,9 +63,8 @@
     public static final int DEXOPT_STORAGE_DE     = 1 << 8;
     /** Indicates that dexopt is invoked from the background service. */
     public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
-    /* Indicates that dexopt should not restrict access to private APIs.
-     * Must be kept in sync with com.android.internal.os.ZygoteInit. */
-    public static final int DEXOPT_DISABLE_HIDDEN_API_CHECKS = 1 << 10;
+    /** Indicates that dexopt should restrict access to private APIs. */
+    public static final int DEXOPT_ENABLE_HIDDEN_API_CHECKS = 1 << 10;
 
     // NOTE: keep in sync with installd
     public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 2c68e67..458d725 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -57,7 +57,7 @@
 import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
 import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
 import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
-import static com.android.server.pm.Installer.DEXOPT_DISABLE_HIDDEN_API_CHECKS;
+import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 
@@ -528,11 +528,9 @@
         boolean isPublic = !info.isForwardLocked() &&
                 (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
-        // System apps are invoked with a runtime flag which exempts them from
-        // restrictions on hidden API usage. We dexopt with the same runtime flag
-        // otherwise offending methods would have to be re-verified at runtime
-        // and we want to avoid the performance overhead of that.
-        int hiddenApiFlag = info.isAllowedToUseHiddenApi() ? DEXOPT_DISABLE_HIDDEN_API_CHECKS : 0;
+        // Some apps are executed with restrictions on hidden API usage. If this app is one
+        // of them, pass a flag to dexopt to enable the same restrictions during compilation.
+        int hiddenApiFlag = info.isAllowedToUseHiddenApi() ? 0 : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
         int dexFlags =
                 (isPublic ? DEXOPT_PUBLIC : 0)
                 | (debuggable ? DEXOPT_DEBUGGABLE : 0)
@@ -655,8 +653,8 @@
         if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) {
             flagsList.add("idle_background_job");
         }
-        if ((flags & DEXOPT_DISABLE_HIDDEN_API_CHECKS) == DEXOPT_DISABLE_HIDDEN_API_CHECKS) {
-            flagsList.add("disable_hidden_api_checks");
+        if ((flags & DEXOPT_ENABLE_HIDDEN_API_CHECKS) == DEXOPT_ENABLE_HIDDEN_API_CHECKS) {
+            flagsList.add("enable_hidden_api_checks");
         }
 
         return String.join(",", flagsList);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e869f58..6c3beaf 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -59,6 +59,7 @@
     private final IRecentsAnimationRunner mRunner;
     private final RecentsAnimationCallbacks mCallbacks;
     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
+    private final int mDisplayId;
 
     // The recents component app token that is shown behind the visibile tasks
     private AppWindowToken mHomeAppToken;
@@ -159,8 +160,6 @@
     };
 
     /**
-     * Initializes a new RecentsAnimationController.
-     *
      * @param remoteAnimationRunner The remote runner which should be notified when the animation is
      *                              ready to start or has been canceled
      * @param callbacks Callbacks to be made when the animation finishes
@@ -171,16 +170,19 @@
         mService = service;
         mRunner = remoteAnimationRunner;
         mCallbacks = callbacks;
+        mDisplayId = displayId;
+    }
 
-        final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
-        final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
-        if (visibleTasks.isEmpty()) {
-            cancelAnimation();
-            return;
-        }
-
+    /**
+     * Initializes the recents animation controller. This is a separate call from the constructor
+     * because it may call cancelAnimation() which needs to properly clean up the controller
+     * in the window manager.
+     */
+    public void initialize() {
         // Make leashes for each of the visible tasks and add it to the recents animation to be
         // started
+        final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+        final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
         final int taskCount = visibleTasks.size();
         for (int i = 0; i < taskCount; i++) {
             final Task task = visibleTasks.get(i);
@@ -193,6 +195,12 @@
             addAnimation(task);
         }
 
+        // Skip the animation if there is nothing to animate
+        if (mPendingAnimations.isEmpty()) {
+            cancelAnimation();
+            return;
+        }
+
         // Adjust the wallpaper visibility for the showing home activity
         final AppWindowToken recentsComponentAppToken =
                 dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
@@ -222,8 +230,10 @@
     }
 
     void startAnimation() {
-        if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart);
-        if (!mPendingStart) {
+        if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
+                + " mCanceled=" + mCanceled);
+        if (!mPendingStart || mCanceled) {
+            // Skip starting if we've already started or canceled the animation
             return;
         }
         try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c2ed2ae..e68fbdb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2680,6 +2680,7 @@
             cancelRecentsAnimation();
             mRecentsAnimationController = new RecentsAnimationController(this,
                     recentsAnimationRunner, callbacks, displayId);
+            mRecentsAnimationController.initialize();
         }
     }
 
@@ -2687,6 +2688,19 @@
         return mRecentsAnimationController;
     }
 
+    /**
+     * @return Whether the next recents animation can continue to start. Called from
+     *         {@link RecentsAnimation#startRecentsActivity}.
+     */
+    public boolean canStartRecentsAnimation() {
+        synchronized (mWindowMap) {
+            if (mAppTransition.isTransitionSet()) {
+                return false;
+            }
+            return true;
+        }
+    }
+
     public void cancelRecentsAnimation() {
         synchronized (mWindowMap) {
             if (mRecentsAnimationController != null) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
index 98c428d..091d9bd 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
@@ -45,6 +45,7 @@
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Unit tests for {@link android.app.admin.SystemUpdatePolicy}.
@@ -253,6 +254,147 @@
 
     }
 
+    @Test
+    public void testInstallationOptionWithoutFreeze() {
+        // Also duplicated at com.google.android.gts.deviceowner.SystemUpdatePolicyTest
+        final long millis_2018_01_01 = TimeUnit.SECONDS.toMillis(1514764800);
+
+        SystemUpdatePolicy p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+        assertInstallationOption(SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, Long.MAX_VALUE,
+                millis_2018_01_01, p);
+
+        p = SystemUpdatePolicy.createPostponeInstallPolicy();
+        assertInstallationOption(SystemUpdatePolicy.TYPE_POSTPONE, Long.MAX_VALUE,
+                millis_2018_01_01, p);
+
+        p = SystemUpdatePolicy.createWindowedInstallPolicy(120, 180); // 2:00 - 3:00
+        // 00:00 is two hours before the next window
+        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(2),
+                millis_2018_01_01, p);
+        // 02:00 is within the current maintenance window, and one hour until the window ends
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(1),
+                millis_2018_01_01 + TimeUnit.HOURS.toMillis(2), p);
+        // 04:00 is 22 hours from the window next day
+        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(22),
+                millis_2018_01_01 + TimeUnit.HOURS.toMillis(4), p);
+
+        p = SystemUpdatePolicy.createWindowedInstallPolicy(22 * 60, 2 * 60); // 22:00 - 2:00
+        // 21:00 is one hour from the next window
+        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(1),
+                millis_2018_01_01 + TimeUnit.HOURS.toMillis(21), p);
+        // 00:00 is two hours from the end of current window
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(2),
+                millis_2018_01_01, p);
+        // 03:00 is 22 hours from the window today
+        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(19),
+                millis_2018_01_01 + TimeUnit.HOURS.toMillis(3), p);
+    }
+
+    @Test
+    public void testInstallationOptionWithFreeze() throws Exception {
+        final long millis_2016_02_29 = TimeUnit.SECONDS.toMillis(1456704000);
+        final long millis_2017_01_31 = TimeUnit.SECONDS.toMillis(1485820800);
+        final long millis_2017_02_28 = TimeUnit.SECONDS.toMillis(1488240000);
+        final long millis_2018_01_01 = TimeUnit.SECONDS.toMillis(1514764800);
+        final long millis_2018_08_01 = TimeUnit.SECONDS.toMillis(1533081600);
+
+        SystemUpdatePolicy p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+        setFreezePeriods(p, "01-01", "01-31");
+        // Inside a freeze period
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(31),
+                millis_2018_01_01, p);
+        // Device is outside freeze between 2/28 to 12/31 inclusive
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(307),
+                millis_2017_02_28, p);
+
+        // Freeze period contains leap day Feb 29
+        p = SystemUpdatePolicy.createPostponeInstallPolicy();
+        setFreezePeriods(p, "02-01", "03-15");
+        // Freezed until 3/31, note 2016 is a leap year
+        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(16),
+                millis_2016_02_29, p);
+        // Freezed until 3/31, note 2017 is not a leap year
+        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(16),
+                millis_2017_02_28, p);
+        // Next freeze is 2018/2/1
+        assertInstallationOption(SystemUpdatePolicy.TYPE_POSTPONE, TimeUnit.DAYS.toMillis(31),
+                millis_2018_01_01, p);
+
+        // Freeze period start on or right after leap day
+        p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+        setFreezePeriods(p, "03-01", "03-31");
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(1),
+                millis_2016_02_29, p);
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(1),
+                millis_2017_02_28, p);
+        setFreezePeriods(p, "02-28", "03-15");
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(16),
+                millis_2016_02_29, p);
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(16),
+                millis_2017_02_28, p);
+
+        // Freeze period end on or right after leap day
+        p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+        setFreezePeriods(p, "02-01", "02-28");
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(1),
+                millis_2016_02_29, p);
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(1),
+                millis_2017_02_28, p);
+        p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+        setFreezePeriods(p, "02-01", "03-01");
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(2),
+                millis_2016_02_29, p);
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(2),
+                millis_2017_02_28, p);
+
+        // Freeze period with maintenance window
+        p = SystemUpdatePolicy.createWindowedInstallPolicy(23 * 60, 1 * 60); // 23:00 - 1:00
+        setFreezePeriods(p, "02-01", "02-28");
+        // 00:00 is within the current window, outside freeze period
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(1),
+                millis_2018_01_01, p);
+        // Last day of feeze period, which ends in 22 hours
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(22),
+                millis_2017_02_28 + TimeUnit.HOURS.toMillis(2), p);
+        // Last day before the next freeze, and within window
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(1),
+                millis_2017_01_31, p);
+        // Last day before the next freeze, and there is still a partial maintenance window before
+        // the freeze.
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(19),
+                millis_2017_01_31 + TimeUnit.HOURS.toMillis(4), p);
+
+        // Two freeze periods
+        p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+        setFreezePeriods(p, "05-01", "06-01", "12-01", "01-31");
+        // automatic policy for August, September, November and December
+        assertInstallationOption(
+                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(122),
+                millis_2018_08_01, p);
+    }
+
+    private void assertInstallationOption(int expectedType, long expectedTime, long now,
+            SystemUpdatePolicy p) {
+        assertEquals(expectedType, p.getInstallationOptionAt(now).getType());
+        assertEquals(expectedTime, p.getInstallationOptionAt(now).getEffectiveTime());
+    }
+
     private void testFreezePeriodsSucceeds(String...dates) throws Exception {
         SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
         setFreezePeriods(p, dates);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index c532a8a..6144c51 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -17,21 +17,32 @@
 package com.android.server.notification;
 
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
 import android.media.AudioAttributes;
 import android.provider.Settings;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Before;
@@ -46,15 +57,24 @@
 public class ZenModeHelperTest extends UiServiceTestCase {
 
     @Mock ConditionProviders mConditionProviders;
+    @Mock NotificationManager mNotificationManager;
+    @Mock private Resources mResources;
     private TestableLooper mTestableLooper;
     private ZenModeHelper mZenModeHelperSpy;
+    private Context mContext;
+    private ContentResolver mContentResolver;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
         mTestableLooper = TestableLooper.get(this);
-        mZenModeHelperSpy = spy(new ZenModeHelper(getContext(), mTestableLooper.getLooper(),
+        mContext = spy(getContext());
+        mContentResolver = mContext.getContentResolver();
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
+
+        mZenModeHelperSpy = spy(new ZenModeHelper(mContext, mTestableLooper.getLooper(),
                 mConditionProviders));
     }
 
@@ -194,4 +214,31 @@
             verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage);
         }
     }
+
+    @Test
+    public void testZenUpgradeNotification() {
+        // shows zen upgrade notification if stored settings says to shows, boot is completed
+        // and we're setting zen mode on
+        Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
+        mZenModeHelperSpy.mIsBootComplete = true;
+        mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        verify(mZenModeHelperSpy, times(1)).createZenUpgradeNotification();
+        verify(mNotificationManager, times(1)).notify(eq(ZenModeHelper.TAG),
+                eq(SystemMessage.NOTE_ZEN_UPGRADE), any());
+        assertEquals(0, Settings.Global.getInt(mContentResolver,
+                Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, -1));
+    }
+
+    @Test
+    public void testNoZenUpgradeNotification() {
+        // doesn't show upgrade notification if stored settings says don't show
+        Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+        mZenModeHelperSpy.mIsBootComplete = true;
+        mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        verify(mZenModeHelperSpy, never()).createZenUpgradeNotification();
+        verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG),
+                eq(SystemMessage.NOTE_ZEN_UPGRADE), any());
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 6806947..a7fc470 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1786,7 +1786,7 @@
                         mGadgetProxy = IUsbGadget.getService();
                         mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
                                 USB_GADGET_HAL_DEATH_COOKIE);
-                        if (!mCurrentFunctionsApplied) {
+                        if (!mCurrentFunctionsApplied && !mCurrentUsbFunctionsRequested) {
                             setEnabledFunctions(mCurrentFunctions, false);
                         }
                     } catch (NoSuchElementException e) {
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 5fcff18..024bd30 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -29,7 +29,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -82,7 +81,7 @@
     private int mConnectionProperties;
     private String mDisconnectMessage;
     private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
-    private long mConnectElapsedTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
+    private long mConnectionStartElapsedRealTime = CONNECT_TIME_NOT_SPECIFIED;
     private StatusHints mStatusHints;
     private Bundle mExtras;
     private Set<String> mPreviousExtraKeys;
@@ -584,30 +583,36 @@
     }
 
     /**
-     * Sets the connection start time of the {@code Conference}.  Should be specified in wall-clock
-     * time returned by {@link System#currentTimeMillis()}.
+     * Sets the connection start time of the {@code Conference}.  This is used in the call log to
+     * indicate the date and time when the conference took place.
+     * <p>
+     * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
      * <p>
      * When setting the connection time, you should always set the connection elapsed time via
-     * {@link #setConnectionElapsedTime(long)}.
+     * {@link #setConnectionStartElapsedRealTime(long)} to ensure the duration is reflected.
      *
-     * @param connectionTimeMillis The connection time, in milliseconds.
+     * @param connectionTimeMillis The connection time, in milliseconds, as returned by
+     *                             {@link System#currentTimeMillis()}.
      */
     public final void setConnectionTime(long connectionTimeMillis) {
         mConnectTimeMillis = connectionTimeMillis;
     }
 
     /**
-     * Sets the elapsed time since system boot when the {@link Conference} was connected.
-     * This is used to determine the duration of the {@link Conference}.
+     * Sets the start time of the {@link Conference} which is the basis for the determining the
+     * duration of the {@link Conference}.
      * <p>
-     * When setting the connection elapsed time, you should always set the connection time via
+     * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
+     * zone changes do not impact the conference duration.
+     * <p>
+     * When setting this, you should also set the connection time via
      * {@link #setConnectionTime(long)}.
      *
-     * @param connectionElapsedTime The connection time, as measured by
+     * @param connectionStartElapsedRealTime The connection time, as measured by
      * {@link SystemClock#elapsedRealtime()}.
      */
-    public final void setConnectionElapsedTime(long connectionElapsedTime) {
-        mConnectElapsedTimeMillis = connectionElapsedTime;
+    public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
+        mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
     }
 
     /**
@@ -642,8 +647,8 @@
      * @return The elapsed time at which the {@link Conference} was connected.
      * @hide
      */
-    public final long getConnectElapsedTime() {
-        return mConnectElapsedTimeMillis;
+    public final long getConnectionStartElapsedRealTime() {
+        return mConnectionStartElapsedRealTime;
     }
 
     /**
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index d8599e8..3229705 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2299,7 +2299,7 @@
      *
      * @hide
      */
-    public final void setConnectElapsedTimeMillis(long connectElapsedTimeMillis) {
+    public final void setConnectionStartElapsedRealTime(long connectElapsedTimeMillis) {
         mConnectElapsedTimeMillis = connectElapsedTimeMillis;
     }
 
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 211699e..1547857 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -21,7 +21,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -2007,7 +2006,7 @@
                             null : conference.getVideoProvider().getInterface(),
                     conference.getVideoState(),
                     conference.getConnectTimeMillis(),
-                    conference.getConnectElapsedTime(),
+                    conference.getConnectionStartElapsedRealTime(),
                     conference.getStatusHints(),
                     conference.getExtras());