Merge "Update Guardrail." into pi-dev
diff --git a/api/current.txt b/api/current.txt
index 4d6e2d6..d721936 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6261,6 +6261,7 @@
     method public void addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler);
     method public void clear() throws java.io.IOException;
     method public void clear(int) throws java.io.IOException;
+    method public void clearWallpaper();
     method public void clearWallpaperOffsets(android.os.IBinder);
     method public void forgetLoadedWallpaper();
     method public android.graphics.drawable.Drawable getBuiltInDrawable();
@@ -6287,6 +6288,7 @@
     method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
     method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
     method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean, int) throws java.io.IOException;
+    method public void setDisplayPadding(android.graphics.Rect);
     method public void setResource(int) throws java.io.IOException;
     method public int setResource(int, int) throws java.io.IOException;
     method public void setStream(java.io.InputStream) throws java.io.IOException;
@@ -7332,6 +7334,20 @@
     field public static final android.os.Parcelable.Creator<android.app.usage.ConfigurationStats> CREATOR;
   }
 
+  public final class EventStats implements android.os.Parcelable {
+    ctor public EventStats(android.app.usage.EventStats);
+    method public void add(android.app.usage.EventStats);
+    method public int describeContents();
+    method public int getCount();
+    method public int getEventType();
+    method public long getFirstTimeStamp();
+    method public long getLastTimeStamp();
+    method public long getLastTimeUsed();
+    method public long getTotalTime();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.usage.EventStats> CREATOR;
+  }
+
   public final class ExternalStorageStats implements android.os.Parcelable {
     method public int describeContents();
     method public long getAppBytes();
@@ -7437,6 +7453,8 @@
     field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
     field public static final int MOVE_TO_FOREGROUND = 1; // 0x1
     field public static final int NONE = 0; // 0x0
+    field public static final int SCREEN_INTERACTIVE = 15; // 0xf
+    field public static final int SCREEN_NON_INTERACTIVE = 16; // 0x10
     field public static final int SHORTCUT_INVOCATION = 8; // 0x8
     field public static final int STANDBY_BUCKET_CHANGED = 11; // 0xb
     field public static final int USER_INTERACTION = 7; // 0x7
@@ -7460,6 +7478,7 @@
     method public boolean isAppInactive(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
     method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
+    method public java.util.List<android.app.usage.EventStats> queryEventStats(int, long, long);
     method public android.app.usage.UsageEvents queryEvents(long, long);
     method public android.app.usage.UsageEvents queryEventsForSelf(long, long);
     method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
@@ -15786,6 +15805,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> DEPTH_DEPTH_IS_EXCLUSIVE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> DISTORTION_CORRECTION_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> EDGE_AVAILABLE_EDGE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
@@ -16022,6 +16042,9 @@
     field public static final int CONTROL_SCENE_MODE_THEATRE = 7; // 0x7
     field public static final int CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0; // 0x0
     field public static final int CONTROL_VIDEO_STABILIZATION_MODE_ON = 1; // 0x1
+    field public static final int DISTORTION_CORRECTION_MODE_FAST = 1; // 0x1
+    field public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2
+    field public static final int DISTORTION_CORRECTION_MODE_OFF = 0; // 0x0
     field public static final int EDGE_MODE_FAST = 1; // 0x1
     field public static final int EDGE_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int EDGE_MODE_OFF = 0; // 0x0
@@ -16174,6 +16197,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_SCENE_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_VIDEO_STABILIZATION_MODE;
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.CaptureRequest> CREATOR;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> DISTORTION_CORRECTION_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> EDGE_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> FLASH_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> HOT_PIXEL_MODE;
@@ -16256,6 +16280,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_POST_RAW_SENSITIVITY_BOOST;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SCENE_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_VIDEO_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> DISTORTION_CORRECTION_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EDGE_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_STATE;
@@ -38317,10 +38342,8 @@
     method public void shutdown();
   }
 
-  public static abstract class SEService.SecureElementListener extends android.os.Binder {
-    ctor public SEService.SecureElementListener();
-    method public android.os.IBinder asBinder();
-    method public void serviceConnected();
+  public static abstract interface SEService.SecureElementListener {
+    method public abstract void onServiceConnected();
   }
 
   public class Session {
@@ -38330,9 +38353,7 @@
     method public android.se.omapi.Reader getReader();
     method public boolean isClosed();
     method public android.se.omapi.Channel openBasicChannel(byte[], byte) throws java.io.IOException;
-    method public android.se.omapi.Channel openBasicChannel(byte[]) throws java.io.IOException;
     method public android.se.omapi.Channel openLogicalChannel(byte[], byte) throws java.io.IOException;
-    method public android.se.omapi.Channel openLogicalChannel(byte[]) throws java.io.IOException;
   }
 
 }
@@ -38600,6 +38621,7 @@
     method public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
+    method public boolean isTrustedUserPresenceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
     method public boolean isUserConfirmationRequired();
@@ -38618,6 +38640,7 @@
     method public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
+    method public android.security.keystore.KeyProtection.Builder setTrustedUserPresenceRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
@@ -41555,7 +41578,6 @@
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
-    field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
@@ -42356,12 +42378,11 @@
     method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
     method public boolean isConcurrentVoiceAndDataSupported();
-    method public deprecated boolean isDataEnabled();
+    method public boolean isDataEnabled();
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isNetworkRoaming();
     method public boolean isSmsCapable();
     method public deprecated boolean isTtyModeSupported();
-    method public boolean isUserMobileDataEnabled();
     method public boolean isVoiceCapable();
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
@@ -42372,13 +42393,12 @@
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
     method public void sendVisualVoicemailSms(java.lang.String, int, java.lang.String, android.app.PendingIntent);
-    method public deprecated void setDataEnabled(boolean);
+    method public void setDataEnabled(boolean);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
     method public void setNetworkSelectionModeAutomatic();
     method public boolean setNetworkSelectionModeManual(java.lang.String, boolean);
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setPreferredNetworkTypeToGlobal();
-    method public void setUserMobileDataEnabled(boolean);
     method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings);
     method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
     method public deprecated void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
diff --git a/api/system-current.txt b/api/system-current.txt
index e049f53..ced5224 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -400,10 +400,8 @@
   }
 
   public class WallpaperManager {
-    method public void clearWallpaper();
     method public void clearWallpaper(int, int);
     method public void setDisplayOffset(android.os.IBinder, int, int);
-    method public void setDisplayPadding(android.graphics.Rect);
     method public boolean setWallpaperComponent(android.content.ComponentName);
   }
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 99611f4..2095649 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -111,13 +111,14 @@
         BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67;
         BluetoothConnectionStateChanged bluetooth_connection_state_changed = 68;
         BluetoothA2dpAudioStateChanged bluetooth_a2dp_audio_state_changed = 69;
-        UsbConnectorStateChanged usb_connector_changed = 70;
+        UsbConnectorStateChanged usb_connector_state_changed = 70;
         SpeakerImpedanceReported speaker_impedance_reported = 71;
         HardwareFailed hardware_failed = 72;
         PhysicalDropDetected physical_drop_detected = 73;
         ChargeCyclesReported charge_cycles_reported = 74;
         MobileConnectionStateChanged mobile_connection_state_changed = 75;
         MobileRadioTechnologyChanged mobile_radio_technology_changed = 76;
+        UsbDeviceAttached usb_device_attached = 77;
     }
 
     // Pulled events will start at field 10000.
@@ -291,7 +292,7 @@
  * Logs when a sensor state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message SensorStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -329,7 +330,7 @@
  * Logs when a sync manager sync state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message SyncStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -348,7 +349,7 @@
  * Logs when a job scheduler job state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message ScheduledJobStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -365,7 +366,7 @@
 
     // The reason a job has stopped.
     // This is only applicable when the state is FINISHED.
-    // The default value is CANCELED.
+    // The default value is STOP_REASON_UNKNOWN.
     optional android.app.job.StopReasonEnum stop_reason = 4;
 }
 
@@ -373,7 +374,7 @@
  * Logs when the audio state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message AudioStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -381,6 +382,8 @@
     enum State {
         OFF = 0;
         ON = 1;
+        // RESET indicates all audio stopped. Used when it (re)starts (e.g. after it crashes).
+        RESET = 2;
     }
     optional State state = 2;
 }
@@ -389,7 +392,7 @@
  * Logs when the video codec state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message MediaCodecActivityChanged {
     repeated AttributionNode attribution_node = 1;
@@ -397,6 +400,8 @@
     enum State {
         OFF = 0;
         ON = 1;
+        // RESET indicates all mediaCodec stopped. Used when it (re)starts (e.g. after it crashes).
+        RESET = 2;
     }
     optional State state = 2;
 }
@@ -405,7 +410,7 @@
  * Logs when the flashlight state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message FlashlightStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -413,6 +418,8 @@
     enum State {
         OFF = 0;
         ON = 1;
+        // RESET indicates all flashlight stopped. Used when it (re)starts (e.g. after it crashes).
+        RESET = 2;
     }
     optional State state = 2;
 }
@@ -421,7 +428,7 @@
  * Logs when the camera state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message CameraStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -429,6 +436,8 @@
     enum State {
         OFF = 0;
         ON = 1;
+        // RESET indicates all camera stopped. Used when it (re)starts (e.g. after it crashes).
+        RESET = 2;
     }
     optional State state = 2;
 }
@@ -966,6 +975,21 @@
     optional int32 sim_slot_index = 2;
 }
 
+/**
+ * Logs the VID and PID of any connected USB devices.
+ *
+ * Notes if any Audio, HID (input buttons/mouse/keyboard), or Storage interfaces are present.
+ *
+ * Logged by Vendor.
+ */
+message UsbDeviceAttached {
+    optional int32 vid = 1;
+    optional int32 pid = 2;
+    optional bool has_audio = 3;
+    optional bool has_hid = 4;
+    optional bool has_storage = 5;
+}
+
 
 /**
  * Logs when Bluetooth is enabled and disabled.
@@ -1103,6 +1127,8 @@
     optional int32 confidence_pctg = 1;
     // Peak acceleration of the drop, in 1/1000s of a g.
     optional int32 accel_peak_thousandths_g = 2;
+    // Duration of freefall in ms
+    optional int32 freefall_time_millis = 3;
 }
 
 /**
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 19240e2..a6e42cf 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -57,10 +57,7 @@
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.service.wallpaper.WallpaperService;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -915,9 +912,14 @@
     /**
      * Get the primary colors of a wallpaper.
      *
-     * <p>You can expect null if:
-     * • Colors are still being processed by the system.
-     * • A live wallpaper doesn't implement {@link WallpaperService.Engine#onComputeColors()}.
+     * <p>This method can return {@code null} when:
+     * <ul>
+     * <li>Colors are still being processed by the system.</li>
+     * <li>The user has chosen to use a live wallpaper:  live wallpapers might not
+     * implement
+     * {@link android.service.wallpaper.WallpaperService.Engine#onComputeColors()
+     *     WallpaperService.Engine#onComputeColors()}.</li>
+     * </ul>
      *
      * @param which Wallpaper type. Must be either {@link #FLAG_SYSTEM} or
      *     {@link #FLAG_LOCK}.
@@ -929,7 +931,7 @@
     }
 
     /**
-     * Get the primary colors of a wallpaper
+     * Get the primary colors of the wallpaper configured in the given user.
      * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or
      *     {@link #FLAG_LOCK}
      * @param userId Owner of the wallpaper.
@@ -1559,11 +1561,13 @@
      * Specify extra padding that the wallpaper should have outside of the display.
      * That is, the given padding supplies additional pixels the wallpaper should extend
      * outside of the display itself.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#SET_WALLPAPER_HINTS}.
+     *
      * @param padding The number of pixels the wallpaper should extend beyond the display,
      * on its left, top, right, and bottom sides.
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_HINTS)
     public void setDisplayPadding(Rect padding) {
         try {
@@ -1600,11 +1604,11 @@
     }
 
     /**
-     * Clear the wallpaper.
+     * Reset all wallpaper to the factory default.
      *
-     * @hide
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#SET_WALLPAPER}.
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
     public void clearWallpaper() {
         clearWallpaper(FLAG_LOCK, mContext.getUserId());
diff --git a/core/java/android/app/usage/EventStats.java b/core/java/android/app/usage/EventStats.java
new file mode 100644
index 0000000..b799de9
--- /dev/null
+++ b/core/java/android/app/usage/EventStats.java
@@ -0,0 +1,182 @@
+/**
+ * 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.app.usage;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains usage statistics for an event type for a specific
+ * time range.
+ */
+public final class EventStats implements Parcelable {
+
+    /**
+     * {@hide}
+     */
+    public int mEventType;
+
+    /**
+     * {@hide}
+     */
+    public long mBeginTimeStamp;
+
+    /**
+     * {@hide}
+     */
+    public long mEndTimeStamp;
+
+    /**
+     * Last time used by the user with an explicit action (notification, activity launch).
+     * {@hide}
+     */
+    public long mLastTimeUsed;
+
+    /**
+     * {@hide}
+     */
+    public long mTotalTime;
+
+    /**
+     * {@hide}
+     */
+    public int mCount;
+
+    /**
+     * {@hide}
+     */
+    public EventStats() {
+    }
+
+    public EventStats(EventStats stats) {
+        mEventType = stats.mEventType;
+        mBeginTimeStamp = stats.mBeginTimeStamp;
+        mEndTimeStamp = stats.mEndTimeStamp;
+        mLastTimeUsed = stats.mLastTimeUsed;
+        mTotalTime = stats.mTotalTime;
+        mCount = stats.mCount;
+    }
+
+    /**
+     * Return the type of event this is usage for.  May be one of the event
+     * constants in {@link UsageEvents.Event}.
+     */
+    public int getEventType() {
+        return mEventType;
+    }
+
+    /**
+     * Get the beginning of the time range this {@link android.app.usage.EventStats} represents,
+     * measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getFirstTimeStamp() {
+        return mBeginTimeStamp;
+    }
+
+    /**
+     * Get the end of the time range this {@link android.app.usage.EventStats} represents,
+     * measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getLastTimeStamp() {
+        return mEndTimeStamp;
+    }
+
+    /**
+     * Get the last time this event was used, measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getLastTimeUsed() {
+        return mLastTimeUsed;
+    }
+
+    /**
+     * Return the number of times that this event occurred over the interval.
+     */
+    public int getCount() {
+        return mCount;
+    }
+
+    /**
+     * Get the total time this event was active, measured in milliseconds.
+     */
+    public long getTotalTime() {
+        return mTotalTime;
+    }
+
+    /**
+     * Add the statistics from the right {@link EventStats} to the left. The event type for
+     * both {@link UsageStats} objects must be the same.
+     * @param right The {@link EventStats} object to merge into this one.
+     * @throws java.lang.IllegalArgumentException if the event types of the two
+     *         {@link UsageStats} objects are different.
+     */
+    public void add(EventStats right) {
+        if (mEventType != right.mEventType) {
+            throw new IllegalArgumentException("Can't merge EventStats for event #"
+                    + mEventType + " with EventStats for event #" + right.mEventType);
+        }
+
+        // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with
+        // regards to their mEndTimeStamp.
+        if (right.mBeginTimeStamp > mBeginTimeStamp) {
+            mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed);
+        }
+        mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
+        mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
+        mTotalTime += right.mTotalTime;
+        mCount += right.mCount;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mEventType);
+        dest.writeLong(mBeginTimeStamp);
+        dest.writeLong(mEndTimeStamp);
+        dest.writeLong(mLastTimeUsed);
+        dest.writeLong(mTotalTime);
+        dest.writeInt(mCount);
+    }
+
+    public static final Creator<EventStats> CREATOR = new Creator<EventStats>() {
+        @Override
+        public EventStats createFromParcel(Parcel in) {
+            EventStats stats = new EventStats();
+            stats.mEventType = in.readInt();
+            stats.mBeginTimeStamp = in.readLong();
+            stats.mEndTimeStamp = in.readLong();
+            stats.mLastTimeUsed = in.readLong();
+            stats.mTotalTime = in.readLong();
+            stats.mCount = in.readInt();
+            return stats;
+        }
+
+        @Override
+        public EventStats[] newArray(int size) {
+            return new EventStats[size];
+        }
+    };
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index d52bd37..00d8711 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -32,6 +32,8 @@
             String callingPackage);
     ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
             String callingPackage);
+    ParceledListSlice queryEventStats(int bucketType, long beginTime, long endTime,
+            String callingPackage);
     UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
     UsageEvents queryEventsForPackage(long beginTime, long endTime, String callingPackage);
     void setAppInactive(String packageName, boolean inactive, int userId);
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index b354e81..a665652 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -139,6 +139,19 @@
         @SystemApi
         public static final int SLICE_PINNED = 14;
 
+        /**
+         * An event type denoting that the screen has gone in to an interactive state (turned
+         * on for full user interaction, not ambient display or other non-interactive state).
+         */
+        public static final int SCREEN_INTERACTIVE = 15;
+
+        /**
+         * An event type denoting that the screen has gone in to a non-interactive state
+         * (completely turned off or turned on only in a non-interactive state like ambient
+         * display).
+         */
+        public static final int SCREEN_NON_INTERACTIVE = 16;
+
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
 
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 4744147..6feb527 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -301,6 +301,44 @@
     }
 
     /**
+     * Gets aggregated event stats for the given time range, aggregated by the specified interval.
+     * <p>The returned list will contain a {@link EventStats} object for each event type that
+     * is being aggregated and has data for an interval that is a subset of the time range given.
+     *
+     * <p>The current event types that will be aggregated here are:</p>
+     * <ul>
+     *     <li>{@link UsageEvents.Event#SCREEN_INTERACTIVE}</li>
+     *     <li>{@link UsageEvents.Event#SCREEN_NON_INTERACTIVE}</li>
+     * </ul>
+     *
+     * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
+     *
+     * @param intervalType The time interval by which the stats are aggregated.
+     * @param beginTime The inclusive beginning of the range of stats to include in the results.
+     * @param endTime The exclusive end of the range of stats to include in the results.
+     * @return A list of {@link EventStats}
+     *
+     * @see #INTERVAL_DAILY
+     * @see #INTERVAL_WEEKLY
+     * @see #INTERVAL_MONTHLY
+     * @see #INTERVAL_YEARLY
+     * @see #INTERVAL_BEST
+     */
+    public List<EventStats> queryEventStats(int intervalType, long beginTime, long endTime) {
+        try {
+            @SuppressWarnings("unchecked")
+            ParceledListSlice<EventStats> slice = mService.queryEventStats(intervalType, beginTime,
+                    endTime, mContext.getOpPackageName());
+            if (slice != null) {
+                return slice.getList();
+            }
+        } catch (RemoteException e) {
+            // fallthrough and return the empty list.
+        }
+        return Collections.emptyList();
+    }
+
+    /**
      * Query for events in the given time range. Events are only kept by the system for a few
      * days.
      * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 390b83f..e6aaab1 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -3392,6 +3392,21 @@
     public static final Key<Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE =
             new Key<Integer>("android.logicalMultiCamera.sensorSyncType", int.class);
 
+    /**
+     * <p>List of distortion correction modes for {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} that are
+     * supported by this camera device.</p>
+     * <p>No device is required to support this API; such devices will always list only 'OFF'.
+     * All devices that support this API will list both FAST and HIGH_QUALITY.</p>
+     * <p><b>Range of valid values:</b><br>
+     * Any value listed in {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    @PublicKey
+    public static final Key<int[]> DISTORTION_CORRECTION_AVAILABLE_MODES =
+            new Key<int[]>("android.distortionCorrection.availableModes", int[].class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 7669c01..7467c3a 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2730,6 +2730,31 @@
     public static final int TONEMAP_PRESET_CURVE_REC709 = 1;
 
     //
+    // Enumeration values for CaptureRequest#DISTORTION_CORRECTION_MODE
+    //
+
+    /**
+     * <p>No distortion correction is applied.</p>
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    public static final int DISTORTION_CORRECTION_MODE_OFF = 0;
+
+    /**
+     * <p>Lens distortion correction is applied without reducing frame rate
+     * relative to sensor output. It may be the same as OFF if distortion correction would
+     * reduce frame rate relative to sensor.</p>
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    public static final int DISTORTION_CORRECTION_MODE_FAST = 1;
+
+    /**
+     * <p>High-quality distortion correction is applied, at the cost of
+     * possibly reduced frame rate relative to sensor output.</p>
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2;
+
+    //
     // Enumeration values for CaptureResult#CONTROL_AE_STATE
     //
 
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index b0cbec7..d36785a 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -3167,6 +3167,49 @@
     public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
             new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
 
+    /**
+     * <p>Mode of operation for the lens distortion correction block.</p>
+     * <p>The lens distortion correction block attempts to improve image quality by fixing
+     * radial, tangential, or other geometric aberrations in the camera device's optics.  If
+     * available, the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} field documents the lens's distortion parameters.</p>
+     * <p>OFF means no distortion correction is done.</p>
+     * <p>FAST/HIGH_QUALITY both mean camera device determined distortion correction will be
+     * applied. HIGH_QUALITY mode indicates that the camera device will use the highest-quality
+     * correction algorithms, even if it slows down capture rate. FAST means the camera device
+     * will not slow down capture rate when applying correction. FAST may be the same as OFF if
+     * any correction at all would slow down capture rate.  Every output stream will have a
+     * similar amount of enhancement applied.</p>
+     * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
+     * applied to any RAW output.  Metadata coordinates such as face rectangles or metering
+     * regions are also not affected by correction.</p>
+     * <p>Applications enabling distortion correction need to pay extra attention when converting
+     * image coordinates between corrected output buffers and the sensor array. For example, if
+     * the app supports tap-to-focus and enables correction, it then has to apply the distortion
+     * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
+     * calculate the tap position on the sensor active array to be used with
+     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
+     * they need to be drawn on top of the corrected output buffers.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_FAST FAST}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
+     * @see CameraCharacteristics#LENS_DISTORTION
+     * @see #DISTORTION_CORRECTION_MODE_OFF
+     * @see #DISTORTION_CORRECTION_MODE_FAST
+     * @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
+     */
+    @PublicKey
+    public static final Key<Integer> DISTORTION_CORRECTION_MODE =
+            new Key<Integer>("android.distortionCorrection.mode", int.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 6331942..bb82260 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -4450,6 +4450,49 @@
     public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
             new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
 
+    /**
+     * <p>Mode of operation for the lens distortion correction block.</p>
+     * <p>The lens distortion correction block attempts to improve image quality by fixing
+     * radial, tangential, or other geometric aberrations in the camera device's optics.  If
+     * available, the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} field documents the lens's distortion parameters.</p>
+     * <p>OFF means no distortion correction is done.</p>
+     * <p>FAST/HIGH_QUALITY both mean camera device determined distortion correction will be
+     * applied. HIGH_QUALITY mode indicates that the camera device will use the highest-quality
+     * correction algorithms, even if it slows down capture rate. FAST means the camera device
+     * will not slow down capture rate when applying correction. FAST may be the same as OFF if
+     * any correction at all would slow down capture rate.  Every output stream will have a
+     * similar amount of enhancement applied.</p>
+     * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
+     * applied to any RAW output.  Metadata coordinates such as face rectangles or metering
+     * regions are also not affected by correction.</p>
+     * <p>Applications enabling distortion correction need to pay extra attention when converting
+     * image coordinates between corrected output buffers and the sensor array. For example, if
+     * the app supports tap-to-focus and enables correction, it then has to apply the distortion
+     * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
+     * calculate the tap position on the sensor active array to be used with
+     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
+     * they need to be drawn on top of the corrected output buffers.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_FAST FAST}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
+     * @see CameraCharacteristics#LENS_DISTORTION
+     * @see #DISTORTION_CORRECTION_MODE_OFF
+     * @see #DISTORTION_CORRECTION_MODE_FAST
+     * @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
+     */
+    @PublicKey
+    public static final Key<Integer> DISTORTION_CORRECTION_MODE =
+            new Key<Integer>("android.distortionCorrection.mode", int.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/fingerprint/FingerprintDialog.java b/core/java/android/hardware/fingerprint/FingerprintDialog.java
index 49835963..13b7064 100644
--- a/core/java/android/hardware/fingerprint/FingerprintDialog.java
+++ b/core/java/android/hardware/fingerprint/FingerprintDialog.java
@@ -71,7 +71,7 @@
      * after this amount of time.
      * @hide
      */
-    public static final int HIDE_DIALOG_DELAY = 3000; // ms
+    public static final int HIDE_DIALOG_DELAY = 2000; // ms
     /**
      * @hide
      */
@@ -166,7 +166,9 @@
         }
 
         /**
-         * Required: Set the text for the negative button.
+         * Required: Set the text for the negative button. This would typically be used as a
+         * "Cancel" button, but may be also used to show an alternative method for authentication,
+         * such as screen that asks for a backup password.
          * @param text
          * @return
          */
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 8048099c..a19ba19 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -1155,9 +1155,14 @@
         @Override // binder call
         public void onError(long deviceId, int error, int vendorCode) {
             if (mExecutor != null) {
-                mExecutor.execute(() -> {
-                    sendErrorResult(deviceId, error, vendorCode);
-                });
+                // FingerprintDialog case, post a delayed runnable on the FingerprintManager handler
+                // that sends the error message after FingerprintDialog.HIDE_DIALOG_DELAY to send
+                // the error to the application.
+                mHandler.postDelayed(() -> {
+                    mExecutor.execute(() -> {
+                        sendErrorResult(deviceId, error, vendorCode);
+                    });
+                }, FingerprintDialog.HIDE_DIALOG_DELAY);
             } else {
                 mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
             }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f528d63..6ebb102 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1585,6 +1585,7 @@
         public static final int STATE2_CAMERA_FLAG = 1<<21;
         public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
         public static final int STATE2_CELLULAR_HIGH_TX_POWER_FLAG = 1 << 19;
+        public static final int STATE2_USB_DATA_LINK_FLAG = 1 << 18;
 
         public static final int MOST_INTERESTING_STATES2 =
                 STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
@@ -2363,8 +2364,7 @@
                 SCREEN_BRIGHTNESS_NAMES, SCREEN_BRIGHTNESS_SHORT_NAMES),
     };
 
-    public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS
-            = new BitDescription[] {
+    public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS = new BitDescription[] {
         new BitDescription(HistoryItem.STATE2_POWER_SAVE_FLAG, "power_save", "ps"),
         new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
         new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Ww"),
@@ -2375,6 +2375,7 @@
                 new String[] { "off", "light", "full", "???" },
                 new String[] { "off", "light", "full", "???" }),
         new BitDescription(HistoryItem.STATE2_CHARGING_FLAG, "charging", "ch"),
+        new BitDescription(HistoryItem.STATE2_USB_DATA_LINK_FLAG, "usb_data", "Ud"),
         new BitDescription(HistoryItem.STATE2_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
         new BitDescription(HistoryItem.STATE2_BLUETOOTH_ON_FLAG, "bluetooth", "b"),
         new BitDescription(HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK,
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 62bb385..e3c4870 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2803,8 +2803,8 @@
                     Class<?> parcelableClass = Class.forName(name, false /* initialize */,
                             parcelableClassLoader);
                     if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
-                        throw new BadParcelableException("Parcelable protocol requires that the "
-                                + "class implements Parcelable");
+                        throw new BadParcelableException("Parcelable protocol requires subclassing "
+                                + "from Parcelable on class " + name);
                     }
                     Field f = parcelableClass.getField("CREATOR");
                     if ((f.getModifiers() & Modifier.STATIC) == 0) {
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index d3877ca..5c99f6c 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -269,22 +269,7 @@
         return (mountFlags & MOUNT_FLAG_VISIBLE) != 0;
     }
 
-    public boolean isVisibleForRead(int userId) {
-        if (type == TYPE_PUBLIC) {
-            if (isPrimary() && mountUserId != userId) {
-                // Primary physical is only visible to single user
-                return false;
-            } else {
-                return isVisible();
-            }
-        } else if (type == TYPE_EMULATED) {
-            return isVisible();
-        } else {
-            return false;
-        }
-    }
-
-    public boolean isVisibleForWrite(int userId) {
+    public boolean isVisibleForUser(int userId) {
         if (type == TYPE_PUBLIC && mountUserId == userId) {
             return isVisible();
         } else if (type == TYPE_EMULATED) {
@@ -294,6 +279,14 @@
         }
     }
 
+    public boolean isVisibleForRead(int userId) {
+        return isVisibleForUser(userId);
+    }
+
+    public boolean isVisibleForWrite(int userId) {
+        return isVisibleForUser(userId);
+    }
+
     public File getPath() {
         return (path != null) ? new File(path) : null;
     }
@@ -409,9 +402,9 @@
      * Build an intent to browse the contents of this volume. Only valid for
      * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}.
      */
-    public Intent buildBrowseIntent() {
+    public @Nullable Intent buildBrowseIntent() {
         final Uri uri;
-        if (type == VolumeInfo.TYPE_PUBLIC) {
+        if (type == VolumeInfo.TYPE_PUBLIC && mountUserId == UserHandle.myUserId()) {
             uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid);
         } else if (type == VolumeInfo.TYPE_EMULATED && isPrimary()) {
             uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9ecae8c..70f343e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7801,6 +7801,14 @@
                 "low_power_warning_acknowledged";
 
         /**
+         * 0 (default) Auto battery saver suggestion has not been suppressed. 1) it has been
+         * suppressed.
+         * @hide
+         */
+        public static final String SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION =
+                "suppress_auto_battery_saver_suggestion";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
index 65ce67f..c8efede 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -47,7 +47,8 @@
     private final SEService mService;
     private final Object mLock = new Object();
 
-    Channel(SEService service, Session session, ISecureElementChannel channel) {
+    Channel(@NonNull SEService service, @NonNull Session session,
+            @NonNull ISecureElementChannel channel) {
         if (service == null || session == null || channel == null) {
             throw new IllegalArgumentException("Parameters cannot be null");
         }
@@ -158,7 +159,7 @@
      * @throws SecurityException if the command is filtered by the security policy.
      * @throws NullPointerException if command is NULL.
      */
-    public @NonNull byte[] transmit(byte[] command) throws IOException {
+    public @NonNull byte[] transmit(@NonNull byte[] command) throws IOException {
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/core/java/android/se/omapi/ISecureElementListener.aidl
index e0c6e04..e9dd181 100644
--- a/core/java/android/se/omapi/ISecureElementListener.aidl
+++ b/core/java/android/se/omapi/ISecureElementListener.aidl
@@ -24,8 +24,4 @@
  * @hide
  */
 interface ISecureElementListener {
-  /**
-   * Called by the framework when the service is connected.
-   */
-  void serviceConnected();
 }
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 3dec976..9be3da6 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -46,7 +46,7 @@
     private final Object mLock = new Object();
 
 
-    Reader(SEService service, String name, ISecureElementReader reader) {
+    Reader(@NonNull SEService service, @NonNull String name, @NonNull ISecureElementReader reader) {
         if (reader == null || service == null || name == null) {
             throw new IllegalArgumentException("Parameters cannot be null");
         }
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index d59e86a..311dc4c 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -62,17 +62,32 @@
     /**
      * Interface to send call-backs to the application when the service is connected.
      */
-    public abstract static class SecureElementListener extends ISecureElementListener.Stub {
+    public interface SecureElementListener {
+        /**
+         * Called by the framework when the service is connected.
+         */
+        void onServiceConnected();
+    }
+
+    /**
+     * Listener object that allows the notification of the caller if this
+     * SEService could be bound to the backend.
+     */
+    private class SEListener extends ISecureElementListener.Stub {
+        public SecureElementListener mListener = null;
+
         @Override
         public IBinder asBinder() {
             return this;
         }
 
-        /**
-         * Called by the framework when the service is connected.
-         */
-        public void serviceConnected() {};
+        public void onServiceConnected() {
+            if (mListener != null) {
+                mListener.onServiceConnected();
+            }
+        }
     }
+    private SEListener mSEListener = new SEListener();
 
     private static final String TAG = "OMAPI.SEService";
 
@@ -95,34 +110,28 @@
     private final HashMap<String, Reader> mReaders = new HashMap<String, Reader>();
 
     /**
-     * Listener object that allows the notification of the caller if this
-     * SEService could be bound to the backend.
-     */
-    private ISecureElementListener mSEListener;
-
-    /**
      * Establishes a new connection that can be used to connect to all the
      * Secure Elements available in the system. The connection process can be
      * quite long, so it happens in an asynchronous way. It is usable only if
      * the specified listener is called or if isConnected() returns
      * <code>true</code>. <br>
      * The call-back object passed as a parameter will have its
-     * serviceConnected() method called when the connection actually happen.
+     * onServiceConnected() method called when the connection actually happen.
      *
      * @param context
      *            the context of the calling application. Cannot be
      *            <code>null</code>.
      * @param listener
-     *            a SecureElementListener object. Can be <code>null</code>.
+     *            a SecureElementListener object.
      */
-    public SEService(Context context, SecureElementListener listener) {
+    public SEService(@NonNull Context context, @NonNull SecureElementListener listener) {
 
         if (context == null) {
             throw new NullPointerException("context must not be null");
         }
 
         mContext = context;
-        mSEListener = listener;
+        mSEListener.mListener = listener;
 
         mConnection = new ServiceConnection() {
 
@@ -131,9 +140,7 @@
 
                 mSecureElementService = ISecureElementService.Stub.asInterface(service);
                 if (mSEListener != null) {
-                    try {
-                        mSEListener.serviceConnected();
-                    } catch (RemoteException ignore) { }
+                    mSEListener.onServiceConnected();
                 }
                 Log.i(TAG, "Service onServiceConnected");
             }
@@ -233,7 +240,7 @@
      *
      * @return String containing the OpenMobile API version (e.g. "3.0").
      */
-    public String getVersion() {
+    public @NonNull String getVersion() {
         return "3.2";
     }
 
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
index 3d8b74b..adfeddd 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -47,7 +47,8 @@
     private final ISecureElementSession mSession;
     private static final String TAG = "OMAPI.Session";
 
-    Session(SEService service, ISecureElementSession session, Reader reader) {
+    Session(@NonNull SEService service, @NonNull ISecureElementSession session,
+            @NonNull Reader reader) {
         if (service == null || reader == null || session == null) {
             throw new IllegalArgumentException("Parameters cannot be null");
         }
@@ -195,7 +196,8 @@
      *             supported by the device
      * @return an instance of Channel if available or null.
      */
-    public @Nullable Channel openBasicChannel(byte[] aid, byte p2) throws IOException {
+    public @Nullable Channel openBasicChannel(@Nullable byte[] aid, @Nullable byte p2)
+            throws IOException {
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
@@ -223,32 +225,6 @@
     }
 
     /**
-     * This method is provided to ease the development of mobile application and for compliancy
-     * with existing applications.
-     * This method is equivalent to openBasicChannel(aid, P2=0x00)
-     *
-     * @param aid the AID of the Applet to be selected on this channel, as a
-     *            byte array, or null if no Applet is to be selected.
-     * @throws IOException if there is a communication problem to the reader or
-     *             the Secure Element.
-     * @throws IllegalStateException if the Secure Element session is used after
-     *             being closed.
-     * @throws IllegalArgumentException if the aid's length is not within 5 to
-     *             16 (inclusive).
-     * @throws SecurityException if the calling application cannot be granted
-     *             access to this AID or the default Applet on this
-     *             session.
-     * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
-     *             selected.
-     * @throws UnsupportedOperationException if the given P2 parameter is not
-     *             supported by the device
-     * @return an instance of Channel if available or null.
-     */
-    public @Nullable Channel openBasicChannel(byte[] aid) throws IOException {
-        return openBasicChannel(aid, (byte) 0x00);
-    }
-
-    /**
      * Open a logical channel with the Secure Element, selecting the Applet represented by
      * the given AID. If the AID is null, which means no Applet is to be selected on this
      * channel, the default Applet is used. It's up to the Secure Element to choose which
@@ -300,7 +276,8 @@
      * @return an instance of Channel. Null if the Secure Element is unable to
      *         provide a new logical channel.
      */
-    public @Nullable Channel openLogicalChannel(byte[] aid, byte p2) throws IOException {
+    public @Nullable Channel openLogicalChannel(@Nullable byte[] aid, @Nullable byte p2)
+            throws IOException {
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
@@ -327,32 +304,4 @@
             }
         }
     }
-
-    /**
-     * This method is provided to ease the development of mobile application and for compliancy
-     * with existing applications.
-     * This method is equivalent to openLogicalChannel(aid, P2=0x00)
-     *
-     * @param aid the AID of the Applet to be selected on this channel, as a
-     *            byte array.
-     * @throws IOException if there is a communication problem to the reader or
-     *             the Secure Element.
-     * @throws IllegalStateException if the Secure Element is used after being
-     *             closed.
-     * @throws IllegalArgumentException if the aid's length is not within 5 to
-     *             16 (inclusive).
-     * @throws SecurityException if the calling application cannot be granted
-     *             access to this AID or the default Applet on this
-     *             session.
-     * @throws NoSuchElementException if the AID on the Secure Element is not
-     *             available or cannot be selected or a logical channel is already
-     *             open to a non-multiselectable Applet.
-     * @throws UnsupportedOperationException if the given P2 parameter is not
-     *             supported by the device.
-     * @return an instance of Channel. Null if the Secure Element is unable to
-     *         provide a new logical channel.
-     */
-    public @Nullable Channel openLogicalChannel(byte[] aid) throws IOException {
-        return openLogicalChannel(aid, (byte) 0x00);
-    }
 }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 9687009..eecdb74 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -37,7 +37,6 @@
     private static final Map<String, String> DEFAULT_FLAGS;
     static {
         DEFAULT_FLAGS = new HashMap<>();
-        DEFAULT_FLAGS.put("settings_battery_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
         DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
         DEFAULT_FLAGS.put("settings_about_phone_v2", "true");
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 01fd090..88300db 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1819,13 +1819,22 @@
             final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
                     Helper.toArray(ids));
 
+            ArrayList<AutofillId> failedIds = null;
+
             for (int i = 0; i < itemCount; i++) {
                 final AutofillId id = ids.get(i);
                 final AutofillValue value = values.get(i);
                 final int viewId = id.getViewId();
                 final View view = views[i];
                 if (view == null) {
-                    Log.w(TAG, "autofill(): no View with id " + viewId);
+                    // Most likely view has been removed after the initial request was sent to the
+                    // the service; this is fine, but we need to update the view status in the
+                    // server side so it can be triggered again.
+                    Log.d(TAG, "autofill(): no View with id " + id);
+                    if (failedIds == null) {
+                        failedIds = new ArrayList<>();
+                    }
+                    failedIds.add(id);
                     continue;
                 }
                 if (id.isVirtual()) {
@@ -1859,12 +1868,28 @@
                 }
             }
 
+            if (failedIds != null) {
+                if (sVerbose) {
+                    Log.v(TAG, "autofill(): total failed views: " + failedIds);
+                }
+                try {
+                    mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
+                } catch (RemoteException e) {
+                    // In theory, we could ignore this error since it's not a big deal, but
+                    // in reality, we rather crash the app anyways, as the failure could be
+                    // a consequence of something going wrong on the server side...
+                    e.rethrowFromSystemServer();
+                }
+            }
+
             if (virtualValues != null) {
                 for (int i = 0; i < virtualValues.size(); i++) {
                     final View parent = virtualValues.keyAt(i);
                     final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
                     parent.autofill(childrenValues);
                     numApplied += childrenValues.size();
+                    // TODO: we should provide a callback so the parent can call failures; something
+                    // like notifyAutofillFailed(View view, int[] childrenIds);
                 }
             }
 
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 56f79ab..176df73 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -16,6 +16,8 @@
 
 package android.view.autofill;
 
+import java.util.List;
+
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -47,6 +49,7 @@
             in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId,
             boolean hasCallback, int flags, in ComponentName componentName, int sessionId,
             int action, boolean compatMode);
+    void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
     void finishSession(int sessionId, int userId);
     void cancelSession(int sessionId, int userId);
     void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 514ff76..03dd77f 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -93,6 +93,7 @@
     void noteVibratorOff(int uid);
     void noteGpsChanged(in WorkSource oldSource, in WorkSource newSource);
     void noteGpsSignalQuality(int signalLevel);
+    void noteUsbConnectionState(boolean connected);
     void noteScreenState(int state);
     void noteScreenBrightness(int brightness);
     void noteUserActivity(int uid, int event);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3c150c1..8117bf7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.job.JobProtoEnums;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.UidTraffic;
 import android.content.ContentResolver;
@@ -767,6 +766,8 @@
     int mCameraOnNesting;
     StopwatchTimer mCameraOnTimer;
 
+    int mUsbDataState; // 0: unknown, 1: disconnected, 2: connected
+
     int mGpsSignalQualityBin = -1;
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     protected final StopwatchTimer[] mGpsSignalQualityTimer =
@@ -5240,6 +5241,19 @@
         }
     }
 
+    public void noteUsbConnectionStateLocked(boolean connected) {
+        int newState = connected ? 2 : 1;
+        if (mUsbDataState != newState) {
+            mUsbDataState = newState;
+            if (connected) {
+                mHistoryCur.states2 |= HistoryItem.STATE2_USB_DATA_LINK_FLAG;
+            } else {
+                mHistoryCur.states2 &= ~HistoryItem.STATE2_USB_DATA_LINK_FLAG;
+            }
+            addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+        }
+    }
+
     void stopAllPhoneSignalStrengthTimersLocked(int except) {
         final long elapsedRealtime = mClocks.elapsedRealtime();
         for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -5999,7 +6013,6 @@
             if (strengthBin >= 0) {
                 if (!mWifiSignalStrengthsTimer[strengthBin].isRunningLocked()) {
                     mWifiSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtime);
-                    StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED, strengthBin);
                 }
                 mHistoryCur.states2 =
                         (mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
@@ -6010,6 +6023,7 @@
             } else {
                 stopAllWifiSignalStrengthTimersLocked(-1);
             }
+            StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED, strengthBin);
             mWifiSignalStrengthBin = strengthBin;
         }
     }
@@ -7250,25 +7264,17 @@
 
         public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
             createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
-                    StatsLog.AUDIO_STATE_CHANGED__STATE__ON);
         }
 
         public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
             if (mAudioTurnedOnTimer != null) {
                 mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
-                if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
-                            StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
-                }
             }
         }
 
         public void noteResetAudioLocked(long elapsedRealtimeMs) {
             if (mAudioTurnedOnTimer != null) {
                 mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
-                        StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -7282,25 +7288,17 @@
 
         public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
             createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
-                    StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON);
         }
 
         public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
             if (mVideoTurnedOnTimer != null) {
                 mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
-                if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
-                            null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
-                }
             }
         }
 
         public void noteResetVideoLocked(long elapsedRealtimeMs) {
             if (mVideoTurnedOnTimer != null) {
                 mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
-                        StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
             }
         }
 
@@ -7314,25 +7312,17 @@
 
         public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
             createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
-                    StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
         }
 
         public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
             if (mFlashlightTurnedOnTimer != null) {
                 mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
-                if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
-                    StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
-                            StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
-                }
             }
         }
 
         public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
             if (mFlashlightTurnedOnTimer != null) {
                 mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
-                        StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -7346,25 +7336,17 @@
 
         public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
             createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
-                    StatsLog.CAMERA_STATE_CHANGED__STATE__ON);
         }
 
         public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
             if (mCameraTurnedOnTimer != null) {
                 mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
-                if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
-                            StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
-                }
             }
         }
 
         public void noteResetCameraLocked(long elapsedRealtimeMs) {
             if (mCameraTurnedOnTimer != null) {
                 mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
-                        StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -10040,8 +10022,6 @@
             DualTimer t = mSyncStats.startObject(name);
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
-                        StatsLog.SYNC_STATE_CHANGED__STATE__ON);
             }
         }
 
@@ -10049,10 +10029,6 @@
             DualTimer t = mSyncStats.stopObject(name);
             if (t != null) {
                 t.stopRunningLocked(elapsedRealtimeMs);
-                if (!t.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
-                            StatsLog.SYNC_STATE_CHANGED__STATE__OFF);
-                }
             }
         }
 
@@ -10060,9 +10036,6 @@
             DualTimer t = mJobStats.startObject(name);
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
-                        name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
-                        JobProtoEnums.STOP_REASON_CANCELLED);
             }
         }
 
@@ -10070,11 +10043,6 @@
             DualTimer t = mJobStats.stopObject(name);
             if (t != null) {
                 t.stopRunningLocked(elapsedRealtimeMs);
-                if (!t.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
-                            name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
-                            stopReason);
-                }
             }
             if (mBsi.mOnBatteryTimeBase.isRunning()) {
                 SparseIntArray types = mJobCompletions.get(name);
@@ -10182,10 +10150,6 @@
         public void noteStartSensor(int sensor, long elapsedRealtimeMs) {
             DualTimer t = getSensorTimerLocked(sensor, /* create= */ true);
             t.startRunningLocked(elapsedRealtimeMs);
-            if (sensor != Sensor.GPS) {
-                StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
-                        StatsLog.SENSOR_STATE_CHANGED__STATE__ON);
-            }
         }
 
         public void noteStopSensor(int sensor, long elapsedRealtimeMs) {
@@ -10193,10 +10157,6 @@
             DualTimer t = getSensorTimerLocked(sensor, false);
             if (t != null) {
                 t.stopRunningLocked(elapsedRealtimeMs);
-                if (sensor != Sensor.GPS) {
-                    StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
-                             sensor, StatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
-                }
             }
         }
 
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index c71e505..c5be8e4 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -142,12 +142,24 @@
     // Package names that are exempted from private API blacklisting
     final ArraySet<String> mHiddenApiPackageWhitelist = new ArraySet<>();
 
+    // The list of carrier applications which should be disabled until used.
+    // This function suppresses update notifications for these pre-installed apps.
+    // In SubscriptionInfoUpdater, the listed applications are disabled until used when all of the
+    // following conditions are met.
+    // 1. Not currently carrier-privileged according to the inserted SIM
+    // 2. Pre-installed
+    // 3. In the default state (enabled but not explicitly)
+    // And SubscriptionInfoUpdater undoes this and marks the app enabled when a SIM is inserted
+    // that marks the app as carrier privileged. It also grants the app default permissions
+    // for Phone and Location. As such, apps MUST only ever be added to this list if they
+    // obtain user consent to access their location through other means.
+    final ArraySet<String> mDisabledUntilUsedPreinstalledCarrierApps = new ArraySet<>();
+
     // These are the packages of carrier-associated apps which should be disabled until used until
     // a SIM is inserted which grants carrier privileges to that carrier app.
     final ArrayMap<String, List<String>> mDisabledUntilUsedPreinstalledCarrierAssociatedApps =
             new ArrayMap<>();
 
-
     final ArrayMap<String, ArraySet<String>> mPrivAppPermissions = new ArrayMap<>();
     final ArrayMap<String, ArraySet<String>> mPrivAppDenyPermissions = new ArrayMap<>();
 
@@ -232,6 +244,10 @@
         return mBackupTransportWhitelist;
     }
 
+    public ArraySet<String> getDisabledUntilUsedPreinstalledCarrierApps() {
+        return mDisabledUntilUsedPreinstalledCarrierApps;
+    }
+
     public ArrayMap<String, List<String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps() {
         return mDisabledUntilUsedPreinstalledCarrierAssociatedApps;
     }
@@ -630,6 +646,18 @@
                         associatedPkgs.add(pkgname);
                     }
                     XmlUtils.skipCurrentTag(parser);
+                } else if ("disabled-until-used-preinstalled-carrier-app".equals(name)
+                        && allowAppConfigs) {
+                    String pkgname = parser.getAttributeValue(null, "package");
+                    if (pkgname == null) {
+                        Slog.w(TAG,
+                                "<disabled-until-used-preinstalled-carrier-app> without "
+                                        + "package in " + permFile + " at "
+                                        + parser.getPositionDescription());
+                    } else {
+                        mDisabledUntilUsedPreinstalledCarrierApps.add(pkgname);
+                    }
+                    XmlUtils.skipCurrentTag(parser);
                 } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
                     // privapp permissions from system, vendor and product partitions are stored
                     // separately. This is to prevent xml files in the vendor partition from
diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto
index 0f14f20..17bf4fb 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -24,6 +24,7 @@
 // Reasons a job is stopped.
 // Primarily used in android.app.job.JobParameters.java.
 enum StopReasonEnum {
+  STOP_REASON_UNKNOWN = -1;
   STOP_REASON_CANCELLED = 0;
   STOP_REASON_CONSTRAINTS_NOT_SATISFIED = 1;
   STOP_REASON_PREEMPT = 2;
diff --git a/core/proto/android/content/clipdata.proto b/core/proto/android/content/clipdata.proto
index aeeef97..cbc00a7 100644
--- a/core/proto/android/content/clipdata.proto
+++ b/core/proto/android/content/clipdata.proto
@@ -25,7 +25,7 @@
 
 // An android.content.ClipData object.
 message ClipDataProto {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    option (.android.msg_privacy).dest = DEST_LOCAL;
 
     optional android.content.ClipDescriptionProto description = 1;
 
@@ -40,7 +40,7 @@
 
     // An android.content.ClipData.Item object.
     message Item {
-        option (.android.msg_privacy).dest = DEST_EXPLICIT;
+        option (.android.msg_privacy).dest = DEST_LOCAL;
 
         oneof data {
             string html_text = 1;
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index f40d669..76a3b5d 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -20,9 +20,9 @@
 option java_multiple_files = true;
 option java_outer_classname = "SettingsServiceProto";
 
-import "frameworks/base/core/proto/android/providers/settings/common.proto";
 import "frameworks/base/core/proto/android/providers/settings/global.proto";
 import "frameworks/base/core/proto/android/providers/settings/secure.proto";
+import "frameworks/base/core/proto/android/providers/settings/system.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 message SettingsServiceDumpProto {
@@ -48,111 +48,6 @@
     optional SystemSettingsProto system_settings = 3;
 }
 
-// Note: it's a conscious decision to add each setting as a separate field. This
-// allows annotating each setting with its own privacy tag.
-message SystemSettingsProto {
-    option (android.msg_privacy).dest = DEST_EXPLICIT;
-
-    repeated SettingsOperationProto historical_operations = 1;
-
-    optional SettingProto end_button_behavior = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto advanced_settings = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto bluetooth_discoverability = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto bluetooth_discoverability_timeout = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto font_scale = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto system_locales = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto display_color_mode = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screen_off_timeout = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screen_brightness = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screen_brightness_for_vr = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screen_brightness_mode = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screen_auto_brightness_adj = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Determines which streams are affected by ringer mode changes. The stream
-    // type's bit will be set to 1 if it should be muted when going into an
-    // inaudible ringer mode.
-    optional SettingProto mode_ringer_streams_affected = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto mute_streams_affected = 15 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto vibrate_on = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto vibrate_input_devices = 17 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto notification_vibration_intensity = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto haptic_feedback_intensity = 19 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_ring = 20 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_system = 21 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_voice = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_music = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_alarm = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_notification = 25 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_bluetooth_sco = 26 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_accessibility = 27 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_master = 28 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto master_mono = 29 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Whether silent mode should allow vibration feedback. This is used
-    // internally in AudioService and the Sound settings activity to coordinate
-    // decoupling of vibrate and silent modes. This setting will likely be
-    // removed in a future release with support for audio/vibe feedback
-    // profiles.
-    // Not used anymore. On devices with vibrator, the user explicitly selects
-    // silent or vibrate mode. Kept for use by legacy database upgrade code in
-    // DatabaseHelper.
-    optional SettingProto vibrate_in_silent = 30 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Appended to various volume related settings to record the previous values
-    // before the settings were affected by a silent/vibrate ringer mode change.
-    optional SettingProto append_for_last_audible = 31 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto ringtone = 32;
-    optional SettingProto ringtone_cache = 33;
-    optional SettingProto notification_sound = 34;
-    optional SettingProto notification_sound_cache = 35;
-    optional SettingProto alarm_alert = 36;
-    optional SettingProto alarm_alert_cache = 37;
-    optional SettingProto media_button_receiver = 38;
-    optional SettingProto text_auto_replace = 39 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto text_auto_caps = 40 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto text_auto_punctuate = 41 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto text_show_password = 42 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto show_gtalk_service_status = 43 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto time_12_24 = 44 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto date_format = 45 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto setup_wizard_has_run = 46 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accelerometer_rotation = 47 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto user_rotation = 48 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto hide_rotation_lock_toggle_for_accessibility = 49 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto vibrate_when_ringing = 50 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto dtmf_tone_when_dialing = 51 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto dtmf_tone_type_when_dialing = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto hearing_aid = 53 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto tty_mode = 54 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // User-selected RTT mode. When on, outgoing and incoming calls will be
-    // answered as RTT calls when supported by the device and carrier. Boolean
-    // value.
-    optional SettingProto rtt_calling_mode = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sound_effects_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto haptic_feedback_enabled = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto notification_light_pulse = 58 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Show pointer location on screen? 0 = no, 1 = yes.
-    optional SettingProto pointer_location = 59 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto show_touches = 60 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Log raw orientation data from {@link
-    // com.android.server.policy.WindowOrientationListener} for use with the
-    // orientationplot.py tool.
-    // 0 = no, 1 = yes
-    optional SettingProto window_orientation_listener_log = 61 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lockscreen_sounds_enabled = 62 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lockscreen_disabled = 63 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sip_receive_calls = 64 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sip_call_options = 65 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sip_always = 66 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sip_address_only = 67 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto pointer_speed = 68 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lock_to_app_enabled = 69 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto egg_mode = 70 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto show_battery_percent = 71 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto when_to_make_wifi_calls = 72 [ (android.privacy).dest = DEST_AUTOMATIC ];
-
-    // Please insert fields in the same order as in
-    // frameworks/base/core/java/android/provider/Settings.java.
-    // Next tag = 73;
-}
-
 message SettingsProto {
     // Enum values gotten from Settings.java
     enum ScreenBrightnessMode {
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
new file mode 100644
index 0000000..6b6edd2
--- /dev/null
+++ b/core/proto/android/providers/settings/system.proto
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.providers.settings;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/providers/settings/common.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+// Note: it's a conscious decision to add each setting as a separate field. This
+// allows annotating each setting with its own privacy tag.
+message SystemSettingsProto {
+    option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+    repeated SettingsOperationProto historical_operations = 1;
+
+    optional SettingProto advanced_settings = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Alarm {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // "alarm_alert" in code.
+        optional SettingProto default_uri = 1;
+        optional SettingProto alert_cache = 2;
+    }
+    optional Alarm alarm = 3;
+
+    message Bluetooth {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Whether remote devices may discover and/or connect to this device:
+        // 2 -- discoverable and connectable
+        // 1 -- connectable but not discoverable
+        // 0 -- neither connectable nor discoverable
+        optional SettingProto discoverability = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto discoverability_timeout_secs = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Bluetooth bluetooth = 4;
+
+    optional SettingProto date_format = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto display_color_mode = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message DevOptions {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Show pointer location on screen? 0 = no, 1 = yes. "pointer_location
+        // in code.
+        optional SettingProto pointer_location = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto show_touches = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Log raw orientation data from {@link
+        // com.android.server.policy.WindowOrientationListener} for use with the
+        // orientationplot.py tool.
+        // 0 = no, 1 = yes
+        optional SettingProto window_orientation_listener_log = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional DevOptions developer_options = 7;
+
+    message DtmfTone {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // "dtmf_tone_when_dialing" in code.
+        optional SettingProto play_when_dialing = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // "dtmf_tone_type_when_dialing" in code.
+        optional SettingProto type_played_when_dialing = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional DtmfTone dtmf_tone = 8;
+
+    optional SettingProto egg_mode = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto end_button_behavior = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto font_scale = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message HapticFeedback {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto intensity = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional HapticFeedback haptic_feedback = 12;
+
+    // Whether the hearing aid is enabled. The value is boolean (1 or 0).
+    optional SettingProto hearing_aid = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto lock_to_app_enabled = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Lockscreen {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto sounds_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto disabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Lockscreen lockscreen = 15;
+
+    // The system default media button event receiver.
+    optional SettingProto media_button_receiver = 16;
+
+    message Notification {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto sound = 1;
+        optional SettingProto sound_cache = 2;
+        optional SettingProto light_pulse = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto vibration_intensity = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Notification notification = 17;
+
+    optional SettingProto pointer_speed = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Ringtone {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // "ringtone" in code. The system-wide default ringtone URI.
+        optional SettingProto default_uri = 1;
+        optional SettingProto cache = 2;
+    }
+    optional Ringtone ringtone = 19;
+
+    message Rotation {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Control whether the accelerometer will be used to change screen
+        // orientation.  If 0, it will not be used unless explicitly requested
+        // by the application; if 1, it will be used by default unless
+        // explicitly disabled by the application.
+        optional SettingProto accelerometer_rotation = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Default screen rotation when no other policy applies.
+        // When accelerometer_rotation is zero and no on-screen Activity expresses a
+        // preference, this rotation value will be used. Must be one of the
+        // {@link android.view.Surface#ROTATION_0 Surface rotation constants}.
+        optional SettingProto user_rotation = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Control whether the rotation lock toggle in the System UI should be hidden.
+        // Typically this is done for accessibility purposes to make it harder for
+        // the user to accidentally toggle the rotation lock while the display rotation
+        // has been locked for accessibility.
+        // If 0, then rotation lock toggle is not hidden for accessibility (although it may be
+        // unavailable for other reasons).  If 1, then the rotation lock toggle is hidden.
+        optional SettingProto hide_rotation_lock_toggle_for_accessibility = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Rotation rotation = 20;
+
+    // User-selected RTT mode. When on, outgoing and incoming calls will be
+    // answered as RTT calls when supported by the device and carrier. Boolean
+    // value.
+    optional SettingProto rtt_calling_mode = 21 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Screen {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto off_timeout = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto brightness = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto brightness_for_vr = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto brightness_mode = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto auto_brightness_adj = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Screen screen = 22;
+
+    optional SettingProto setup_wizard_has_run = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto show_battery_percent = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto show_gtalk_service_status = 25 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Sip {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto receive_calls = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto call_options = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto always = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto address_only = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Sip sip = 26;
+
+    optional SettingProto sound_effects_enabled = 27 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto system_locales = 28 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Text {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto auto_replace = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto auto_caps = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto auto_punctuate = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto show_password = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Text text = 29;
+
+    optional SettingProto time_12_24 = 30 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto tty_mode = 31 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Vibrate {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto on = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto input_devices = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Whether silent mode should allow vibration feedback. This is used
+        // internally in AudioService and the Sound settings activity to coordinate
+        // decoupling of vibrate and silent modes. This setting will likely be
+        // removed in a future release with support for audio/vibe feedback
+        // profiles.
+        // Not used anymore. On devices with vibrator, the user explicitly selects
+        // silent or vibrate mode. Kept for use by legacy database upgrade code in
+        // DatabaseHelper.
+        optional SettingProto in_silent = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto when_ringing = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Vibrate vibrate = 32;
+
+    message Volume {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto ring = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto system = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto voice = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto music = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto alarm = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto notification = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto bluetooth_sco = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto accessibility = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto master = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto master_mono = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Determines which streams are affected by ringer mode changes. The stream
+        // type's bit will be set to 1 if it should be muted when going into an
+        // inaudible ringer mode.
+        optional SettingProto mode_ringer_streams_affected = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Which streams are affected by mute. The stream type's bit should be set
+        // to 1 if it should be muted when a mute request is received.
+        optional SettingProto mute_streams_affected = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Volume volume = 33;
+
+    optional SettingProto when_to_make_wifi_calls = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    // Please insert fields in alphabetical order and group them into messages
+    // if possible (to avoid reaching the method limit).
+    // Next tag = 35;
+}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 8d4b56c..bb8ce81 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -578,6 +578,12 @@
 
         optional .android.os.PersistableBundleProto extras = 14;
         optional .android.os.BundleProto transient_extras = 15;
+        // ClipData of information that is returned to the application at
+        // execution time, but not persisted by the system. This is provided by
+        // the app and the main purpose of providing a ClipData is to allow
+        // granting of URI permissions for data associated with the clip.  The
+        // exact kind of permission grant to perform is specified in the flags
+        // field.
         optional .android.content.ClipDataProto clip_data = 16;
 
         optional GrantedUriPermissionsDumpProto granted_uri_permissions = 17;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c4d3667..3fb52dc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4229,6 +4229,16 @@
                   android:exported="false">
         </receiver>
 
+        <receiver android:name="com.android.server.am.BatteryStatsService$UsbConnectionReceiver"
+                  android:exported="false">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.hardware.usb.action.USB_STATE" />
+            </intent-filter>
+        </receiver>
+
         <service android:name="android.hardware.location.GeofenceHardwareService"
             android:permission="android.permission.LOCATION_HARDWARE"
             android:exported="false" />
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 265eaaf..351bd81 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -29,7 +29,10 @@
 
     <!-- Height of the status bar -->
     <dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen>
-
+    <!-- Height of area above QQS where battery/time go -->
+    <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
+    <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
+    <dimen name="quick_qs_total_height">152dp</dimen>
     <!-- Default height of an action bar. -->
     <dimen name="action_bar_default_height">40dip</dimen>
     <!-- Vertical padding around action bar icons. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b5e223c..a52a089 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1095,6 +1095,9 @@
     <!-- Display low battery warning when battery level dips to this value -->
     <integer name="config_lowBatteryWarningLevel">15</integer>
 
+    <!-- The default suggested battery % at which we enable battery saver automatically.  -->
+    <integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
+
     <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
          plus this -->
     <integer name="config_lowBatteryCloseWarningBump">5</integer>
@@ -2389,19 +2392,6 @@
         <item>com.android.inputmethod.latin</item>
     </string-array>
 
-    <!-- The list of carrier applications which should be disabled until used.
-         This function suppresses update notifications for these pre-installed apps.
-         In SubscriptionInfoUpdater, the listed applications are disabled until used when all of the
-         following conditions are met.
-         1. Not currently carrier-privileged according to the inserted SIM
-         2. Pre-installed
-         3. In the default state (enabled but not explicitly)
-         And SubscriptionInfoUpdater undoes this and marks the app enabled when a SIM is inserted
-         that marks the app as carrier privileged. It also grants the app default permissions
-         for Phone and Location. As such, apps MUST only ever be added to this list if they
-         obtain user consent to access their location through other means. -->
-    <string-array name="config_disabledUntilUsedPreinstalledCarrierApps" translatable="false" />
-
     <!-- The list of classes that should be added to the notification ranking pipeline.
      See {@link com.android.server.notification.NotificationSignalExtractor}
       If you add a new extractor to this list make sure to update
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5eeb418..03acd5c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1233,7 +1233,6 @@
   <java-symbol type="array" name="supported_locales" />
   <java-symbol type="array" name="config_cdma_dun_supported_types" />
   <java-symbol type="array" name="config_disabledUntilUsedPreinstalledImes" />
-  <java-symbol type="array" name="config_disabledUntilUsedPreinstalledCarrierApps" />
   <java-symbol type="array" name="config_callBarringMMI" />
   <java-symbol type="array" name="config_globalActionsList" />
   <java-symbol type="array" name="config_telephonyEuiccDeviceCapabilities" />
@@ -1660,6 +1659,7 @@
   <java-symbol type="dimen" name="navigation_bar_height_landscape_car_mode" />
   <java-symbol type="dimen" name="navigation_bar_width_car_mode" />
   <java-symbol type="dimen" name="status_bar_height" />
+  <java-symbol type="dimen" name="quick_qs_offset_height" />
   <java-symbol type="dimen" name="quick_qs_total_height" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_off" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_on" />
@@ -3314,4 +3314,6 @@
   <java-symbol type="string" name="notification_app_name_system" />
   <java-symbol type="string" name="notification_app_name_settings" />
 
+  <java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
+
 </resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 6ef773a..2a3fcad 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -586,7 +586,8 @@
                  Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL,
                  Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
                  Settings.Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT,
-                 Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED);
+                 Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
+                 Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 97edf22..1eebd26 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -318,13 +318,17 @@
      * Retrieve the timestamp associated with the texture image set by the most recent call to
      * updateTexImage.
      *
-     * This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
-     * should be unaffected by time-of-day adjustments, and for a camera should be strictly
-     * monotonic but for a MediaPlayer may be reset when the position is set.  The
-     * specific meaning and zero point of the timestamp depends on the source providing images to
-     * the SurfaceTexture. Unless otherwise specified by the image source, timestamps cannot
-     * generally be compared across SurfaceTexture instances, or across multiple program
-     * invocations. It is mostly useful for determining time offsets between subsequent frames.
+     * <p>This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
+     * should be unaffected by time-of-day adjustments. The specific meaning and zero point of the
+     * timestamp depends on the source providing images to the SurfaceTexture. Unless otherwise
+     * specified by the image source, timestamps cannot generally be compared across SurfaceTexture
+     * instances, or across multiple program invocations. It is mostly useful for determining time
+     * offsets between subsequent frames.</p>
+     *
+     * <p>For camera sources, timestamps should be strictly monotonic. Timestamps from MediaPlayer
+     * sources may be reset when the playback position is set. For EGL and Vulkan producers, the
+     * timestamp is the desired present time set with the EGL_ANDROID_presentation_time or
+     * VK_GOOGLE_display_timing extensions.</p>
      */
 
     public long getTimestamp() {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 09b3b9b..419eb24 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -243,13 +243,7 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
-                        spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody(),
-                        spec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                        spec.isUserConfirmationRequired());
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec);
             } catch (IllegalStateException | IllegalArgumentException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -285,16 +279,7 @@
         args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
-        KeymasterUtils.addUserAuthArgs(args,
-                spec.isUserAuthenticationRequired(),
-                spec.getUserAuthenticationValidityDurationSeconds(),
-                spec.isUserAuthenticationValidWhileOnBody(),
-                spec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                spec.isUserConfirmationRequired());
-        if (spec.isTrustedUserPresenceRequired()) {
-            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
-        }
+        KeymasterUtils.addUserAuthArgs(args, spec);
         KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                 args,
                 mKeymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index e33e3cd..d68a33d 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -344,13 +344,7 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
-                        mSpec.isUserAuthenticationRequired(),
-                        mSpec.getUserAuthenticationValidityDurationSeconds(),
-                        mSpec.isUserAuthenticationValidWhileOnBody(),
-                        mSpec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                        mSpec.isUserConfirmationRequired());
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec);
             } catch (IllegalArgumentException | IllegalStateException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -541,13 +535,7 @@
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
 
-        KeymasterUtils.addUserAuthArgs(args,
-                mSpec.isUserAuthenticationRequired(),
-                mSpec.getUserAuthenticationValidityDurationSeconds(),
-                mSpec.isUserAuthenticationValidWhileOnBody(),
-                mSpec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                mSpec.isUserConfirmationRequired());
+        KeymasterUtils.addUserAuthArgs(args, mSpec);
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart());
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
                 mSpec.getKeyValidityForOriginationEnd());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 05cc74a..fc86ca0 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -497,13 +497,7 @@
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
                         KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
-                KeymasterUtils.addUserAuthArgs(importArgs,
-                        spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody(),
-                        spec.isInvalidatedByBiometricEnrollment(),
-                        spec.getBoundToSpecificSecureUserId(),
-                        spec.isUserConfirmationRequired());
+                KeymasterUtils.addUserAuthArgs(importArgs, spec);
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
                         spec.getKeyValidityStart());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
@@ -700,13 +694,7 @@
             int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
                     params.getEncryptionPaddings());
             args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
-            KeymasterUtils.addUserAuthArgs(args,
-                    params.isUserAuthenticationRequired(),
-                    params.getUserAuthenticationValidityDurationSeconds(),
-                    params.isUserAuthenticationValidWhileOnBody(),
-                    params.isInvalidatedByBiometricEnrollment(),
-                    params.getBoundToSpecificSecureUserId(),
-                    params.isUserConfirmationRequired());
+            KeymasterUtils.addUserAuthArgs(args, params);
             KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                     args,
                     keymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index da23c70..4b9f3c80 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.text.TextUtils;
 
@@ -232,7 +233,7 @@
  * key = (SecretKey) keyStore.getKey("key2", null);
  * }</pre>
  */
-public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
+public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
 
     private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
     private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
@@ -669,6 +670,13 @@
     }
 
     /**
+     * @hide
+     */
+    public long getBoundToSpecificSecureUserId() {
+        return GateKeeper.INVALID_SECURE_USER_ID;
+    }
+
+    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index b5b3281..95eeec7 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -212,7 +212,7 @@
  * ...
  * }</pre>
  */
-public final class KeyProtection implements ProtectionParameter {
+public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
     private final Date mKeyValidityStart;
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
@@ -224,6 +224,7 @@
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
+    private final boolean mTrustedUserPresenceRequred;
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mInvalidatedByBiometricEnrollment;
     private final long mBoundToSecureUserId;
@@ -242,6 +243,7 @@
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
+            boolean trustedUserPresenceRequred,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
             long boundToSecureUserId,
@@ -260,6 +262,7 @@
         mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticationRequired = userAuthenticationRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mTrustedUserPresenceRequred = trustedUserPresenceRequred;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mBoundToSecureUserId = boundToSecureUserId;
@@ -437,6 +440,14 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only if a test of user presence has
+     * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
+     */
+    public boolean isTrustedUserPresenceRequired() {
+        return mTrustedUserPresenceRequred;
+    }
+
+    /**
      * Returns {@code true} if the key will be de-authorized when the device is removed from the
      * user's body.  This option has no effect on keys that don't have an authentication validity
      * duration, and has no effect if the device lacks an on-body sensor.
@@ -509,6 +520,7 @@
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
+        private boolean mTrustedUserPresenceRequired = false;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
         private boolean mUserConfirmationRequired;
@@ -811,6 +823,16 @@
         }
 
         /**
+         * Sets whether a test of user presence is required to be performed between the
+         * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+         */
+        @NonNull
+        public Builder setTrustedUserPresenceRequired(boolean required) {
+            mTrustedUserPresenceRequired = required;
+            return this;
+        }
+
+        /**
          * Sets whether the key will remain authorized only until the device is removed from the
          * user's body up to the limit of the authentication validity period (see
          * {@link #setUserAuthenticationValidityDurationSeconds} and
@@ -910,6 +932,7 @@
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
+                    mTrustedUserPresenceRequired,
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
                     mBoundToSecureUserId,
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 4e28601..0ef08f2 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -18,6 +18,7 @@
 
 import android.util.Log;
 import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
 import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.security.keymaster.KeymasterArguments;
@@ -101,22 +102,21 @@
      *         require user authentication.
      */
     public static void addUserAuthArgs(KeymasterArguments args,
-            boolean userAuthenticationRequired,
-            int userAuthenticationValidityDurationSeconds,
-            boolean userAuthenticationValidWhileOnBody,
-            boolean invalidatedByBiometricEnrollment,
-            long boundToSpecificSecureUserId,
-            boolean userConfirmationRequired) {
-        if (userConfirmationRequired) {
+            UserAuthArgs spec) {
+        if (spec.isUserConfirmationRequired()) {
             args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
         }
 
-        if (!userAuthenticationRequired) {
+        if (spec.isTrustedUserPresenceRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
+        }
+
+        if (!spec.isUserAuthenticationRequired()) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
             return;
         }
 
-        if (userAuthenticationValidityDurationSeconds == -1) {
+        if (spec.getUserAuthenticationValidityDurationSeconds() == -1) {
             // Every use of this key needs to be authorized by the user. This currently means
             // fingerprint-only auth.
             FingerprintManager fingerprintManager =
@@ -132,9 +132,9 @@
             }
 
             long sid;
-            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = boundToSpecificSecureUserId;
-            } else if (invalidatedByBiometricEnrollment) {
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = spec.getBoundToSpecificSecureUserId();
+            } else if (spec.isInvalidatedByBiometricEnrollment()) {
                 // The fingerprint-only SID will change on fingerprint enrollment or removal of all,
                 // enrolled fingerprints, invalidating the key.
                 sid = fingerprintOnlySid;
@@ -147,14 +147,14 @@
             args.addUnsignedLong(
                     KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid));
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
-            if (userAuthenticationValidWhileOnBody) {
+            if (spec.isUserAuthenticationValidWhileOnBody()) {
                 throw new ProviderException("Key validity extension while device is on-body is not "
                         + "supported for keys requiring fingerprint authentication");
             }
         } else {
             long sid;
-            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = boundToSpecificSecureUserId;
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = spec.getBoundToSpecificSecureUserId();
             } else {
                 // The key is authorized for use for the specified amount of time after the user has
                 // authenticated. Whatever unlocks the secure lock screen should authorize this key.
@@ -165,8 +165,8 @@
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
                     KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
-                    userAuthenticationValidityDurationSeconds);
-            if (userAuthenticationValidWhileOnBody) {
+                    spec.getUserAuthenticationValidityDurationSeconds());
+            if (spec.isUserAuthenticationValidWhileOnBody()) {
                 args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
             }
         }
diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java
new file mode 100644
index 0000000..1949592
--- /dev/null
+++ b/keystore/java/android/security/keystore/UserAuthArgs.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+/**
+ * @hide
+ *
+ * This is an interface to encapsulate the user authentication arguments that
+ * are passed to KeymasterUtils.addUserAuthArgs. Classes that represent
+ * authorization characteristics for new or imported keys can implement this
+ * interface to be passed to that method.
+ */
+public interface UserAuthArgs {
+
+    boolean isUserAuthenticationRequired();
+    int getUserAuthenticationValidityDurationSeconds();
+    boolean isUserAuthenticationValidWhileOnBody();
+    boolean isInvalidatedByBiometricEnrollment();
+    boolean isUserConfirmationRequired();
+    long getBoundToSpecificSecureUserId();
+    boolean isTrustedUserPresenceRequired();
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 48b7c35..42b3156 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -16,20 +16,20 @@
 
 package com.android.settingslib;
 
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.UserHandle;
+import android.support.v14.preference.SwitchPreference;
 import android.support.v4.content.res.TypedArrayUtils;
 import android.support.v7.preference.PreferenceManager;
 import android.support.v7.preference.PreferenceViewHolder;
-import android.support.v14.preference.SwitchPreference;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.TextView;
 
-import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
 /**
  * Version of SwitchPreference that can be disabled by a device admin
  * using a user restriction.
@@ -37,7 +37,7 @@
 public class RestrictedSwitchPreference extends SwitchPreference {
     RestrictedPreferenceHelper mHelper;
     boolean mUseAdditionalSummary = false;
-    String mRestrictedSwitchSummary = null;
+    CharSequence mRestrictedSwitchSummary;
 
     public RestrictedSwitchPreference(Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
@@ -57,19 +57,15 @@
 
             final TypedValue restrictedSwitchSummary = attributes.peekValue(
                     R.styleable.RestrictedSwitchPreference_restrictedSwitchSummary);
-            CharSequence data = null;
             if (restrictedSwitchSummary != null
                     && restrictedSwitchSummary.type == TypedValue.TYPE_STRING) {
                 if (restrictedSwitchSummary.resourceId != 0) {
-                    data = context.getString(restrictedSwitchSummary.resourceId);
+                    mRestrictedSwitchSummary =
+                        context.getText(restrictedSwitchSummary.resourceId);
                 } else {
-                    data = restrictedSwitchSummary.string;
+                    mRestrictedSwitchSummary = restrictedSwitchSummary.string;
                 }
             }
-            mRestrictedSwitchSummary = data == null ? null : data.toString();
-        }
-        if (mRestrictedSwitchSummary == null) {
-            mRestrictedSwitchSummary = context.getString(R.string.disabled_by_admin);
         }
         if (mUseAdditionalSummary) {
             setLayoutResource(R.layout.restricted_switch_preference);
@@ -94,6 +90,15 @@
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
         mHelper.onBindViewHolder(holder);
+
+        CharSequence switchSummary;
+        if (mRestrictedSwitchSummary == null) {
+            switchSummary = getContext().getText(isChecked()
+                ? R.string.enabled_by_admin : R.string.disabled_by_admin);
+        } else {
+            switchSummary = mRestrictedSwitchSummary;
+        }
+
         final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
         final View switchWidget = holder.findViewById(android.R.id.switch_widget);
         if (restrictedIcon != null) {
@@ -102,12 +107,13 @@
         if (switchWidget != null) {
             switchWidget.setVisibility(isDisabledByAdmin() ? View.GONE : View.VISIBLE);
         }
+
         if (mUseAdditionalSummary) {
             final TextView additionalSummaryView = (TextView) holder.findViewById(
                     R.id.additional_summary);
             if (additionalSummaryView != null) {
                 if (isDisabledByAdmin()) {
-                    additionalSummaryView.setText(mRestrictedSwitchSummary);
+                    additionalSummaryView.setText(switchSummary);
                     additionalSummaryView.setVisibility(View.VISIBLE);
                 } else {
                     additionalSummaryView.setVisibility(View.GONE);
@@ -117,7 +123,7 @@
             final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
             if (summaryView != null) {
                 if (isDisabledByAdmin()) {
-                    summaryView.setText(mRestrictedSwitchSummary);
+                    summaryView.setText(switchSummary);
                     summaryView.setVisibility(View.VISIBLE);
                 }
                 // No need to change the visibility to GONE in the else case here since Preference
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index e2c7747..28833a3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -20,7 +20,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.PowerManager;
+import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
+import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 
 /**
@@ -34,10 +36,27 @@
 
     private static final boolean DEBUG = false;
 
-    // Broadcast action for SystemUI to show the battery saver confirmation dialog.
+    private static final String SYSUI_PACKAGE = "com.android.systemui";
+
+    /** Broadcast action for SystemUI to show the battery saver confirmation dialog. */
     public static final String ACTION_SHOW_START_SAVER_CONFIRMATION = "PNW.startSaverConfirmation";
 
     /**
+     * Broadcast action for SystemUI to show the notification that suggests turning on
+     * automatic battery saver.
+     */
+    public static final String ACTION_SHOW_AUTO_SAVER_SUGGESTION
+            = "PNW.autoSaverSuggestion";
+
+    /**
+     * We show the auto battery saver suggestion notification when the user manually enables
+     * battery saver for the START_NTH time through the END_NTH time.
+     * (We won't show it for END_NTH + 1 time and after.)
+     */
+    private static final int AUTO_SAVER_SUGGESTION_START_NTH = 4;
+    private static final int AUTO_SAVER_SUGGESTION_END_NTH = 8;
+
+    /**
      * Enable / disable battery saver by user request.
      * - If it's the first time and needFirstTimeWarning, show the first time dialog.
      * - If it's 4th time through 8th time, show the schedule suggestion notification.
@@ -62,11 +81,17 @@
 
         if (context.getSystemService(PowerManager.class).setPowerSaveMode(enable)) {
             if (enable) {
-                Secure.putInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT,
-                        Secure.getInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 0) + 1);
+                final int count =
+                        Secure.getInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 0) + 1;
+                Secure.putInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, count);
 
-                // TODO If enabling, and the count is between 4 and 8 (inclusive), then
-                // show the "battery saver schedule suggestion" notification.
+                if ((count >= AUTO_SAVER_SUGGESTION_START_NTH)
+                        && (count <= AUTO_SAVER_SUGGESTION_END_NTH)
+                        && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0
+                        && Secure.getInt(cr,
+                        Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) {
+                    showAutoBatterySaverSuggestion(context);
+                }
             }
 
             return true;
@@ -79,13 +104,34 @@
                 Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 0) != 0) {
             return false; // Already shown.
         }
-        final Intent i = new Intent(ACTION_SHOW_START_SAVER_CONFIRMATION);
-        context.sendBroadcast(i);
-
+        context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION));
         return true;
     }
 
+    private static void showAutoBatterySaverSuggestion(Context context) {
+        context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION));
+    }
+
+    private static Intent getSystemUiBroadcast(String action) {
+        final Intent i = new Intent(action);
+        i.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        i.setPackage(SYSUI_PACKAGE);
+        return i;
+    }
+
     private static void setBatterySaverConfirmationAcknowledged(Context context) {
         Secure.putInt(context.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
     }
+
+    public static void suppressAutoBatterySaver(Context context) {
+        Secure.putInt(context.getContentResolver(),
+                Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 1);
+    }
+
+    public static void scheduleAutoBatterySaver(Context context, int level) {
+        if (Global.getInt(context.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)
+                == 0) {
+            Global.putInt(context.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, level);
+        }
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 6daed35..a444ac8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2258,7 +2258,6 @@
 
 
         // Please insert new settings using the same order as in SecureSettingsProto.
-
     }
 
     private static void dumpProtoSystemSettingsLocked(
@@ -2267,15 +2266,293 @@
 
         s.dumpHistoricalOperations(p, SystemSettingsProto.HISTORICAL_OPERATIONS);
 
-        // This uses the same order as in Settings.System.
+        // This uses the same order as in SystemSettingsProto.
 
-        // Settings.System.STAY_ON_WHILE_PLUGGED_IN intentionally excluded since it's deprecated.
+        dumpSetting(s, p,
+                Settings.System.ADVANCED_SETTINGS,
+                SystemSettingsProto.ADVANCED_SETTINGS);
+
+        final long alarmToken = p.start(SystemSettingsProto.ALARM);
+        dumpSetting(s, p,
+                Settings.System.ALARM_ALERT,
+                SystemSettingsProto.Alarm.DEFAULT_URI);
+        dumpSetting(s, p,
+                Settings.System.ALARM_ALERT_CACHE,
+                SystemSettingsProto.Alarm.ALERT_CACHE);
+        // Settings.System.NEXT_ALARM_FORMATTED intentionally excluded since it's deprecated.
+        p.end(alarmToken);
+
+        final long bluetoothToken = p.start(SystemSettingsProto.BLUETOOTH);
+        dumpSetting(s, p,
+                Settings.System.BLUETOOTH_DISCOVERABILITY,
+                SystemSettingsProto.Bluetooth.DISCOVERABILITY);
+        dumpSetting(s, p,
+                Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
+                SystemSettingsProto.Bluetooth.DISCOVERABILITY_TIMEOUT_SECS);
+        p.end(bluetoothToken);
+
+        dumpSetting(s, p,
+                Settings.System.DATE_FORMAT,
+                SystemSettingsProto.DATE_FORMAT);
+        dumpSetting(s, p,
+                Settings.System.DISPLAY_COLOR_MODE,
+                SystemSettingsProto.DISPLAY_COLOR_MODE);
+
+        final long devOptionsToken = p.start(SystemSettingsProto.DEVELOPER_OPTIONS);
+        dumpSetting(s, p,
+                Settings.System.SHOW_TOUCHES,
+                SystemSettingsProto.DevOptions.SHOW_TOUCHES);
+        dumpSetting(s, p,
+                Settings.System.POINTER_LOCATION,
+                SystemSettingsProto.DevOptions.POINTER_LOCATION);
+        dumpSetting(s, p,
+                Settings.System.WINDOW_ORIENTATION_LISTENER_LOG,
+                SystemSettingsProto.DevOptions.WINDOW_ORIENTATION_LISTENER_LOG);
+        p.end(devOptionsToken);
+
+        final long dtmfToneToken = p.start(SystemSettingsProto.DTMF_TONE);
+        dumpSetting(s, p,
+                Settings.System.DTMF_TONE_WHEN_DIALING,
+                SystemSettingsProto.DtmfTone.PLAY_WHEN_DIALING);
+        dumpSetting(s, p,
+                Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
+                SystemSettingsProto.DtmfTone.TYPE_PLAYED_WHEN_DIALING);
+        p.end(dtmfToneToken);
+
+        dumpSetting(s, p,
+                Settings.System.EGG_MODE,
+                SystemSettingsProto.EGG_MODE);
         dumpSetting(s, p,
                 Settings.System.END_BUTTON_BEHAVIOR,
                 SystemSettingsProto.END_BUTTON_BEHAVIOR);
         dumpSetting(s, p,
-                Settings.System.ADVANCED_SETTINGS,
-                SystemSettingsProto.ADVANCED_SETTINGS);
+                Settings.System.FONT_SCALE,
+                SystemSettingsProto.FONT_SCALE);
+
+        final long hapticFeedbackToken = p.start(SystemSettingsProto.HAPTIC_FEEDBACK);
+        dumpSetting(s, p,
+                Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                SystemSettingsProto.HapticFeedback.ENABLED);
+        dumpSetting(s, p,
+                Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                SystemSettingsProto.HapticFeedback.INTENSITY);
+        p.end(hapticFeedbackToken);
+
+        dumpSetting(s, p,
+                Settings.System.HEARING_AID,
+                SystemSettingsProto.HEARING_AID);
+        dumpSetting(s, p,
+                Settings.System.LOCK_TO_APP_ENABLED,
+                SystemSettingsProto.LOCK_TO_APP_ENABLED);
+
+        final long lockscreenToken = p.start(SystemSettingsProto.LOCKSCREEN);
+        dumpSetting(s, p,
+                Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
+                SystemSettingsProto.Lockscreen.SOUNDS_ENABLED);
+        dumpSetting(s, p,
+                Settings.System.LOCKSCREEN_DISABLED,
+                SystemSettingsProto.Lockscreen.DISABLED);
+        p.end(lockscreenToken);
+
+        dumpSetting(s, p,
+                Settings.System.MEDIA_BUTTON_RECEIVER,
+                SystemSettingsProto.MEDIA_BUTTON_RECEIVER);
+
+        final long notificationToken = p.start(SystemSettingsProto.NOTIFICATION);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_SOUND,
+                SystemSettingsProto.Notification.SOUND);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_SOUND_CACHE,
+                SystemSettingsProto.Notification.SOUND_CACHE);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_LIGHT_PULSE,
+                SystemSettingsProto.Notification.LIGHT_PULSE);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                SystemSettingsProto.Notification.VIBRATION_INTENSITY);
+        // Settings.System.NOTIFICATIONS_USE_RING_VOLUME intentionally excluded since it's deprecated.
+        p.end(notificationToken);
+
+        dumpSetting(s, p,
+                Settings.System.POINTER_SPEED,
+                SystemSettingsProto.POINTER_SPEED);
+
+        final long ringtoneToken = p.start(SystemSettingsProto.RINGTONE);
+        dumpSetting(s, p,
+                Settings.System.RINGTONE,
+                SystemSettingsProto.Ringtone.DEFAULT_URI);
+        dumpSetting(s, p,
+                Settings.System.RINGTONE_CACHE,
+                SystemSettingsProto.Ringtone.CACHE);
+        p.end(ringtoneToken);
+
+        final long rotationToken = p.start(SystemSettingsProto.ROTATION);
+        dumpSetting(s, p,
+                Settings.System.ACCELEROMETER_ROTATION,
+                SystemSettingsProto.Rotation.ACCELEROMETER_ROTATION);
+        dumpSetting(s, p,
+                Settings.System.USER_ROTATION,
+                SystemSettingsProto.Rotation.USER_ROTATION);
+        dumpSetting(s, p,
+                Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
+                SystemSettingsProto.Rotation.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY);
+        p.end(rotationToken);
+
+        dumpSetting(s, p,
+                Settings.System.RTT_CALLING_MODE,
+                SystemSettingsProto.RTT_CALLING_MODE);
+
+        final long screenToken = p.start(SystemSettingsProto.SCREEN);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_OFF_TIMEOUT,
+                SystemSettingsProto.Screen.OFF_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS,
+                SystemSettingsProto.Screen.BRIGHTNESS);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
+                SystemSettingsProto.Screen.BRIGHTNESS_FOR_VR);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                SystemSettingsProto.Screen.BRIGHTNESS_MODE);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
+                SystemSettingsProto.Screen.AUTO_BRIGHTNESS_ADJ);
+        p.end(screenToken);
+
+        dumpSetting(s, p,
+                Settings.System.SETUP_WIZARD_HAS_RUN,
+                SystemSettingsProto.SETUP_WIZARD_HAS_RUN);
+        dumpSetting(s, p,
+                Settings.System.SHOW_BATTERY_PERCENT,
+                SystemSettingsProto.SHOW_BATTERY_PERCENT);
+        dumpSetting(s, p,
+                Settings.System.SHOW_GTALK_SERVICE_STATUS,
+                SystemSettingsProto.SHOW_GTALK_SERVICE_STATUS);
+        // Settings.System.SHOW_PROCESSES intentionally excluded since it's deprecated.
+        // Settings.System.SHOW_WEB_SUGGESTIONS intentionally excluded since it's deprecated.
+
+        final long sipToken = p.start(SystemSettingsProto.SIP);
+        dumpSetting(s, p,
+                Settings.System.SIP_RECEIVE_CALLS,
+                SystemSettingsProto.Sip.RECEIVE_CALLS);
+        dumpSetting(s, p,
+                Settings.System.SIP_CALL_OPTIONS,
+                SystemSettingsProto.Sip.CALL_OPTIONS);
+        dumpSetting(s, p,
+                Settings.System.SIP_ALWAYS,
+                SystemSettingsProto.Sip.ALWAYS);
+        dumpSetting(s, p,
+                Settings.System.SIP_ADDRESS_ONLY,
+                SystemSettingsProto.Sip.ADDRESS_ONLY);
+        // Settings.System.SIP_ASK_ME_EACH_TIME intentionally excluded since it's deprecated.
+        p.end(sipToken);
+
+        dumpSetting(s, p,
+                Settings.System.SOUND_EFFECTS_ENABLED,
+                SystemSettingsProto.SOUND_EFFECTS_ENABLED);
+        // Settings.System.POWER_SOUNDS_ENABLED intentionally excluded since it's deprecated.
+        // Settings.System.DOCK_SOUNDS_ENABLED intentionally excluded since it's deprecated.
+        // Settings.System.LOW_BATTERY_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.DESK_DOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.DESK_UNDOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.CAR_DOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.CAR_UNDOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.LOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.UNLOCK_SOUND intentionally excluded since it's deprecated.
+        dumpSetting(s, p,
+                Settings.System.SYSTEM_LOCALES,
+                SystemSettingsProto.SYSTEM_LOCALES);
+
+        final long textToken = p.start(SystemSettingsProto.TEXT);
+        dumpSetting(s, p,
+                Settings.System.TEXT_AUTO_REPLACE,
+                SystemSettingsProto.Text.AUTO_REPLACE);
+        dumpSetting(s, p,
+                Settings.System.TEXT_AUTO_CAPS,
+                SystemSettingsProto.Text.AUTO_CAPS);
+        dumpSetting(s, p,
+                Settings.System.TEXT_AUTO_PUNCTUATE,
+                SystemSettingsProto.Text.AUTO_PUNCTUATE);
+        dumpSetting(s, p,
+                Settings.System.TEXT_SHOW_PASSWORD,
+                SystemSettingsProto.Text.SHOW_PASSWORD);
+        p.end(textToken);
+
+        // Settings.System.AUTO_TIME intentionally excluded since it's deprecated.
+        // Settings.System.AUTO_TIME_ZONE intentionally excluded since it's deprecated.
+        dumpSetting(s, p,
+                Settings.System.TIME_12_24,
+                SystemSettingsProto.TIME_12_24);
+        dumpSetting(s, p,
+                Settings.System.TTY_MODE,
+                SystemSettingsProto.TTY_MODE);
+
+        final long vibrateToken = p.start(SystemSettingsProto.VIBRATE);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_ON,
+                SystemSettingsProto.Vibrate.ON);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_INPUT_DEVICES,
+                SystemSettingsProto.Vibrate.INPUT_DEVICES);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_IN_SILENT,
+                SystemSettingsProto.Vibrate.IN_SILENT);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_WHEN_RINGING,
+                SystemSettingsProto.Vibrate.WHEN_RINGING);
+        p.end(vibrateToken);
+
+        final long volumeToken = p.start(SystemSettingsProto.VOLUME);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_RING,
+                SystemSettingsProto.Volume.RING);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_SYSTEM,
+                SystemSettingsProto.Volume.SYSTEM);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_VOICE,
+                SystemSettingsProto.Volume.VOICE);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_MUSIC,
+                SystemSettingsProto.Volume.MUSIC);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_ALARM,
+                SystemSettingsProto.Volume.ALARM);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_NOTIFICATION,
+                SystemSettingsProto.Volume.NOTIFICATION);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_BLUETOOTH_SCO,
+                SystemSettingsProto.Volume.BLUETOOTH_SCO);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_ACCESSIBILITY,
+                SystemSettingsProto.Volume.ACCESSIBILITY);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_MASTER,
+                SystemSettingsProto.Volume.MASTER);
+        dumpSetting(s, p,
+                Settings.System.MASTER_MONO,
+                SystemSettingsProto.Volume.MASTER_MONO);
+        dumpSetting(s, p,
+                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+                SystemSettingsProto.Volume.MODE_RINGER_STREAMS_AFFECTED);
+        dumpSetting(s, p,
+                Settings.System.MUTE_STREAMS_AFFECTED,
+                SystemSettingsProto.Volume.MUTE_STREAMS_AFFECTED);
+        p.end(volumeToken);
+
+        dumpSetting(s, p,
+                Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
+                SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
+
+        // Please insert new settings using the same order as in SecureSettingsProto.
+
+        // The rest of the settings were moved to Settings.Secure, and are thus excluded here since
+        // they're deprecated from Settings.System.
+
+        // Settings.System.STAY_ON_WHILE_PLUGGED_IN intentionally excluded since it's deprecated.
         // Settings.System.AIRPLANE_MODE_ON intentionally excluded since it's deprecated.
         // Settings.System.RADIO_BLUETOOTH intentionally excluded since it's just a constant.
         // Settings.System.RADIO_WIFI intentionally excluded since it's just a constant.
@@ -2292,245 +2569,24 @@
         // Settings.System.WIFI_STATIC_NETMASK intentionally excluded since it's deprecated.
         // Settings.System.WIFI_STATIC_DNS1 intentionally excluded since it's deprecated.
         // Settings.System.WIFI_STATIC_DNS2 intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.BLUETOOTH_DISCOVERABILITY,
-                SystemSettingsProto.BLUETOOTH_DISCOVERABILITY);
-        dumpSetting(s, p,
-                Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
-                SystemSettingsProto.BLUETOOTH_DISCOVERABILITY_TIMEOUT);
         // Settings.System.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
         // Settings.System.LOCK_PATTERN_VISIBLE intentionally excluded since it's deprecated.
         // Settings.System.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED intentionally excluded since it's deprecated.
-        // Settings.System.NEXT_ALARM_FORMATTED intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.FONT_SCALE,
-                SystemSettingsProto.FONT_SCALE);
-        dumpSetting(s, p,
-                Settings.System.SYSTEM_LOCALES,
-                SystemSettingsProto.SYSTEM_LOCALES);
         // Settings.System.DEBUG_APP intentionally excluded since it's deprecated.
         // Settings.System.WAIT_FOR_DEBUGGER intentionally excluded since it's deprecated.
         // Settings.System.DIM_SCREEN intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.DISPLAY_COLOR_MODE,
-                SystemSettingsProto.DISPLAY_COLOR_MODE);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_OFF_TIMEOUT,
-                SystemSettingsProto.SCREEN_OFF_TIMEOUT);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_BRIGHTNESS,
-                SystemSettingsProto.SCREEN_BRIGHTNESS);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
-                SystemSettingsProto.SCREEN_BRIGHTNESS_FOR_VR);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                SystemSettingsProto.SCREEN_BRIGHTNESS_MODE);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
-                SystemSettingsProto.SCREEN_AUTO_BRIGHTNESS_ADJ);
-        // Settings.System.SHOW_PROCESSES intentionally excluded since it's deprecated.
         // Settings.System.ALWAYS_FINISH_ACTIVITIES intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
-                SystemSettingsProto.MODE_RINGER_STREAMS_AFFECTED);
-        dumpSetting(s, p,
-                Settings.System.MUTE_STREAMS_AFFECTED,
-                SystemSettingsProto.MUTE_STREAMS_AFFECTED);
-        dumpSetting(s, p,
-                Settings.System.VIBRATE_ON,
-                SystemSettingsProto.VIBRATE_ON);
-        dumpSetting(s, p,
-                Settings.System.VIBRATE_INPUT_DEVICES,
-                SystemSettingsProto.VIBRATE_INPUT_DEVICES);
-        dumpSetting(s, p,
-                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                SystemSettingsProto.NOTIFICATION_VIBRATION_INTENSITY);
-        dumpSetting(s, p,
-                Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                SystemSettingsProto.HAPTIC_FEEDBACK_INTENSITY);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_RING,
-                SystemSettingsProto.VOLUME_RING);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_SYSTEM,
-                SystemSettingsProto.VOLUME_SYSTEM);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_VOICE,
-                SystemSettingsProto.VOLUME_VOICE);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_MUSIC,
-                SystemSettingsProto.VOLUME_MUSIC);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_ALARM,
-                SystemSettingsProto.VOLUME_ALARM);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_NOTIFICATION,
-                SystemSettingsProto.VOLUME_NOTIFICATION);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_BLUETOOTH_SCO,
-                SystemSettingsProto.VOLUME_BLUETOOTH_SCO);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_ACCESSIBILITY,
-                SystemSettingsProto.VOLUME_ACCESSIBILITY);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_MASTER,
-                SystemSettingsProto.VOLUME_MASTER);
-        dumpSetting(s, p,
-                Settings.System.MASTER_MONO,
-                SystemSettingsProto.MASTER_MONO);
-        // Settings.System.NOTIFICATIONS_USE_RING_VOLUME intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.VIBRATE_IN_SILENT,
-                SystemSettingsProto.VIBRATE_IN_SILENT);
-        dumpSetting(s, p,
-                Settings.System.APPEND_FOR_LAST_AUDIBLE,
-                SystemSettingsProto.APPEND_FOR_LAST_AUDIBLE);
-        dumpSetting(s, p,
-                Settings.System.RINGTONE,
-                SystemSettingsProto.RINGTONE);
-        dumpSetting(s, p,
-                Settings.System.RINGTONE_CACHE,
-                SystemSettingsProto.RINGTONE_CACHE);
-        dumpSetting(s, p,
-                Settings.System.NOTIFICATION_SOUND,
-                SystemSettingsProto.NOTIFICATION_SOUND);
-        dumpSetting(s, p,
-                Settings.System.NOTIFICATION_SOUND_CACHE,
-                SystemSettingsProto.NOTIFICATION_SOUND_CACHE);
-        dumpSetting(s, p,
-                Settings.System.ALARM_ALERT,
-                SystemSettingsProto.ALARM_ALERT);
-        dumpSetting(s, p,
-                Settings.System.ALARM_ALERT_CACHE,
-                SystemSettingsProto.ALARM_ALERT_CACHE);
-        dumpSetting(s, p,
-                Settings.System.MEDIA_BUTTON_RECEIVER,
-                SystemSettingsProto.MEDIA_BUTTON_RECEIVER);
-        dumpSetting(s, p,
-                Settings.System.TEXT_AUTO_REPLACE,
-                SystemSettingsProto.TEXT_AUTO_REPLACE);
-        dumpSetting(s, p,
-                Settings.System.TEXT_AUTO_CAPS,
-                SystemSettingsProto.TEXT_AUTO_CAPS);
-        dumpSetting(s, p,
-                Settings.System.TEXT_AUTO_PUNCTUATE,
-                SystemSettingsProto.TEXT_AUTO_PUNCTUATE);
-        dumpSetting(s, p,
-                Settings.System.TEXT_SHOW_PASSWORD,
-                SystemSettingsProto.TEXT_SHOW_PASSWORD);
-        dumpSetting(s, p,
-                Settings.System.SHOW_GTALK_SERVICE_STATUS,
-                SystemSettingsProto.SHOW_GTALK_SERVICE_STATUS);
+        // Settings.System.APPEND_FOR_LAST_AUDIBLE intentionally excluded since it hasn't been used since API 2.
         // Settings.System.WALLPAPER_ACTIVITY intentionally excluded since it's deprecated.
-        // Settings.System.AUTO_TIME intentionally excluded since it's deprecated.
-        // Settings.System.AUTO_TIME_ZONE intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.TIME_12_24,
-                SystemSettingsProto.TIME_12_24);
-        dumpSetting(s, p,
-                Settings.System.DATE_FORMAT,
-                SystemSettingsProto.DATE_FORMAT);
-        dumpSetting(s, p,
-                Settings.System.SETUP_WIZARD_HAS_RUN,
-                SystemSettingsProto.SETUP_WIZARD_HAS_RUN);
         // Settings.System.WINDOW_ANIMATION_SCALE intentionally excluded since it's deprecated.
         // Settings.System.TRANSITION_ANIMATION_SCALE intentionally excluded since it's deprecated.
         // Settings.System.ANIMATOR_ANIMATION_SCALE intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.ACCELEROMETER_ROTATION,
-                SystemSettingsProto.ACCELEROMETER_ROTATION);
-        dumpSetting(s, p,
-                Settings.System.USER_ROTATION,
-                SystemSettingsProto.USER_ROTATION);
-        dumpSetting(s, p,
-                Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
-                SystemSettingsProto.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY);
-        dumpSetting(s, p,
-                Settings.System.VIBRATE_WHEN_RINGING,
-                SystemSettingsProto.VIBRATE_WHEN_RINGING);
-        dumpSetting(s, p,
-                Settings.System.DTMF_TONE_WHEN_DIALING,
-                SystemSettingsProto.DTMF_TONE_WHEN_DIALING);
-        dumpSetting(s, p,
-                Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
-                SystemSettingsProto.DTMF_TONE_TYPE_WHEN_DIALING);
-        dumpSetting(s, p,
-                Settings.System.HEARING_AID,
-                SystemSettingsProto.HEARING_AID);
-        dumpSetting(s, p,
-                Settings.System.TTY_MODE,
-                SystemSettingsProto.TTY_MODE);
-        dumpSetting(s, p,
-                Settings.System.RTT_CALLING_MODE,
-                SystemSettingsProto.RTT_CALLING_MODE);
-        dumpSetting(s, p,
-                Settings.System.SOUND_EFFECTS_ENABLED,
-                SystemSettingsProto.SOUND_EFFECTS_ENABLED);
-        dumpSetting(s, p,
-                Settings.System.HAPTIC_FEEDBACK_ENABLED,
-                SystemSettingsProto.HAPTIC_FEEDBACK_ENABLED);
-        // Settings.System.SHOW_WEB_SUGGESTIONS intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.NOTIFICATION_LIGHT_PULSE,
-                SystemSettingsProto.NOTIFICATION_LIGHT_PULSE);
-        dumpSetting(s, p,
-                Settings.System.POINTER_LOCATION,
-                SystemSettingsProto.POINTER_LOCATION);
-        dumpSetting(s, p,
-                Settings.System.SHOW_TOUCHES,
-                SystemSettingsProto.SHOW_TOUCHES);
-        dumpSetting(s, p,
-                Settings.System.WINDOW_ORIENTATION_LISTENER_LOG,
-                SystemSettingsProto.WINDOW_ORIENTATION_LISTENER_LOG);
-        // Settings.System.POWER_SOUNDS_ENABLED intentionally excluded since it's deprecated.
-        // Settings.System.DOCK_SOUNDS_ENABLED intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
-                SystemSettingsProto.LOCKSCREEN_SOUNDS_ENABLED);
-        dumpSetting(s, p,
-                Settings.System.LOCKSCREEN_DISABLED,
-                SystemSettingsProto.LOCKSCREEN_DISABLED);
-        // Settings.System.LOW_BATTERY_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.DESK_DOCK_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.DESK_UNDOCK_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.CAR_DOCK_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.CAR_UNDOCK_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.LOCK_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.UNLOCK_SOUND intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.SIP_RECEIVE_CALLS,
-                SystemSettingsProto.SIP_RECEIVE_CALLS);
-        dumpSetting(s, p,
-                Settings.System.SIP_CALL_OPTIONS,
-                SystemSettingsProto.SIP_CALL_OPTIONS);
-        dumpSetting(s, p,
-                Settings.System.SIP_ALWAYS,
-                SystemSettingsProto.SIP_ALWAYS);
-        dumpSetting(s, p,
-                Settings.System.SIP_ADDRESS_ONLY,
-                SystemSettingsProto.SIP_ADDRESS_ONLY);
-        // Settings.System.SIP_ASK_ME_EACH_TIME intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.POINTER_SPEED,
-                SystemSettingsProto.POINTER_SPEED);
-        dumpSetting(s, p,
-                Settings.System.LOCK_TO_APP_ENABLED,
-                SystemSettingsProto.LOCK_TO_APP_ENABLED);
-        dumpSetting(s, p,
-                Settings.System.EGG_MODE,
-                SystemSettingsProto.EGG_MODE);
-        dumpSetting(s, p,
-                Settings.System.SHOW_BATTERY_PERCENT,
-                SystemSettingsProto.SHOW_BATTERY_PERCENT);
-        dumpSetting(s, p,
-                Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
-                SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
+
         // The rest of the settings were moved to Settings.Secure, and are thus excluded here since
         // they're deprecated from Settings.System.
 
-        // Please insert new settings using the same order as in Settings.System.
-
+        // Please insert new settings using the same order as in SecureSettingsProto.
         p.end(token);
+        // Please insert new settings using the same order as in SecureSettingsProto.
     }
 }
diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml
index dca50a5..e45c0f9 100644
--- a/packages/SystemUI/res/layout/car_volume_dialog.xml
+++ b/packages/SystemUI/res/layout/car_volume_dialog.xml
@@ -24,7 +24,7 @@
     android:clipChildren="false" >
     <LinearLayout
         android:id="@+id/volume_dialog"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal|top"
         android:orientation="vertical"
@@ -53,14 +53,15 @@
         android:layout_width="wrap_content"
         android:layout_height="@dimen/car_single_line_list_item_height"
         android:gravity="center"
-        android:layout_marginEnd="@dimen/car_keyline_1">
-        <ImageButton
+        android:layout_marginEnd="@dimen/car_keyline_1"
+        android:layout_gravity="end">
+        <com.android.keyguard.AlphaOptimizedImageButton
             android:id="@+id/expand"
             android:layout_gravity="center"
             android:layout_width="@dimen/car_primary_icon_size"
             android:layout_height="@dimen/car_primary_icon_size"
-            android:layout_marginEnd="@dimen/car_keyline_1"
             android:src="@drawable/car_ic_arrow_drop_up"
+            android:background="?android:attr/selectableItemBackground"
             android:tint="@color/car_tint"
             android:scaleType="fitCenter"
         />
diff --git a/packages/SystemUI/res/layout/car_volume_dialog_row.xml b/packages/SystemUI/res/layout/car_volume_dialog_row.xml
index 14baf49..33cecfa 100644
--- a/packages/SystemUI/res/layout/car_volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/car_volume_dialog_row.xml
@@ -33,13 +33,14 @@
             android:layout_width="@dimen/car_primary_icon_size"
             android:layout_height="@dimen/car_primary_icon_size"
             android:layout_marginStart="@dimen/car_keyline_1"
+            android:background="?android:attr/selectableItemBackground"
             android:tint="@color/car_tint"
             android:scaleType="fitCenter"
             android:soundEffectsEnabled="false" />
         <SeekBar
                 android:id="@+id/volume_row_slider"
                 android:clickable="true"
-                android:layout_marginStart="@dimen/car_keyline_3"
+                android:layout_marginStart="@dimen/car_keyline_1_keyline_3_diff"
                 android:layout_marginEnd="@dimen/car_keyline_3"
                 android:layout_width="match_parent"
                 android:layout_height="@dimen/car_single_line_list_item_height"/>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index f38129f..388b633 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -31,25 +31,23 @@
 
     <com.android.systemui.statusbar.policy.Clock
         android:id="@+id/clock"
-        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:singleLine="true"
+        android:gravity="center_vertical|start"
         android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
         android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
-        android:gravity="center_vertical|start"
-        systemui:showDark="false"
-    />
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+        systemui:showDark="false" />
 
     <com.android.systemui.statusbar.policy.DateView
         android:id="@+id/date"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:padding="4dp"
+        android:gravity="center_vertical"
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
         android:textSize="@dimen/qs_time_collapsed_size"
-        android:gravity="center_vertical"
         systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
 
     <android.widget.Space
@@ -57,12 +55,11 @@
         android:layout_width="0dp"
         android:layout_height="match_parent"
         android:layout_weight="1"
-        android:gravity="center_vertical|center_horizontal"
-    />
+        android:gravity="center_vertical|center_horizontal" />
 
-    <com.android.systemui.BatteryMeterView android:id="@+id/battery"
+    <com.android.systemui.BatteryMeterView
+        android:id="@+id/battery"
         android:layout_height="match_parent"
         android:layout_width="wrap_content"
-        android:gravity="center_vertical|end"
-        />
+        android:gravity="center_vertical|end" />
 </LinearLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f76603b..711d550 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -45,9 +45,6 @@
      interface.  This name is in the ComponentName flattened format (package/class)  -->
     <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
 
-    <!-- Component name of launcher service for overview to connect to -->
-    <string name="config_overviewServiceComponent" translatable="false">com.android.launcher3/com.android.quickstep.TouchInteractionService</string>
-
     <!-- Whether or not we show the number in the bar. -->
     <bool name="config_statusBarShowNumber">false</bool>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 909c18b..8c00af6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2096,6 +2096,8 @@
     <string name="notification_channel_general">General Messages</string>
     <!-- Title for the notification channel for problems with storage (i.e. low disk). [CHAR LIMIT=NONE] -->
     <string name="notification_channel_storage">Storage</string>
+    <!-- Title for the notification channel for hints and suggestions. [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_hints">Hints</string>
 
     <!-- App label of the instant apps notification [CHAR LIMIT=60] -->
     <string name="instant_apps">Instant Apps</string>
@@ -2186,4 +2188,25 @@
     <string-array name="recents_onboarding_blacklisted_packages" translatable="false">
     </string-array>
 
+    <!-- The title of the notification to suggest enabling automatic battery saver.  [CHAR LIMIT=NONE]-->
+    <string name="auto_saver_title">Tap to schedule Battery Saver</string>
+
+    <!-- The content of the notification to suggest enabling automatic battery saver.  [CHAR LIMIT=NONE]-->
+    <string name="auto_saver_text">Turn on automatically when battery is at <xliff:g id="percentage">%d</xliff:g>%%</string>
+
+    <!-- An action on the notification to suggest enabling automatic battery saver: Do not turn on automatic battery saver.  [CHAR LIMIT=NONE]-->
+    <string name="no_auto_saver_action">No thanks</string>
+
+    <!-- The title of the dialog that tells that scheduled (i.e. automatic) battery saver has been turned on. [CHAR LIMIT=NONE]-->
+    <string name="auto_saver_enabled_title">Battery Saver schedule turned on</string>
+
+    <!-- The content of the dialog that tells that scheduled (i.e. automatic) battery saver has been turned on. [CHAR LIMIT=NONE]-->
+    <string name="auto_saver_enabled_text">Battery Saver will turn on automatically once battery goes below <xliff:g id="percentage">%d</xliff:g>%%.</string>
+
+    <!-- An action on the dialog that tells that scheduled (i.e. automatic) battery saver: open the battery saver setting.  [CHAR LIMIT=NONE]-->
+    <string name="open_saver_setting_action">Settings</string>
+
+    <!-- An action on the dialog that tells that scheduled (i.e. automatic) battery saver: user acknowledges and closes the dialog.  [CHAR LIMIT=NONE]-->
+    <string name="auto_saver_okay_action">Got it</string>
+
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
index 14767f1..20d1418 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
@@ -106,11 +107,12 @@
             return createDrawableFromBitmap(tdIcon, userId, desc);
         }
         if (desc.getIconResource() != 0) {
-            // TODO: Use task context here
             try {
-                return createBadgedDrawable(
-                        mContext.getDrawable(desc.getIconResource()), userId, desc);
-            } catch (Resources.NotFoundException e) {
+                Context packageContext = mContext.createPackageContextAsUser(
+                        taskKey.getPackageName(), 0, UserHandle.of(userId));
+                return createBadgedDrawable(packageContext.getDrawable(desc.getIconResource()),
+                        userId, desc);
+            } catch (Resources.NotFoundException|PackageManager.NameNotFoundException e) {
                 Log.e(TAG, "Could not find icon drawable from resource", e);
             }
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 6bddbe0..6af89fc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -80,6 +80,13 @@
             return this.baseIntent.getComponent();
         }
 
+        public String getPackageName() {
+            if (this.baseIntent.getComponent() != null) {
+                return this.baseIntent.getComponent().getPackageName();
+            }
+            return this.baseIntent.getPackage();
+        }
+
         @Override
         public boolean equals(Object o) {
             if (!(o instanceof TaskKey)) {
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 1ae06d7..0683514 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -81,6 +81,14 @@
     private float mDarkIntensity;
     private int mUser;
 
+    /**
+     * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings.
+     */
+    private boolean mUseWallpaperTextColors;
+
+    private int mNonAdaptedForegroundColor;
+    private int mNonAdaptedBackgroundColor;
+
     public BatteryMeterView(Context context) {
         this(context, null, 0);
     }
@@ -140,6 +148,29 @@
         updateShowPercent();
     }
 
+    /**
+     * Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll
+     * revert back to dark-mode-based/tinted colors.
+     *
+     * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for all
+     *                                    components
+     */
+    public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
+        if (shouldUseWallpaperTextColor == mUseWallpaperTextColors) {
+            return;
+        }
+
+        mUseWallpaperTextColors = shouldUseWallpaperTextColor;
+
+        if (mUseWallpaperTextColors) {
+            updateColors(
+                    Utils.getColorAttr(mContext, R.attr.wallpaperTextColor),
+                    Utils.getColorAttr(mContext, R.attr.wallpaperTextColorSecondary));
+        } else {
+            updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor);
+        }
+    }
+
     public void setColorsFromContext(Context context) {
         if (context == null) {
             return;
@@ -179,7 +210,8 @@
         getContext().getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
         updateShowPercent();
-        Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+        Dependency.get(TunerService.class)
+                .addTunable(this, StatusBarIconController.ICON_BLACKLIST);
         Dependency.get(ConfigurationController.class).addCallback(this);
         mUserTracker.startTracking();
     }
@@ -273,19 +305,23 @@
     @Override
     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
         mDarkIntensity = darkIntensity;
+
         float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0;
-        int foreground = getColorForDarkIntensity(intensity, mLightModeFillColor,
-                mDarkModeFillColor);
-        int background = getColorForDarkIntensity(intensity, mLightModeBackgroundColor,
-                mDarkModeBackgroundColor);
-        mDrawable.setColors(foreground, background);
-        setTextColor(foreground);
+        mNonAdaptedForegroundColor = getColorForDarkIntensity(
+                intensity, mLightModeFillColor, mDarkModeFillColor);
+        mNonAdaptedBackgroundColor = getColorForDarkIntensity(
+                intensity, mLightModeBackgroundColor,mDarkModeBackgroundColor);
+
+        if (!mUseWallpaperTextColors) {
+            updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor);
+        }
     }
 
-    public void setTextColor(int color) {
-        mTextColor = color;
+    private void updateColors(int foregroundColor, int backgroundColor) {
+        mDrawable.setColors(foregroundColor, backgroundColor);
+        mTextColor = foregroundColor;
         if (mBatteryPercentView != null) {
-            mBatteryPercentView.setTextColor(color);
+            mBatteryPercentView.setTextColor(foregroundColor);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index a4af6b2..2983df6 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -57,6 +57,8 @@
  */
 public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
 
+    private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
+
     public static final String TAG_OPS = "OverviewProxyService";
     public static final boolean DEBUG_OVERVIEW_PROXY = false;
     private static final long BACKOFF_MILLIS = 5000;
@@ -64,7 +66,7 @@
     private final Context mContext;
     private final Handler mHandler;
     private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
-    private final ComponentName mLauncherComponentName;
+    private final ComponentName mRecentsComponentName;
     private final DeviceProvisionedController mDeviceProvisionedController
             = Dependency.get(DeviceProvisionedController.class);
     private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
@@ -191,8 +193,8 @@
         mContext = context;
         mHandler = new Handler();
         mConnectionBackoffAttempts = 0;
-        mLauncherComponentName = ComponentName
-                .unflattenFromString(context.getString(R.string.config_overviewServiceComponent));
+        mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
+                com.android.internal.R.string.config_recentsComponentName));
 
         // Listen for the package update changes.
         if (SystemServicesProxy.getInstance(context)
@@ -200,7 +202,7 @@
             mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
             filter.addDataScheme("package");
-            filter.addDataSchemeSpecificPart(mLauncherComponentName.getPackageName(),
+            filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(),
                     PatternMatcher.PATTERN_LITERAL);
             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
             mContext.registerReceiver(mLauncherAddedReceiver, filter);
@@ -223,8 +225,8 @@
             return;
         }
         mHandler.removeCallbacks(mConnectionRunnable);
-        Intent launcherServiceIntent = new Intent();
-        launcherServiceIntent.setComponent(mLauncherComponentName);
+        Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
+                .setPackage(mRecentsComponentName.getPackageName());
         boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
                 mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
                 UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 391843c..039e7b5 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -31,6 +31,7 @@
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.NotificationEntryManager;
 import com.android.systemui.statusbar.NotificationGutsManager;
 import com.android.systemui.statusbar.NotificationListener;
@@ -130,6 +131,8 @@
         providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
         providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
         providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
+        providers.put(NotificationBlockingHelperManager.class,
+                () -> new NotificationBlockingHelperManager(context));
         providers.put(NotificationRemoteInputManager.class,
                 () -> new NotificationRemoteInputManager(context));
         providers.put(SmartReplyConstants.class,
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
index ebdc703..05906f7 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
@@ -55,7 +55,8 @@
 
     private static final String TAG = "FingerprintDialogView";
 
-    private static final int ANIMATION_DURATION = 250; // ms
+    private static final int ANIMATION_DURATION_SHOW = 250; // ms
+    private static final int ANIMATION_DURATION_AWAY = 350; // ms
 
     private static final int STATE_NONE = 0;
     private static final int STATE_FINGERPRINT = 1;
@@ -164,8 +165,23 @@
 
         title.setText(mBundle.getCharSequence(FingerprintDialog.KEY_TITLE));
         title.setSelected(true);
-        subtitle.setText(mBundle.getCharSequence(FingerprintDialog.KEY_SUBTITLE));
-        description.setText(mBundle.getCharSequence(FingerprintDialog.KEY_DESCRIPTION));
+
+        final CharSequence subtitleText = mBundle.getCharSequence(FingerprintDialog.KEY_SUBTITLE);
+        if (subtitleText == null) {
+            subtitle.setVisibility(View.GONE);
+        } else {
+            subtitle.setVisibility(View.VISIBLE);
+            subtitle.setText(subtitleText);
+        }
+
+        final CharSequence descriptionText = mBundle.getCharSequence(FingerprintDialog.KEY_DESCRIPTION);
+        if (descriptionText == null) {
+            subtitle.setVisibility(View.VISIBLE);
+            description.setVisibility(View.GONE);
+        } else {
+            description.setText(mBundle.getCharSequence(FingerprintDialog.KEY_DESCRIPTION));
+        }
+
         negative.setText(mBundle.getCharSequence(FingerprintDialog.KEY_NEGATIVE_TEXT));
 
         final CharSequence positiveText =
@@ -185,13 +201,13 @@
             public void run() {
                 mLayout.animate()
                         .alpha(1f)
-                        .setDuration(ANIMATION_DURATION)
+                        .setDuration(ANIMATION_DURATION_SHOW)
                         .setInterpolator(mLinearOutSlowIn)
                         .withLayer()
                         .start();
                 mDialog.animate()
                         .translationY(0)
-                        .setDuration(ANIMATION_DURATION)
+                        .setDuration(ANIMATION_DURATION_SHOW)
                         .setInterpolator(mLinearOutSlowIn)
                         .withLayer()
                         .start();
@@ -221,13 +237,13 @@
             public void run() {
                 mLayout.animate()
                         .alpha(0f)
-                        .setDuration(ANIMATION_DURATION)
+                        .setDuration(ANIMATION_DURATION_AWAY)
                         .setInterpolator(mLinearOutSlowIn)
                         .withLayer()
                         .start();
                 mDialog.animate()
                         .translationY(mAnimationTranslationOffset)
-                        .setDuration(ANIMATION_DURATION)
+                        .setDuration(ANIMATION_DURATION_AWAY)
                         .setInterpolator(mLinearOutSlowIn)
                         .withLayer()
                         .withEndAction(endActionRunnable)
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 8d93157..49b00ce 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -21,9 +21,6 @@
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.DialogInterface.OnDismissListener;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.media.AudioAttributes;
@@ -52,15 +49,18 @@
 
     private static final String TAG_BATTERY = "low_battery";
     private static final String TAG_TEMPERATURE = "high_temp";
+    private static final String TAG_AUTO_SAVER = "auto_saver";
 
     private static final int SHOWING_NOTHING = 0;
     private static final int SHOWING_WARNING = 1;
     private static final int SHOWING_INVALID_CHARGER = 3;
+    private static final int SHOWING_AUTO_SAVER_SUGGESTION = 4;
     private static final String[] SHOWING_STRINGS = {
         "SHOWING_NOTHING",
         "SHOWING_WARNING",
         "SHOWING_SAVER",
         "SHOWING_INVALID_CHARGER",
+        "SHOWING_AUTO_SAVER_SUGGESTION",
     };
 
     private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings";
@@ -74,6 +74,18 @@
             "PNW.dismissedThermalShutdownWarning";
     private static final String ACTION_SHOW_START_SAVER_CONFIRMATION =
             BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION;
+    private static final String ACTION_SHOW_AUTO_SAVER_SUGGESTION =
+            BatterySaverUtils.ACTION_SHOW_AUTO_SAVER_SUGGESTION;
+    private static final String ACTION_DISMISS_AUTO_SAVER_SUGGESTION =
+            "PNW.dismissAutoSaverSuggestion";
+
+    private static final String ACTION_ENABLE_AUTO_SAVER =
+            "PNW.enableAutoSaver";
+    private static final String ACTION_AUTO_SAVER_NO_THANKS =
+            "PNW.autoSaverNoThanks";
+
+    private static final String SETTINGS_ACTION_OPEN_BATTERY_SAVER_SETTING =
+            "android.settings.BATTERY_SAVER_SETTINGS";
 
     private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -98,9 +110,11 @@
     private long mLowWarningThreshold;
     private long mSevereWarningThreshold;
     private boolean mWarning;
+    private boolean mShowAutoSaverSuggestion;
     private boolean mPlaySound;
     private boolean mInvalidCharger;
     private SystemUIDialog mSaverConfirmation;
+    private SystemUIDialog mSaverEnabledConfirmation;
     private boolean mHighTempWarning;
     private SystemUIDialog mHighTempDialog;
     private SystemUIDialog mThermalShutdownDialog;
@@ -119,12 +133,19 @@
         pw.print("mInvalidCharger="); pw.println(mInvalidCharger);
         pw.print("mShowing="); pw.println(SHOWING_STRINGS[mShowing]);
         pw.print("mSaverConfirmation="); pw.println(mSaverConfirmation != null ? "not null" : null);
+        pw.print("mSaverEnabledConfirmation=");
+        pw.println(mSaverEnabledConfirmation != null ? "not null" : null);
         pw.print("mHighTempWarning="); pw.println(mHighTempWarning);
         pw.print("mHighTempDialog="); pw.println(mHighTempDialog != null ? "not null" : null);
         pw.print("mThermalShutdownDialog=");
         pw.println(mThermalShutdownDialog != null ? "not null" : null);
     }
 
+    private int getLowBatteryAutoTriggerDefaultLevel() {
+        return mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryAutoTriggerDefaultLevel);
+    }
+
     @Override
     public void update(int batteryLevel, int bucket, long screenOffTime) {
         mBatteryLevel = batteryLevel;
@@ -151,7 +172,6 @@
         mSevereWarningThreshold = severeThreshold;
     }
 
-
     private void updateNotification() {
         if (DEBUG) Slog.d(TAG, "updateNotification mWarning=" + mWarning + " mPlaySound="
                 + mPlaySound + " mInvalidCharger=" + mInvalidCharger);
@@ -161,9 +181,14 @@
         } else if (mWarning) {
             showWarningNotification();
             mShowing = SHOWING_WARNING;
+        } else if (mShowAutoSaverSuggestion) {
+            showAutoSaverSuggestionNotification();
+            mShowing = SHOWING_AUTO_SAVER_SUGGESTION;
         } else {
             mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, UserHandle.ALL);
             mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, UserHandle.ALL);
+            mNoMan.cancelAsUser(TAG_AUTO_SAVER,
+                    SystemMessage.NOTE_AUTO_SAVER_SUGGESTION, UserHandle.ALL);
             mShowing = SHOWING_NOTHING;
         }
     }
@@ -229,6 +254,28 @@
         mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
     }
 
+    private void showAutoSaverSuggestionNotification() {
+        final Notification.Builder nb =
+                new Notification.Builder(mContext, NotificationChannels.HINTS)
+                        .setSmallIcon(R.drawable.ic_power_saver)
+                        .setWhen(0)
+                        .setShowWhen(false)
+                        .setContentTitle(mContext.getString(R.string.auto_saver_title))
+                        .setContentText(mContext.getString(R.string.auto_saver_text,
+                                getLowBatteryAutoTriggerDefaultLevel()));
+        nb.setContentIntent(pendingBroadcast(ACTION_ENABLE_AUTO_SAVER));
+        nb.setDeleteIntent(pendingBroadcast(ACTION_DISMISS_AUTO_SAVER_SUGGESTION));
+        nb.addAction(0,
+                mContext.getString(R.string.no_auto_saver_action),
+                pendingBroadcast(ACTION_AUTO_SAVER_NO_THANKS));
+
+        SystemUI.overrideNotificationAppName(mContext, nb, false);
+
+        final Notification n = nb.build();
+        mNoMan.notifyAsUser(
+                TAG_AUTO_SAVER, SystemMessage.NOTE_AUTO_SAVER_SUGGESTION, n, UserHandle.ALL);
+    }
+
     private String getHybridContentString(String percentage) {
         return PowerUtil.getBatteryRemainingStringFormatted(
             mContext,
@@ -238,8 +285,8 @@
     }
 
     private PendingIntent pendingBroadcast(String action) {
-        return PendingIntent.getBroadcastAsUser(mContext,
-                0, new Intent(action), 0, UserHandle.CURRENT);
+        return PendingIntent.getBroadcastAsUser(mContext, 0,
+                new Intent(action).setPackage(mContext.getPackageName()), 0, UserHandle.CURRENT);
     }
 
     private static Intent settings(String action) {
@@ -394,6 +441,16 @@
         updateNotification();
     }
 
+    private void showAutoSaverSuggestion() {
+        mShowAutoSaverSuggestion = true;
+        updateNotification();
+    }
+
+    private void dismissAutoSaverSuggestion() {
+        mShowAutoSaverSuggestion = false;
+        updateNotification();
+    }
+
     @Override
     public void userSwitched() {
         updateNotification();
@@ -405,22 +462,53 @@
         d.setTitle(R.string.battery_saver_confirmation_title);
         d.setMessage(com.android.internal.R.string.battery_saver_description);
         d.setNegativeButton(android.R.string.cancel, null);
-        d.setPositiveButton(R.string.battery_saver_confirmation_ok, mStartSaverModeNoConfirmation);
+        d.setPositiveButton(R.string.battery_saver_confirmation_ok,
+                (dialog, which) -> setSaverMode(true, false));
         d.setShowForAllUsers(true);
-        d.setOnDismissListener(new OnDismissListener() {
-            @Override
-            public void onDismiss(DialogInterface dialog) {
-                mSaverConfirmation = null;
-            }
-        });
+        d.setOnDismissListener((dialog) -> mSaverConfirmation = null);
         d.show();
         mSaverConfirmation = d;
     }
 
+    private void showAutoSaverEnabledConfirmation() {
+        if (mSaverEnabledConfirmation != null) return;
+
+        // Open the Battery Saver setting page.
+        final Intent actionBatterySaverSetting =
+                new Intent(SETTINGS_ACTION_OPEN_BATTERY_SAVER_SETTING)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        final SystemUIDialog d = new SystemUIDialog(mContext);
+        d.setTitle(R.string.auto_saver_enabled_title);
+        d.setMessage(mContext.getString(R.string.auto_saver_enabled_text,
+                getLowBatteryAutoTriggerDefaultLevel()));
+
+        // Negative == "got it". Just close the dialog. Battery saver has already been enabled.
+        d.setNegativeButton(R.string.auto_saver_okay_action, null);
+        d.setPositiveButton(R.string.open_saver_setting_action, (dialog, which) ->
+                mContext.startActivity(actionBatterySaverSetting));
+        d.setShowForAllUsers(true);
+        d.setOnDismissListener((dialog) -> mSaverEnabledConfirmation = null);
+        d.show();
+        mSaverEnabledConfirmation = d;
+    }
+
+
     private void setSaverMode(boolean mode, boolean needFirstTimeWarning) {
         BatterySaverUtils.setPowerSaveMode(mContext, mode, needFirstTimeWarning);
     }
 
+    private void scheduleAutoBatterySaver() {
+        int autoTriggerThreshold = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryWarningLevel);
+        if (autoTriggerThreshold == 0) {
+            autoTriggerThreshold = 15;
+        }
+
+        BatterySaverUtils.scheduleAutoBatterySaver(mContext, autoTriggerThreshold);
+        showAutoSaverEnabledConfirmation();
+    }
+
     private final class Receiver extends BroadcastReceiver {
 
         public void init() {
@@ -433,6 +521,9 @@
             filter.addAction(ACTION_CLICKED_THERMAL_SHUTDOWN_WARNING);
             filter.addAction(ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING);
             filter.addAction(ACTION_SHOW_START_SAVER_CONFIRMATION);
+            filter.addAction(ACTION_SHOW_AUTO_SAVER_SUGGESTION);
+            filter.addAction(ACTION_ENABLE_AUTO_SAVER);
+            filter.addAction(ACTION_AUTO_SAVER_NO_THANKS);
             mContext.registerReceiverAsUser(this, UserHandle.ALL, filter,
                     android.Manifest.permission.DEVICE_POWER, mHandler);
         }
@@ -462,10 +553,17 @@
                 showThermalShutdownDialog();
             } else if (ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING.equals(action)) {
                 dismissThermalShutdownWarning();
+            } else if (ACTION_SHOW_AUTO_SAVER_SUGGESTION.equals(action)) {
+                showAutoSaverSuggestion();
+            } else if (ACTION_DISMISS_AUTO_SAVER_SUGGESTION.equals(action)) {
+                dismissAutoSaverSuggestion();
+            } else if (ACTION_ENABLE_AUTO_SAVER.equals(action)) {
+                dismissAutoSaverSuggestion();
+                scheduleAutoBatterySaver();
+            } else if (ACTION_AUTO_SAVER_NO_THANKS.equals(action)) {
+                dismissAutoSaverSuggestion();
+                BatterySaverUtils.suppressAutoBatterySaver(context);
             }
         }
     }
-
-    private final OnClickListener mStartSaverModeNoConfirmation =
-            (dialog, which) -> setSaverMode(true, false);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index bfbfbf6..7161463 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -18,17 +18,15 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.graphics.Canvas;
-import android.graphics.Path;
 import android.graphics.Point;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.widget.FrameLayout;
 
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.qs.customize.QSCustomizer;
-import com.android.systemui.statusbar.ExpandableOutlineView;
 
 /**
  * Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
@@ -44,7 +42,11 @@
     protected float mQsExpansion;
     private QSCustomizer mQSCustomizer;
     private View mQSFooter;
+
     private View mBackground;
+    private View mBackgroundGradient;
+    private View mStatusBarBackground;
+
     private int mSideMargins;
 
     public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -60,6 +62,8 @@
         mQSCustomizer = findViewById(R.id.qs_customize);
         mQSFooter = findViewById(R.id.qs_footer);
         mBackground = findViewById(R.id.quick_settings_background);
+        mStatusBarBackground = findViewById(R.id.quick_settings_status_bar_background);
+        mBackgroundGradient = findViewById(R.id.quick_settings_gradient_view);
         mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
 
         setClickable(true);
@@ -68,6 +72,22 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        // Hide the backgrounds when in landscape mode.
+        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            mBackgroundGradient.setVisibility(View.INVISIBLE);
+            mStatusBarBackground.setVisibility(View.INVISIBLE);
+        } else {
+            mBackgroundGradient.setVisibility(View.VISIBLE);
+            mStatusBarBackground.setVisibility(View.VISIBLE);
+        }
+
+        updateResources();
+    }
+
+    @Override
     public boolean performClick() {
         // Want to receive clicks so missing QQS tiles doesn't cause collapse, but
         // don't want to do anything with them.
@@ -101,6 +121,14 @@
         updateExpansion();
     }
 
+    private void updateResources() {
+        LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
+        layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.quick_qs_offset_height);
+
+        mQSPanel.setLayoutParams(layoutParams);
+    }
+
     /**
      * Overrides the height of this view (post-layout), so that the content is clipped to that
      * height and the background is set to that height.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 9792e41..eb779a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Handler;
@@ -52,8 +53,10 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
+import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.policy.DateView;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 
 import java.util.Locale;
@@ -88,6 +91,7 @@
     private TouchAnimator mStatusIconsAlphaAnimator;
     private TouchAnimator mHeaderTextContainerAlphaAnimator;
 
+    private View mSystemIconsView;
     private View mQuickQsStatusIcons;
     private View mDate;
     private View mHeaderTextContainerView;
@@ -97,6 +101,9 @@
     private View mLongPressTooltipView;
     /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
     private TextView mNextAlarmTextView;
+    private BatteryMeterView mBatteryMeterView;
+    private Clock mClockView;
+    private DateView mDateView;
 
     private NextAlarmController mAlarmController;
     private String mNextAlarmText;
@@ -122,6 +129,7 @@
         mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
         mDate = findViewById(R.id.date);
         mDate.setOnClickListener(this);
+        mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
         mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
         mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
 
@@ -145,8 +153,10 @@
         // Set the correct tint for the status icons so they contrast
         mIconManager.setTint(fillColor);
 
-        BatteryMeterView battery = findViewById(R.id.battery);
-        battery.setForceShowPercent(true);
+        mBatteryMeterView = findViewById(R.id.battery);
+        mBatteryMeterView.setForceShowPercent(true);
+        mClockView = findViewById(R.id.clock);
+        mDateView = findViewById(R.id.date);
     }
 
     private void applyDarkness(int id, Rect tintArea, float intensity, int color) {
@@ -167,6 +177,13 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         updateResources();
+
+        // Update color schemes in landscape to use wallpaperTextColor
+        boolean shouldUseWallpaperTextColor =
+                newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
+        mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor);
+        mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
+        mDateView.useWallpaperTextColor(shouldUseWallpaperTextColor);
     }
 
     @Override
@@ -176,11 +193,21 @@
     }
 
     private void updateResources() {
-        // Update height, especially due to landscape mode restricting space.
+        Resources resources = mContext.getResources();
+
+        // Update height for a few views, especially due to landscape mode restricting space.
         mHeaderTextContainerView.getLayoutParams().height =
-                mContext.getResources().getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
+                resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
         mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
 
+        mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize(
+                com.android.internal.R.dimen.quick_qs_offset_height);
+        mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
+
+        getLayoutParams().height =
+                resources.getDimensionPixelSize(com.android.internal.R.dimen.quick_qs_total_height);
+        setLayoutParams(getLayoutParams());
+
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
     }
@@ -449,9 +476,8 @@
         mHeaderQsPanel.setHost(host, null /* No customization in header */);
 
         // Use SystemUI context to get battery meter colors, and let it use the default tint (white)
-        BatteryMeterView battery = findViewById(R.id.battery);
-        battery.setColorsFromContext(mHost.getContext());
-        battery.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
+        mBatteryMeterView.setColorsFromContext(mHost.getContext());
+        mBatteryMeterView.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
     }
 
     public void setCallback(Callback qsPanelCallback) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index ebc7b94..05a5a8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
 
@@ -34,7 +35,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.AttributeSet;
@@ -127,6 +127,8 @@
     private boolean mHasUserChangedExpansion;
     /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
     private boolean mUserExpanded;
+    /** Whether the blocking helper is showing on this notification (even if dismissed) */
+    private boolean mIsBlockingHelperShowing;
 
     /**
      * Has this notification been expanded while it was pinned
@@ -400,8 +402,7 @@
         updateIconVisibilities();
         updateShelfIconColor();
 
-        showBlockingHelper(mEntry.userSentiment ==
-                NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+        showBlockingHelperButton(mEntry.userSentiment == USER_SENTIMENT_NEGATIVE);
         updateRippleAllowed();
     }
 
@@ -594,6 +595,13 @@
         return mNotificationParent != null;
     }
 
+    /**
+     * @return whether this notification is the only child in the group summary
+     */
+    public boolean isOnlyChildInGroup() {
+        return mGroupManager.isOnlyChildInGroup(getStatusBarNotification());
+    }
+
     public ExpandableNotificationRow getNotificationParent() {
         return mNotificationParent;
     }
@@ -1150,11 +1158,31 @@
         return mGroupParentWhenDismissed;
     }
 
+    /**
+     * Dismisses the notification with the option of showing the blocking helper in-place if we have
+     * a negative user sentiment.
+     *
+     * @param fromAccessibility whether this dismiss is coming from an accessibility action
+     * @return whether a blocking helper is shown in this row
+     */
+    public boolean performDismissWithBlockingHelper(boolean fromAccessibility) {
+        NotificationBlockingHelperManager manager =
+                Dependency.get(NotificationBlockingHelperManager.class);
+        boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);
+
+        // Continue with dismiss since we don't want the blocking helper to be directly associated
+        // with a certain notification.
+        performDismiss(fromAccessibility);
+        return isBlockingHelperShown;
+    }
+
     public void performDismiss(boolean fromAccessibility) {
-        if (mGroupManager.isOnlyChildInGroup(getStatusBarNotification())) {
+        if (isOnlyChildInGroup()) {
             ExpandableNotificationRow groupSummary =
                     mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
             if (groupSummary.isClearable()) {
+                // If this is the only child in the group, dismiss the group, but don't try to show
+                // the blocking helper affordance!
                 groupSummary.performDismiss(fromAccessibility);
             }
         }
@@ -1166,6 +1194,14 @@
         }
     }
 
+    public void setBlockingHelperShowing(boolean isBlockingHelperShowing) {
+        mIsBlockingHelperShowing = isBlockingHelperShowing;
+    }
+
+    public boolean isBlockingHelperShowing() {
+        return mIsBlockingHelperShowing;
+    }
+
     public void setOnDismissRunnable(Runnable onDismissRunnable) {
         mOnDismissRunnable = onDismissRunnable;
     }
@@ -1390,7 +1426,7 @@
         requestLayout();
     }
 
-    public void showBlockingHelper(boolean show) {
+    public void showBlockingHelperButton(boolean show) {
         mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
     }
 
@@ -1423,7 +1459,6 @@
         mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
         mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
 
-        final NotificationGutsManager gutsMan = Dependency.get(NotificationGutsManager.class);
         mHelperButton = findViewById(R.id.helper);
         mHelperButton.setOnClickListener(view -> {
             doLongClickCallback();
@@ -2526,7 +2561,7 @@
         }
         switch (action) {
             case AccessibilityNodeInfo.ACTION_DISMISS:
-                performDismiss(true /* fromAccessibility */);
+                performDismissWithBlockingHelper(true /* fromAccessibility */);
                 return true;
             case AccessibilityNodeInfo.ACTION_COLLAPSE:
             case AccessibilityNodeInfo.ACTION_EXPAND:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java
new file mode 100644
index 0000000..c9c1bc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java
@@ -0,0 +1,137 @@
+/*
+ * 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.statusbar;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+
+/**
+ * Manager for the notification blocking helper - tracks and helps create the blocking helper
+ * affordance.
+ */
+public class NotificationBlockingHelperManager {
+    /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
+    private static final boolean DEBUG = false;
+    private static final String TAG = "BlockingHelper";
+
+    private final Context mContext;
+    /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
+    private ExpandableNotificationRow mBlockingHelperRow;
+
+    /**
+     * Whether the notification shade/stack is expanded - used to determine blocking helper
+     * eligibility.
+     */
+    private boolean mIsShadeExpanded;
+
+    public NotificationBlockingHelperManager(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Potentially shows the blocking helper, represented via the {@link NotificationInfo} menu
+     * item, in the current row if user sentiment is negative.
+     *
+     * @param row row to render the blocking helper in
+     * @param menuRow menu used to generate the {@link NotificationInfo} view that houses the
+     *                blocking helper UI
+     * @return whether we're showing a blocking helper in the given notification row
+     */
+    boolean perhapsShowBlockingHelper(
+            ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow) {
+        int numChildren = row.getNumberOfNotificationChildren();
+
+        // We only show the blocking helper if:
+        // - The dismissed row is a valid group (>1 or 0 children) or the only child in the group
+        // - The notification shade is fully expanded (guarantees we're not touching a HUN).
+        // - User sentiment is negative
+        if (DEBUG
+                || row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE
+                && mIsShadeExpanded
+                && (!row.isChildInGroup() || row.isOnlyChildInGroup())) {
+            // Dismiss any current blocking helper before continuing forward (only one can be shown
+            // at a given time).
+            dismissCurrentBlockingHelper();
+
+            if (DEBUG) {
+                Log.d(TAG, "Manager.perhapsShowBlockingHelper: Showing new blocking helper");
+            }
+            NotificationGutsManager manager = Dependency.get(NotificationGutsManager.class);
+
+            // Enable blocking helper on the row before moving forward so everything in the guts is
+            // correctly prepped.
+            mBlockingHelperRow = row;
+            mBlockingHelperRow.setBlockingHelperShowing(true);
+
+            // We don't care about the touch origin (x, y) since we're opening guts without any
+            // explicit user interaction.
+            manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Dismiss the currently showing blocking helper, if any, through a notification update.
+     *
+     * @return whether the blocking helper was dismissed
+     */
+    boolean dismissCurrentBlockingHelper() {
+        if (!isBlockingHelperRowNull()) {
+            if (DEBUG) {
+                Log.d(TAG, "Manager.dismissCurrentBlockingHelper: Dismissing current helper");
+            }
+            if (!mBlockingHelperRow.isBlockingHelperShowing()) {
+                Log.e(TAG, "Manager.dismissCurrentBlockingHelper: "
+                        + "Non-null row is not showing a blocking helper");
+            }
+
+            mBlockingHelperRow.setBlockingHelperShowing(false);
+            if (mBlockingHelperRow.isAttachedToWindow()) {
+                Dependency.get(NotificationEntryManager.class).updateNotifications();
+            }
+            mBlockingHelperRow = null;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Update the expansion status of the notification shade/stack.
+     *
+     * @param expandedHeight how much the shade is expanded ({code 0} indicating it's collapsed)
+     */
+    public void setNotificationShadeExpanded(float expandedHeight) {
+        mIsShadeExpanded = expandedHeight > 0.0f;
+    }
+
+    @VisibleForTesting
+    boolean isBlockingHelperRowNull() {
+        return mBlockingHelperRow == null;
+    }
+
+    @VisibleForTesting
+    void setBlockingHelperRowForTest(ExpandableNotificationRow blockingHelperRowForTest) {
+        mBlockingHelperRow = blockingHelperRowForTest;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 52776d7..bc572a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -30,6 +30,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -189,8 +190,12 @@
     }
 
     public void openControls(
-            int x, int y, boolean needsFalsingProtection, @Nullable Runnable onAnimationEnd) {
-        animateOpen(x, y, onAnimationEnd);
+            boolean shouldDoCircularReveal,
+            int x,
+            int y,
+            boolean needsFalsingProtection,
+            @Nullable Runnable onAnimationEnd) {
+        animateOpen(shouldDoCircularReveal, x, y, onAnimationEnd);
         setExposed(true /* exposed */, needsFalsingProtection);
     }
 
@@ -204,7 +209,20 @@
         }
     }
 
+    /**
+     * Closes any exposed guts/views.
+     *
+     * @param x x coordinate to animate the close circular reveal with
+     * @param y y coordinate to animate the close circular reveal with
+     * @param save whether the state should be saved
+     * @param force whether the guts should be force-closed regardless of state.
+     */
     public void closeControls(int x, int y, boolean save, boolean force) {
+        // First try to dismiss any blocking helper.
+        boolean wasBlockingHelperDismissed =
+                Dependency.get(NotificationBlockingHelperManager.class)
+                        .dismissCurrentBlockingHelper();
+
         if (getWindowToken() == null) {
             if (mClosedListener != null) {
                 mClosedListener.onGutsClosed(this);
@@ -212,8 +230,12 @@
             return;
         }
 
-        if (mGutsContent == null || !mGutsContent.handleCloseControls(save, force)) {
-            animateClose(x, y);
+        if (mGutsContent == null
+                || !mGutsContent.handleCloseControls(save, force)
+                || wasBlockingHelperDismissed) {
+            // We only want to do a circular reveal if we're not showing the blocking helper.
+            animateClose(x, y, !wasBlockingHelperDismissed /* shouldDoCircularReveal */);
+
             setExposed(false, mNeedsFalsingProtection);
             if (mClosedListener != null) {
                 mClosedListener.onGutsClosed(this);
@@ -221,47 +243,58 @@
         }
     }
 
-    private void animateOpen(int x, int y, @Nullable Runnable onAnimationEnd) {
-        final double horz = Math.max(getWidth() - x, x);
-        final double vert = Math.max(getHeight() - y, y);
-        final float r = (float) Math.hypot(horz, vert);
-
-        final Animator a
-                = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
-        a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-        a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        a.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                if (onAnimationEnd != null) {
-                    onAnimationEnd.run();
-                }
-            }
-        });
-        a.start();
+    /** Animates in the guts view via either a fade or a circular reveal. */
+    private void animateOpen(
+            boolean shouldDoCircularReveal, int x, int y, @Nullable Runnable onAnimationEnd) {
+        if (shouldDoCircularReveal) {
+            double horz = Math.max(getWidth() - x, x);
+            double vert = Math.max(getHeight() - y, y);
+            float r = (float) Math.hypot(horz, vert);
+            // Circular reveal originating at (x, y)
+            Animator a = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
+            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+            a.addListener(new AnimateOpenListener(onAnimationEnd));
+            a.start();
+        } else {
+            // Fade in content
+            this.setAlpha(0f);
+            this.animate()
+                    .alpha(1f)
+                    .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
+                    .setInterpolator(Interpolators.ALPHA_IN)
+                    .setListener(new AnimateOpenListener(onAnimationEnd))
+                    .start();
+        }
     }
 
-    private void animateClose(int x, int y) {
-        if (x == -1 || y == -1) {
-            x = (getLeft() + getRight()) / 2;
-            y = (getTop() + getHeight() / 2);
-        }
-        final double horz = Math.max(getWidth() - x, x);
-        final double vert = Math.max(getHeight() - y, y);
-        final float r = (float) Math.hypot(horz, vert);
-        final Animator a = ViewAnimationUtils.createCircularReveal(this,
-                x, y, r, 0);
-        a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-        a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-        a.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                setVisibility(View.GONE);
+
+    /** Animates out the guts view via either a fade or a circular reveal. */
+    private void animateClose(int x, int y, boolean shouldDoCircularReveal) {
+        if (shouldDoCircularReveal) {
+            // Circular reveal originating at (x, y)
+            if (x == -1 || y == -1) {
+                x = (getLeft() + getRight()) / 2;
+                y = (getTop() + getHeight() / 2);
             }
-        });
-        a.start();
+            double horz = Math.max(getWidth() - x, x);
+            double vert = Math.max(getHeight() - y, y);
+            float r = (float) Math.hypot(horz, vert);
+            Animator a = ViewAnimationUtils.createCircularReveal(this,
+                    x, y, r, 0);
+            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+            a.addListener(new AnimateCloseListener(this /* view */));
+            a.start();
+        } else {
+            // Fade in the blocking helper.
+            this.animate()
+                    .alpha(0f)
+                    .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
+                    .setInterpolator(Interpolators.ALPHA_OUT)
+                    .setListener(new AnimateCloseListener(this /* view */))
+                    .start();
+        }
     }
 
     public void setActualHeight(int actualHeight) {
@@ -336,4 +369,36 @@
     public boolean isLeavebehind() {
         return mGutsContent != null && mGutsContent.isLeavebehind();
     }
+
+    /** Listener for animations executed in {@link #animateOpen(boolean, int, int, Runnable)}. */
+    private static class AnimateOpenListener extends AnimatorListenerAdapter {
+        final Runnable mOnAnimationEnd;
+
+        private AnimateOpenListener(Runnable onAnimationEnd) {
+            mOnAnimationEnd = onAnimationEnd;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            super.onAnimationEnd(animation);
+            if (mOnAnimationEnd != null) {
+                mOnAnimationEnd.run();
+            }
+        }
+    }
+
+    /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */
+    private static class AnimateCloseListener extends AnimatorListenerAdapter {
+        final View mView;
+
+        private AnimateCloseListener(View view) {
+            mView = view;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            super.onAnimationEnd(animation);
+            mView.setVisibility(View.GONE);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index 9b2f939..75204d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -21,7 +21,6 @@
 import static android.service.notification.NotificationListenerService.Ranking
         .USER_SENTIMENT_NEGATIVE;
 
-import android.app.AppOpsManager;
 import android.app.INotificationManager;
 import android.app.NotificationChannel;
 import android.content.Context;
@@ -34,6 +33,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
+import android.support.annotation.VisibleForTesting;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.HapticFeedbackConstants;
@@ -119,22 +119,6 @@
         bindGuts(row);
     }
 
-    private void saveAndCloseNotificationMenu(
-            ExpandableNotificationRow row, NotificationGuts guts, View done) {
-        guts.resetFalsingCheck();
-        int[] rowLocation = new int[2];
-        int[] doneLocation = new int[2];
-        row.getLocationOnScreen(rowLocation);
-        done.getLocationOnScreen(doneLocation);
-
-        final int centerX = done.getWidth() / 2;
-        final int centerY = done.getHeight() / 2;
-        final int x = doneLocation[0] - rowLocation[0] + centerX;
-        final int y = doneLocation[1] - rowLocation[1] + centerY;
-        closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
-                true /* removeControls */, x, y, true /* resetMenu */);
-    }
-
     /**
      * Sends an intent to open the app settings for a particular package and optional
      * channel.
@@ -174,12 +158,12 @@
 
     private void bindGuts(final ExpandableNotificationRow row,
             NotificationMenuRowPlugin.MenuItem item) {
+        StatusBarNotification sbn = row.getStatusBarNotification();
+
         row.inflateGuts();
         row.setGutsView(item);
-        final StatusBarNotification sbn = row.getStatusBarNotification();
         row.setTag(sbn.getPackageName());
-        final NotificationGuts guts = row.getGuts();
-        guts.setClosedListener((NotificationGuts g) -> {
+        row.getGuts().setClosedListener((NotificationGuts g) -> {
             if (!g.willBeRemoved() && !row.isRemoved()) {
                 mListContainer.onHeightChanged(
                         row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
@@ -197,87 +181,143 @@
 
         View gutsView = item.getGutsView();
         if (gutsView instanceof NotificationSnooze) {
-            NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
-            snoozeGuts.setSnoozeListener(mListContainer.getSwipeActionHelper());
-            snoozeGuts.setStatusBarNotification(sbn);
-            snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
-            guts.setHeightChangedListener((NotificationGuts g) -> {
-                mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
-            });
+            initializeSnoozeView(row, (NotificationSnooze) gutsView);
+        } else if (gutsView instanceof AppOpsInfo) {
+            initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
+        } else if (gutsView instanceof NotificationInfo) {
+            initializeNotificationInfo(row, (NotificationInfo) gutsView);
         }
+    }
 
-        if (gutsView instanceof AppOpsInfo) {
-            AppOpsInfo info = (AppOpsInfo) gutsView;
-            final UserHandle userHandle = sbn.getUser();
-            PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
-                    userHandle.getIdentifier());
-            final AppOpsInfo.OnSettingsClickListener onSettingsClick = (View v,
-                    String pkg, int uid, ArraySet<Integer> ops) -> {
-                mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
-                guts.resetFalsingCheck();
-                startAppOpsSettingsActivity(pkg, uid, ops, row);
-            };
-            if (!row.getEntry().mActiveAppOps.isEmpty()) {
-                info.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
-            }
+    /**
+     * Sets up the {@link NotificationSnooze} inside the notification row's guts.
+     *
+     * @param row view to set up the guts for
+     * @param notificationSnoozeView view to set up/bind within {@code row}
+     */
+    private void initializeSnoozeView(
+            final ExpandableNotificationRow row,
+            NotificationSnooze notificationSnoozeView) {
+        NotificationGuts guts = row.getGuts();
+        StatusBarNotification sbn = row.getStatusBarNotification();
+
+        notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
+        notificationSnoozeView.setStatusBarNotification(sbn);
+        notificationSnoozeView.setSnoozeOptions(row.getEntry().snoozeCriteria);
+        guts.setHeightChangedListener((NotificationGuts g) -> {
+            mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
+        });
+    }
+
+    /**
+     * Sets up the {@link AppOpsInfo} inside the notification row's guts.
+     *
+     * @param row view to set up the guts for
+     * @param appOpsInfoView view to set up/bind within {@code row}
+     */
+    private void initializeAppOpsInfo(
+            final ExpandableNotificationRow row,
+            AppOpsInfo appOpsInfoView) {
+        NotificationGuts guts = row.getGuts();
+        StatusBarNotification sbn = row.getStatusBarNotification();
+        UserHandle userHandle = sbn.getUser();
+        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+                userHandle.getIdentifier());
+
+        AppOpsInfo.OnSettingsClickListener onSettingsClick =
+                (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
+            mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
+            guts.resetFalsingCheck();
+            startAppOpsSettingsActivity(pkg, uid, ops, row);
+        };
+        if (!row.getEntry().mActiveAppOps.isEmpty()) {
+            appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
         }
+    }
 
-        if (gutsView instanceof NotificationInfo) {
-            final UserHandle userHandle = sbn.getUser();
-            PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
-                    userHandle.getIdentifier());
-            final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
-                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-            final String pkg = sbn.getPackageName();
-            NotificationInfo info = (NotificationInfo) gutsView;
-            // Settings link is only valid for notifications that specify a user, unless this is the
-            // system user.
-            NotificationInfo.OnSettingsClickListener onSettingsClick = null;
-            if (!userHandle.equals(UserHandle.ALL)
-                    || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
-                onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
-                    mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
+    /**
+     * Sets up the {@link NotificationInfo} inside the notification row's guts.
+     *
+     * @param row view to set up the guts for
+     * @param notificationInfoView view to set up/bind within {@code row}
+     */
+    @VisibleForTesting
+    void initializeNotificationInfo(
+            final ExpandableNotificationRow row,
+            NotificationInfo notificationInfoView) {
+        NotificationGuts guts = row.getGuts();
+        StatusBarNotification sbn = row.getStatusBarNotification();
+        String packageName = sbn.getPackageName();
+        // Settings link is only valid for notifications that specify a non-system user
+        NotificationInfo.OnSettingsClickListener onSettingsClick = null;
+        UserHandle userHandle = sbn.getUser();
+        PackageManager pmUser = StatusBar.getPackageManagerForUser(
+                mContext, userHandle.getIdentifier());
+        INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
+                (View v, Intent intent) -> {
+                    mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
                     guts.resetFalsingCheck();
-                    mOnSettingsClickListener.onClick(sbn.getKey());
-                    startAppNotificationSettingsActivity(pkg, appUid, channel, row);
+                    mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row);
                 };
-            }
-            final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
-                    Intent intent) -> {
-                mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
-                guts.resetFalsingCheck();
-                mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row);
-            };
-            final View.OnClickListener onDoneClick = (View v) -> {
-                saveAndCloseNotificationMenu(row, guts, v);
-            };
+        boolean isForBlockingHelper = row.isBlockingHelperShowing();
 
-            ArraySet<NotificationChannel> channels = new ArraySet<>();
-            channels.add(row.getEntry().channel);
-            if (row.isSummaryWithChildren()) {
-                // If this is a summary, then add in the children notification channels for the
-                // same user and pkg.
-                final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
-                final int numChildren = childrenRows.size();
-                for (int i = 0; i < numChildren; i++) {
-                    final ExpandableNotificationRow childRow = childrenRows.get(i);
-                    final NotificationChannel childChannel = childRow.getEntry().channel;
-                    final StatusBarNotification childSbn = childRow.getStatusBarNotification();
-                    if (childSbn.getUser().equals(userHandle) &&
-                            childSbn.getPackageName().equals(pkg)) {
-                        channels.add(childChannel);
-                    }
+        if (!userHandle.equals(UserHandle.ALL)
+                || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
+            onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
+                mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
+                guts.resetFalsingCheck();
+                mOnSettingsClickListener.onClick(sbn.getKey());
+                startAppNotificationSettingsActivity(packageName, appUid, channel, row);
+            };
+        }
+
+        try {
+            notificationInfoView.bindNotification(
+                    pmUser,
+                    iNotificationManager,
+                    packageName,
+                    row.getEntry().channel,
+                    getNumNotificationChannels(row, packageName, userHandle),
+                    sbn,
+                    mCheckSaveListener,
+                    onSettingsClick,
+                    onAppSettingsClick,
+                    mNonBlockablePkgs,
+                    isForBlockingHelper,
+                    row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE);
+        } catch (RemoteException e) {
+            Log.e(TAG, e.toString());
+        }
+    }
+
+    /**
+     * @return the number of channels covered by the notification row (including its children if
+     * it's a summary notification).
+     */
+    private int getNumNotificationChannels(
+            ExpandableNotificationRow row, String packageName, UserHandle userHandle) {
+        ArraySet<NotificationChannel> channels = new ArraySet<>();
+
+        channels.add(row.getEntry().channel);
+
+        // If this is a summary, then add in the children notification channels for the
+        // same user and pkg.
+        if (row.isSummaryWithChildren()) {
+            final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
+            final int numChildren = childrenRows.size();
+            for (int i = 0; i < numChildren; i++) {
+                final ExpandableNotificationRow childRow = childrenRows.get(i);
+                final NotificationChannel childChannel = childRow.getEntry().channel;
+                final StatusBarNotification childSbn = childRow.getStatusBarNotification();
+                if (childSbn.getUser().equals(userHandle) &&
+                        childSbn.getPackageName().equals(packageName)) {
+                    channels.add(childChannel);
                 }
             }
-            try {
-                info.bindNotification(pmUser, iNotificationManager, pkg, row.getEntry().channel,
-                        channels.size(), sbn, mCheckSaveListener, onSettingsClick,
-                        onAppSettingsClick, mNonBlockablePkgs,
-                        row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
         }
+        return channels.size();
     }
 
     /**
@@ -312,37 +352,42 @@
     }
 
     /**
-     * Opens guts on the given ExpandableNotificationRow |v|.
+     * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
+     * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
+     * helper needs to be shown on the row, this will skip the circular reveal.
      *
-     * @param v ExpandableNotificationRow to open guts on
+     * @param view ExpandableNotificationRow to open guts on
      * @param x x coordinate of origin of circular reveal
      * @param y y coordinate of origin of circular reveal
-     * @param item MenuItem the guts should display
+     * @param menuItem MenuItem the guts should display
      * @return true if guts was opened
      */
-    public boolean openGuts(View v, int x, int y,
-            NotificationMenuRowPlugin.MenuItem item) {
-        if (!(v instanceof ExpandableNotificationRow)) {
+    boolean openGuts(
+            View view,
+            int x,
+            int y,
+            NotificationMenuRowPlugin.MenuItem menuItem) {
+        if (!(view instanceof ExpandableNotificationRow)) {
             return false;
         }
 
-        if (v.getWindowToken() == null) {
+        if (view.getWindowToken() == null) {
             Log.e(TAG, "Trying to show notification guts, but not attached to window");
             return false;
         }
 
-        final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+        final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
         if (row.isDark()) {
             return false;
         }
-        v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
         if (row.areGutsExposed()) {
             closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
                     true /* removeControls */, -1 /* x */, -1 /* y */,
                     true /* resetMenu */);
             return false;
         }
-        bindGuts(row, item);
+        bindGuts(row, menuItem);
         NotificationGuts guts = row.getGuts();
 
         // Assume we are a status_bar_notification_row
@@ -372,15 +417,18 @@
                 final boolean needsFalsingProtection =
                         (mPresenter.isPresenterLocked() &&
                                 !mAccessibilityManager.isTouchExplorationEnabled());
-                guts.openControls(x, y, needsFalsingProtection, () -> {
-                    // Move the notification view back over the menu
-                    row.resetTranslation();
-                });
+
+                guts.openControls(
+                        !row.isBlockingHelperShowing(),
+                        x,
+                        y,
+                        needsFalsingProtection,
+                        row::resetTranslation);
 
                 row.closeRemoteInput();
                 mListContainer.onHeightChanged(row, true /* needsAnimation */);
                 mNotificationGutsExposed = guts;
-                mGutsMenuItem = item;
+                mGutsMenuItem = menuItem;
             }
         });
         return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index b1ad30c..a2f336e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -46,9 +46,11 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
@@ -81,11 +83,12 @@
     private OnSettingsClickListener mOnSettingsClickListener;
     private OnAppSettingsClickListener mAppSettingsClickListener;
     private NotificationGuts mGutsContainer;
+
+    /** Whether this view is being shown as part of the blocking helper */
+    private boolean mIsForBlockingHelper;
     private boolean mNegativeUserSentiment;
 
-    private OnClickListener mOnKeepShowing = v -> {
-        closeControls(v);
-    };
+    private OnClickListener mOnKeepShowing = this::closeControls;
 
     private OnClickListener mOnStopMinNotifications = v -> {
         swapContent(false);
@@ -114,7 +117,9 @@
         void onClick(View v, Intent intent);
     }
 
-    public void bindNotification(final PackageManager pm,
+    @VisibleForTesting
+    void bindNotification(
+            final PackageManager pm,
             final INotificationManager iNotificationManager,
             final String pkg,
             final NotificationChannel notificationChannel,
@@ -127,20 +132,24 @@
             throws RemoteException {
         bindNotification(pm, iNotificationManager, pkg, notificationChannel, numChannels, sbn,
                 checkSaveListener, onSettingsClick, onAppSettingsClick, nonBlockablePkgs,
-                false /* negative sentiment */);
+                false /* isBlockingHelper */,
+                false /* isUserSentimentNegative */);
     }
 
-    public void bindNotification(final PackageManager pm,
-            final INotificationManager iNotificationManager,
-            final String pkg,
-            final NotificationChannel notificationChannel,
-            final int numChannels,
-            final StatusBarNotification sbn,
-            final CheckSaveListener checkSaveListener,
-            final OnSettingsClickListener onSettingsClick,
-            final OnAppSettingsClickListener onAppSettingsClick,
-            final Set<String> nonBlockablePkgs,
-            boolean negativeUserSentiment)  throws RemoteException {
+    public void bindNotification(
+            PackageManager pm,
+            INotificationManager iNotificationManager,
+            String pkg,
+            NotificationChannel notificationChannel,
+            int numChannels,
+            StatusBarNotification sbn,
+            CheckSaveListener checkSaveListener,
+            OnSettingsClickListener onSettingsClick,
+            OnAppSettingsClickListener onAppSettingsClick,
+            Set<String> nonBlockablePkgs,
+            boolean isForBlockingHelper,
+            boolean isUserSentimentNegative)
+            throws RemoteException {
         mINotificationManager = iNotificationManager;
         mPkg = pkg;
         mNumNotificationChannels = numChannels;
@@ -152,9 +161,10 @@
         mOnSettingsClickListener = onSettingsClick;
         mSingleNotificationChannel = notificationChannel;
         mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance();
-        mNegativeUserSentiment = negativeUserSentiment;
+        mNegativeUserSentiment = isUserSentimentNegative;
         mIsForeground =
                 (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
+        mIsForBlockingHelper = isForBlockingHelper;
 
         int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
                 pkg, mAppUid, false /* includeDeleted */);
@@ -294,12 +304,14 @@
     }
 
     private void bindButtons() {
+        // Set up stay-in-notification actions
         View block =  findViewById(R.id.block);
-        block.setOnClickListener(mOnStopMinNotifications);
         TextView keep = findViewById(R.id.keep);
-        keep.setOnClickListener(mOnKeepShowing);
-        findViewById(R.id.undo).setOnClickListener(mOnUndo);
         View minimize = findViewById(R.id.minimize);
+
+        findViewById(R.id.undo).setOnClickListener(mOnUndo);
+        block.setOnClickListener(mOnStopMinNotifications);
+        keep.setOnClickListener(mOnKeepShowing);
         minimize.setOnClickListener(mOnStopMinNotifications);
 
         if (mNonblockable) {
@@ -314,7 +326,7 @@
             minimize.setVisibility(GONE);
         }
 
-        // app settings link
+        // Set up app settings link
         TextView settingsLinkView = findViewById(R.id.app_settings);
         Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel,
                 mSbn.getId(), mSbn.getTag());
@@ -421,16 +433,23 @@
         return intent;
     }
 
-    private void closeControls(View v) {
-        int[] parentLoc = new int[2];
-        int[] targetLoc = new int[2];
-        mGutsContainer.getLocationOnScreen(parentLoc);
-        v.getLocationOnScreen(targetLoc);
-        final int centerX = v.getWidth() / 2;
-        final int centerY = v.getHeight() / 2;
-        final int x = targetLoc[0] - parentLoc[0] + centerX;
-        final int y = targetLoc[1] - parentLoc[1] + centerY;
-        mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
+    @VisibleForTesting
+    void closeControls(View v) {
+        if (mIsForBlockingHelper) {
+            NotificationBlockingHelperManager manager =
+                    Dependency.get(NotificationBlockingHelperManager.class);
+            manager.dismissCurrentBlockingHelper();
+        } else {
+            int[] parentLoc = new int[2];
+            int[] targetLoc = new int[2];
+            mGutsContainer.getLocationOnScreen(parentLoc);
+            v.getLocationOnScreen(targetLoc);
+            final int centerX = v.getWidth() / 2;
+            final int centerY = v.getHeight() / 2;
+            final int x = targetLoc[0] - parentLoc[0] + centerX;
+            final int y = targetLoc[1] - parentLoc[1] + centerY;
+            mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index a4aa598..0112661 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -227,7 +227,6 @@
             expandAmount = Math.min(1.0f, expandAmount);
         }
         //  find the first view that doesn't overlap with the shelf
-        int notificationIndex = 0;
         int notGoneIndex = 0;
         int colorOfViewBeforeLast = NO_COLOR;
         boolean backgroundForceHidden = false;
@@ -247,13 +246,15 @@
         int baseZHeight = mAmbientState.getBaseZHeight();
         int backgroundTop = 0;
         float firstElementRoundness = 0.0f;
-        while (notificationIndex < mHostLayout.getChildCount()) {
-            ExpandableView child = (ExpandableView) mHostLayout.getChildAt(notificationIndex);
-            notificationIndex++;
+
+        for (int i = 0; i < mHostLayout.getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
+
             if (!(child instanceof ExpandableNotificationRow)
                     || child.getVisibility() == GONE) {
                 continue;
             }
+
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
             float notificationClipEnd;
             boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight
@@ -315,6 +316,9 @@
             notGoneIndex++;
             previousColor = ownColorUntinted;
         }
+
+        clipTransientViews();
+
         setBackgroundTop(backgroundTop);
         setFirstElementRoundness(firstElementRoundness);
         mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
@@ -337,6 +341,25 @@
         }
     }
 
+    /**
+     * Clips transient views to the top of the shelf - Transient views are only used for
+     * disappearing views/animations and need to be clipped correctly by the shelf to ensure they
+     * don't show underneath the notification stack when something is animating and the user
+     * swipes quickly.
+     */
+    private void clipTransientViews() {
+        for (int i = 0; i < mHostLayout.getTransientViewCount(); i++) {
+            View transientView = mHostLayout.getTransientView(i);
+            if (transientView instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow transientRow = (ExpandableNotificationRow) transientView;
+                updateNotificationClipHeight(transientRow, getTranslationY());
+            } else {
+                Log.e(TAG, "NotificationShelf.clipTransientViews(): "
+                        + "Trying to clip non-row transient view");
+            }
+        }
+    }
+
     private void setFirstElementRoundness(float firstElementRoundness) {
         if (mFirstElementRoundness != firstElementRoundness) {
             mFirstElementRoundness = firstElementRoundness;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 75b8b37..b1e08b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -125,24 +125,30 @@
 
         }
 
-        ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
+        ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>();
         for (int i=0; i< mListContainer.getContainerChildCount(); i++) {
             View child = mListContainer.getContainerChildAt(i);
             if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
-                toRemove.add((ExpandableNotificationRow) child);
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+
+                // Blocking helper is effectively a detached view. Don't bother removing it from the
+                // layout.
+                if (!row.isBlockingHelperShowing()) {
+                    viewsToRemove.add((ExpandableNotificationRow) child);
+                }
             }
         }
 
-        for (ExpandableNotificationRow remove : toRemove) {
-            if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
+        for (ExpandableNotificationRow viewToRemove : viewsToRemove) {
+            if (mGroupManager.isChildInGroupWithSummary(viewToRemove.getStatusBarNotification())) {
                 // we are only transferring this notification to its parent, don't generate an
                 // animation
                 mListContainer.setChildTransferInProgress(true);
             }
-            if (remove.isSummaryWithChildren()) {
-                remove.removeAllChildren();
+            if (viewToRemove.isSummaryWithChildren()) {
+                viewToRemove.removeAllChildren();
             }
-            mListContainer.removeContainerView(remove);
+            mListContainer.removeContainerView(viewToRemove);
             mListContainer.setChildTransferInProgress(false);
         }
 
@@ -168,6 +174,10 @@
                 // We don't care about non-notification views.
                 continue;
             }
+            if (((ExpandableNotificationRow) child).isBlockingHelperShowing()) {
+                // Don't count/reorder notifications that are showing the blocking helper!
+                continue;
+            }
 
             ExpandableNotificationRow targetChild = toShow.get(j);
             if (child != targetChild) {
@@ -340,7 +350,7 @@
                 }
             }
 
-            row.showBlockingHelper(entry.userSentiment ==
+            row.showBlockingHelperButton(entry.userSentiment ==
                     NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
 
             row.showAppOpsIcons(entry.mActiveAppOps);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index b75c7e0..ca65965 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -169,7 +169,7 @@
             if (group.suppressed) {
                 handleSuppressedSummaryHeadsUpped(group.summary);
             }
-            if (!mIsUpdatingUnchangedGroup) {
+            if (!mIsUpdatingUnchangedGroup && mListener != null) {
                 mListener.onGroupsChanged();
             }
         }
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 7a6e98d..7aabc56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -85,7 +85,7 @@
     /**
      * Default alpha value for most scrims.
      */
-    public static final float GRADIENT_SCRIM_ALPHA = 0.45f;
+    public static final float GRADIENT_SCRIM_ALPHA = 0.70f;
     /**
      * A scrim varies its opacity based on a busyness factor, for example
      * how many notifications are currently visible.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 1a64b00..5975608 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -154,7 +154,7 @@
             if (expansion == 1) {
                 mBouncer.onFullyHidden();
             } else if (!mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
-                mBouncer.show(true /* resetSecuritySelection */, false /* notifyFalsing */);
+                mBouncer.show(false /* resetSecuritySelection */, false /* notifyFalsing */);
             } else if (noLongerTracking) {
                 // Notify that falsing manager should stop its session when user stops touching,
                 // even before the animation ends, to guarantee that we're not recording sensitive
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 4c92d01..baeaaad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -40,6 +40,7 @@
 import android.view.View;
 import android.widget.TextView;
 
+import com.android.settingslib.Utils;
 import com.android.systemui.DemoMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.FontSizeUtils;
@@ -84,6 +85,17 @@
     private boolean mShowSeconds;
     private Handler mSecondsHandler;
 
+    /**
+     * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings
+     * for text.
+     */
+    private boolean mUseWallpaperTextColor;
+
+    /**
+     * Color to be set on this {@link TextView}, when wallpaperTextColor is <b>not</b> utilized.
+     */
+    private int mNonAdaptedColor;
+
     public Clock(Context context) {
         this(context, null);
     }
@@ -101,6 +113,7 @@
         try {
             mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE);
             mShowDark = a.getBoolean(R.styleable.Clock_showDark, true);
+            mNonAdaptedColor = getCurrentTextColor();
         } finally {
             a.recycle();
         }
@@ -227,7 +240,10 @@
 
     @Override
     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
-        setTextColor(DarkIconDispatcher.getTint(area, this, tint));
+        mNonAdaptedColor = DarkIconDispatcher.getTint(area, this, tint);
+        if (!mUseWallpaperTextColor) {
+            setTextColor(mNonAdaptedColor);
+        }
     }
 
     @Override
@@ -242,6 +258,25 @@
                 0);
     }
 
+    /**
+     * Sets whether the clock uses the wallpaperTextColor. If we're not using it, we'll revert back
+     * to dark-mode-based/tinted colors.
+     *
+     * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for text color
+     */
+    public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
+        if (shouldUseWallpaperTextColor == mUseWallpaperTextColor) {
+            return;
+        }
+        mUseWallpaperTextColor = shouldUseWallpaperTextColor;
+
+        if (mUseWallpaperTextColor) {
+            setTextColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor));
+        } else {
+            setTextColor(mNonAdaptedColor);
+        }
+    }
+
     private void updateShowSeconds() {
         if (mShowSeconds) {
             // Wait until we have a display to start trying to show seconds.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index 74a30fa..ef630c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -27,6 +27,7 @@
 import android.util.AttributeSet;
 import android.widget.TextView;
 
+import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 
@@ -42,6 +43,17 @@
     private String mLastText;
     private String mDatePattern;
 
+    /**
+     * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings
+     * for text.
+     */
+    private boolean mUseWallpaperTextColor;
+
+    /**
+     * Color to be set on this {@link TextView}, when wallpaperTextColor is <b>not</b> utilized.
+     */
+    private int mNonAdaptedTextColor;
+
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -62,6 +74,7 @@
 
     public DateView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mNonAdaptedTextColor = getCurrentTextColor();
         TypedArray a = context.getTheme().obtainStyledAttributes(
                 attrs,
                 R.styleable.DateView,
@@ -117,6 +130,25 @@
         }
     }
 
+    /**
+     * Sets whether the date view uses the wallpaperTextColor. If we're not using it, we'll revert
+     * back to dark-mode-based/tinted colors.
+     *
+     * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for text color
+     */
+    public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
+        if (shouldUseWallpaperTextColor == mUseWallpaperTextColor) {
+            return;
+        }
+        mUseWallpaperTextColor = shouldUseWallpaperTextColor;
+
+        if (mUseWallpaperTextColor) {
+            setTextColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor));
+        } else {
+            setTextColor(mNonAdaptedTextColor);
+        }
+    }
+
     public void setDatePattern(String pattern) {
         if (TextUtils.equals(pattern, mDatePattern)) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 8f6fa49..375e860 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -70,6 +70,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -83,6 +84,7 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.FooterView;
+import com.android.systemui.statusbar.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationGuts;
 import com.android.systemui.statusbar.NotificationListContainer;
@@ -448,6 +450,13 @@
         mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
         addOnExpandedHeightListener(mRoundnessManager::setExpanded);
 
+        // Blocking helper manager wants to know the expanded state, update as well.
+        NotificationBlockingHelperManager blockingHelperManager =
+                Dependency.get(NotificationBlockingHelperManager.class);
+        addOnExpandedHeightListener((height, unused) -> {
+            blockingHelperManager.setNotificationShadeExpanded(height);
+        });
+
         updateWillNotDraw();
         mBackgroundPaint.setAntiAlias(true);
         if (DEBUG) {
@@ -1038,45 +1047,63 @@
         mQsContainer = qsContainer;
     }
 
+    /**
+     * Handles cleanup after the given {@code view} has been fully swiped out (including
+     * re-invoking dismiss logic in case the notification has not made its way out yet).
+     */
     @Override
-    public void onChildDismissed(View v) {
-        ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+    public void onChildDismissed(View view) {
+        ExpandableNotificationRow row = (ExpandableNotificationRow) view;
         if (!row.isDismissed()) {
-            handleChildDismissed(v);
+            handleChildViewDismissed(view);
         }
         ViewGroup transientContainer = row.getTransientContainer();
         if (transientContainer != null) {
-            transientContainer.removeTransientView(v);
+            transientContainer.removeTransientView(view);
         }
     }
 
-    private void handleChildDismissed(View v) {
+    /**
+     * Starts up notification dismiss and tells the notification, if any, to remove itself from
+     * layout.
+     *
+     * @param view view (e.g. notification) to dismiss from the layout
+     */
+    private void handleChildViewDismissed(View view) {
         if (mDismissAllInProgress) {
             return;
         }
+
+        boolean isBlockingHelperShown = false;
+
         setSwipingInProgress(false);
-        if (mDragAnimPendingChildren.contains(v)) {
-            // We start the swipe and finish it in the same frame, we don't want any animation
-            // for the drag
-            mDragAnimPendingChildren.remove(v);
+        if (mDragAnimPendingChildren.contains(view)) {
+            // We start the swipe and finish it in the same frame; we don't want a drag animation.
+            mDragAnimPendingChildren.remove(view);
         }
-        mSwipedOutViews.add(v);
-        mAmbientState.onDragFinished(v);
+        mAmbientState.onDragFinished(view);
         updateContinuousShadowDrawing();
-        if (v instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+
+        if (view instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
             if (row.isHeadsUp()) {
                 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
             }
-        }
-        if (v instanceof ExpandableNotificationRow) {
-            ((ExpandableNotificationRow) v).performDismiss(false /* fromAccessibility */);
+            isBlockingHelperShown =
+                    row.performDismissWithBlockingHelper(false /* fromAccessibility */);
         }
 
+        if (!isBlockingHelperShown) {
+            mSwipedOutViews.add(view);
+        }
         mFalsingManager.onNotificationDismissed();
         if (mFalsingManager.shouldEnforceBouncer()) {
-            mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
-                    false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
+            mStatusBar.executeRunnableDismissingKeyguard(
+                    null,
+                    null /* cancelAction */,
+                    false /* dismissShade */,
+                    true /* afterKeyguardGone */,
+                    false /* deferred */);
         }
     }
 
@@ -2729,9 +2756,8 @@
         updateScrollStateForRemovedChild(expandableView);
         boolean animationGenerated = generateRemoveAnimation(child);
         if (animationGenerated) {
-            if (!mSwipedOutViews.contains(child)) {
-                container.getOverlay().add(child);
-            } else if (Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
+            if (!mSwipedOutViews.contains(child)
+                    || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
                 container.addTransientView(child, 0);
                 expandableView.setTransientContainer(container);
             }
@@ -4618,7 +4644,7 @@
             if (mIsExpanded) {
                 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
                 // of the panel early.
-                handleChildDismissed(view);
+                handleChildViewDismissed(view);
             }
             mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
                     false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index d48ae76..c80bdc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -51,6 +51,7 @@
             = (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
                     * HeadsUpAppearInterpolator.getFractionUntilOvershoot());
     public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
+    public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
     public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
     public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
     public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
@@ -79,6 +80,7 @@
     private ValueAnimator mBottomOverScrollAnimator;
     private int mHeadsUpAppearHeightBottom;
     private boolean mShadeExpanded;
+    private ArrayList<ExpandableView> mTransientViewsToRemove = new ArrayList<>();
     private NotificationShelf mShelf;
     private float mStatusBarIconLocation;
     private int[] mTmpLocation = new int[2];
@@ -333,6 +335,12 @@
 
     private void onAnimationFinished() {
         mHostLayout.onChildAnimationFinished();
+
+        for (ExpandableView transientViewsToRemove : mTransientViewsToRemove) {
+            transientViewsToRemove.getTransientContainer()
+                    .removeTransientView(transientViewsToRemove);
+        }
+        mTransientViewsToRemove.clear();
     }
 
     /**
@@ -362,7 +370,7 @@
             } else if (event.animationType ==
                     NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
                 if (changingView.getVisibility() != View.VISIBLE) {
-                    removeFromOverlay(changingView);
+                    removeTransientView(changingView);
                     continue;
                 }
 
@@ -402,8 +410,7 @@
                         0, new Runnable() {
                     @Override
                     public void run() {
-                        // remove the temporary overlay
-                        removeFromOverlay(changingView);
+                        removeTransientView(changingView);
                     }
                 }, null);
             } else if (event.animationType ==
@@ -494,6 +501,12 @@
         }
     }
 
+    private static void removeTransientView(ExpandableView viewToRemove) {
+        if (viewToRemove.getTransientContainer() != null) {
+            viewToRemove.getTransientContainer().removeTransientView(viewToRemove);
+        }
+    }
+
     public static void removeFromOverlay(View changingView) {
         ViewGroup parent = (ViewGroup) changingView.getParent();
         if (parent != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index fc932c3..1cbdfe8 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -37,6 +37,7 @@
     public static String STORAGE     = "DSK";
     public static String TVPIP       = "TPP";
     public static String BATTERY     = "BAT";
+    public static String HINTS       = "HNT";
 
     @VisibleForTesting
     static void createAll(Context context) {
@@ -73,6 +74,12 @@
                         : NotificationManager.IMPORTANCE_LOW);
         storage.setBypassDnd(true);
 
+        final NotificationChannel hint = new NotificationChannel(
+                HINTS,
+                context.getString(R.string.notification_channel_hints),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        // No need to bypass DND.
+
         nm.createNotificationChannels(Arrays.asList(
                 alerts,
                 general,
@@ -80,7 +87,8 @@
                 createScreenshotChannel(
                         context.getString(R.string.notification_channel_screenshot),
                         nm.getNotificationChannel(SCREENSHOTS_LEGACY)),
-                batteryChannel
+                batteryChannel,
+                hint
         ));
 
         // Delete older SS channel if present.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index af29ab4..23a3405 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -40,8 +42,13 @@
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 import java.util.function.Consumer;
 
@@ -49,52 +56,59 @@
 @RunWith(AndroidJUnit4.class)
 public class ExpandableNotificationRowTest extends SysuiTestCase {
 
-    private ExpandableNotificationRow mGroup;
+    private ExpandableNotificationRow mGroupRow;
+
     private NotificationTestHelper mNotificationTestHelper;
     boolean mHeadsUpAnimatingAway = false;
 
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
+
     @Before
     public void setUp() throws Exception {
         mNotificationTestHelper = new NotificationTestHelper(mContext);
-        mGroup = mNotificationTestHelper.createGroup();
-        mGroup.setHeadsUpAnimatingAwayListener(
+        mGroupRow = mNotificationTestHelper.createGroup();
+        mGroupRow.setHeadsUpAnimatingAwayListener(
                 animatingAway -> mHeadsUpAnimatingAway = animatingAway);
+        mDependency.injectTestDependency(
+                NotificationBlockingHelperManager.class,
+                mBlockingHelperManager);
     }
 
     @Test
     public void testGroupSummaryNotShowingIconWhenPublic() {
-        mGroup.setSensitive(true, true);
-        mGroup.setHideSensitiveForIntrinsicHeight(true);
-        Assert.assertTrue(mGroup.isSummaryWithChildren());
-        Assert.assertFalse(mGroup.isShowingIcon());
+        mGroupRow.setSensitive(true, true);
+        mGroupRow.setHideSensitiveForIntrinsicHeight(true);
+        assertTrue(mGroupRow.isSummaryWithChildren());
+        assertFalse(mGroupRow.isShowingIcon());
     }
 
     @Test
     public void testNotificationHeaderVisibleWhenAnimating() {
-        mGroup.setSensitive(true, true);
-        mGroup.setHideSensitive(true, false, 0, 0);
-        mGroup.setHideSensitive(false, true, 0, 0);
-        Assert.assertTrue(mGroup.getChildrenContainer().getVisibleHeader().getVisibility()
+        mGroupRow.setSensitive(true, true);
+        mGroupRow.setHideSensitive(true, false, 0, 0);
+        mGroupRow.setHideSensitive(false, true, 0, 0);
+        assertTrue(mGroupRow.getChildrenContainer().getVisibleHeader().getVisibility()
                 == View.VISIBLE);
     }
 
     @Test
     public void testUserLockedResetEvenWhenNoChildren() {
-        mGroup.setUserLocked(true);
-        mGroup.removeAllChildren();
-        mGroup.setUserLocked(false);
-        Assert.assertFalse("The childrencontainer should not be userlocked but is, the state "
-                + "seems out of sync.", mGroup.getChildrenContainer().isUserLocked());
+        mGroupRow.setUserLocked(true);
+        mGroupRow.removeAllChildren();
+        mGroupRow.setUserLocked(false);
+        assertFalse("The childrencontainer should not be userlocked but is, the state "
+                + "seems out of sync.", mGroupRow.getChildrenContainer().isUserLocked());
     }
 
     @Test
     public void testReinflatedOnDensityChange() {
-        mGroup.setUserLocked(true);
-        mGroup.removeAllChildren();
-        mGroup.setUserLocked(false);
+        mGroupRow.setUserLocked(true);
+        mGroupRow.removeAllChildren();
+        mGroupRow.setUserLocked(false);
         NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
-        mGroup.setChildrenContainer(mockContainer);
-        mGroup.onDensityOrFontScaleChanged();
+        mGroupRow.setChildrenContainer(mockContainer);
+        mGroupRow.onDensityOrFontScaleChanged();
         verify(mockContainer).reInflateViews(any(), any());
     }
 
@@ -151,38 +165,38 @@
 
     @Test
     public void testClickSound() throws Exception {
-        Assert.assertTrue("Should play sounds by default.", mGroup.isSoundEffectsEnabled());
-        mGroup.setDark(true /* dark */, false /* fade */, 0 /* delay */);
-        mGroup.setSecureStateProvider(()-> false);
-        Assert.assertFalse("Shouldn't play sounds when dark and trusted.",
-                mGroup.isSoundEffectsEnabled());
-        mGroup.setSecureStateProvider(()-> true);
-        Assert.assertTrue("Should always play sounds when not trusted.",
-                mGroup.isSoundEffectsEnabled());
+        assertTrue("Should play sounds by default.", mGroupRow.isSoundEffectsEnabled());
+        mGroupRow.setDark(true /* dark */, false /* fade */, 0 /* delay */);
+        mGroupRow.setSecureStateProvider(()-> false);
+        assertFalse("Shouldn't play sounds when dark and trusted.",
+                mGroupRow.isSoundEffectsEnabled());
+        mGroupRow.setSecureStateProvider(()-> true);
+        assertTrue("Should always play sounds when not trusted.",
+                mGroupRow.isSoundEffectsEnabled());
     }
 
     @Test
     public void testSetDismissed_longPressListenerRemoved() {
         ExpandableNotificationRow.LongPressListener listener =
                 mock(ExpandableNotificationRow.LongPressListener.class);
-        mGroup.setLongPressListener(listener);
-        mGroup.doLongClickCallback(0,0);
-        verify(listener, times(1)).onLongPress(eq(mGroup), eq(0), eq(0),
+        mGroupRow.setLongPressListener(listener);
+        mGroupRow.doLongClickCallback(0,0);
+        verify(listener, times(1)).onLongPress(eq(mGroupRow), eq(0), eq(0),
                 any(NotificationMenuRowPlugin.MenuItem.class));
         reset(listener);
 
-        mGroup.setDismissed(true);
-        mGroup.doLongClickCallback(0,0);
-        verify(listener, times(0)).onLongPress(eq(mGroup), eq(0), eq(0),
+        mGroupRow.setDismissed(true);
+        mGroupRow.doLongClickCallback(0,0);
+        verify(listener, times(0)).onLongPress(eq(mGroupRow), eq(0), eq(0),
                 any(NotificationMenuRowPlugin.MenuItem.class));
     }
 
     @Test
     public void testShowAppOps_noHeader() {
         // public notification is custom layout - no header
-        mGroup.setSensitive(true, true);
-        mGroup.setAppOpsOnClickListener(null);
-        mGroup.showAppOpsIcons(null);
+        mGroupRow.setSensitive(true, true);
+        mGroupRow.setAppOpsOnClickListener(null);
+        mGroupRow.showAppOpsIcons(null);
     }
 
     @Test
@@ -190,17 +204,17 @@
         NotificationHeaderView mockHeader = mock(NotificationHeaderView.class);
 
         NotificationContentView publicLayout = mock(NotificationContentView.class);
-        mGroup.setPublicLayout(publicLayout);
+        mGroupRow.setPublicLayout(publicLayout);
         NotificationContentView privateLayout = mock(NotificationContentView.class);
-        mGroup.setPrivateLayout(privateLayout);
+        mGroupRow.setPrivateLayout(privateLayout);
         NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
         when(mockContainer.getNotificationChildCount()).thenReturn(1);
         when(mockContainer.getHeaderView()).thenReturn(mockHeader);
-        mGroup.setChildrenContainer(mockContainer);
+        mGroupRow.setChildrenContainer(mockContainer);
 
         ArraySet<Integer> ops = new ArraySet<>();
         ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
-        mGroup.showAppOpsIcons(ops);
+        mGroupRow.showAppOpsIcons(ops);
 
         verify(mockHeader, times(1)).showAppOpsIcons(ops);
         verify(privateLayout, times(1)).showAppOpsIcons(ops);
@@ -214,17 +228,50 @@
                 ExpandableNotificationRow.OnAppOpsClickListener.class);
         View view = mock(View.class);
 
-        mGroup.setAppOpsOnClickListener(l);
+        mGroupRow.setAppOpsOnClickListener(l);
 
-        mGroup.getAppOpsOnClickListener().onClick(view);
+        mGroupRow.getAppOpsOnClickListener().onClick(view);
         verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
     }
 
     @Test
     public void testHeadsUpAnimatingAwayListener() {
-        mGroup.setHeadsUpAnimatingAway(true);
+        mGroupRow.setHeadsUpAnimatingAway(true);
         Assert.assertEquals(true, mHeadsUpAnimatingAway);
-        mGroup.setHeadsUpAnimatingAway(false);
+        mGroupRow.setHeadsUpAnimatingAway(false);
         Assert.assertEquals(false, mHeadsUpAnimatingAway);
     }
+
+    @Test
+    public void testPerformDismissWithBlockingHelper_falseWhenBlockingHelperIsntShown() {
+        when(mBlockingHelperManager.perhapsShowBlockingHelper(
+                eq(mGroupRow), any(NotificationMenuRowPlugin.class))).thenReturn(false);
+
+        assertFalse(
+                mGroupRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
+    }
+
+    @Test
+    public void testPerformDismissWithBlockingHelper_doesntPerformOnGroupSummary() {
+        ExpandableNotificationRow childRow = mGroupRow.getChildrenContainer().getViewAtPosition(0);
+        when(mBlockingHelperManager.perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class)))
+                .thenReturn(true);
+
+        assertTrue(
+                childRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
+
+        verify(mBlockingHelperManager, times(1))
+                .perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class));
+        verify(mBlockingHelperManager, times(0))
+                .perhapsShowBlockingHelper(eq(mGroupRow), any(NotificationMenuRowPlugin.class));
+    }
+
+    @Test
+    public void testIsBlockingHelperShowing_isCorrectlyUpdated() {
+        mGroupRow.setBlockingHelperShowing(true);
+        assertTrue(mGroupRow.isBlockingHelperShowing());
+
+        mGroupRow.setBlockingHelperShowing(false);
+        assertFalse(mGroupRow.isBlockingHelperShowing());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java
new file mode 100644
index 0000000..64f34e0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.statusbar;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+
+import android.content.Context;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link NotificationBlockingHelperManager}.
+ */
+@SmallTest
+@org.junit.runner.RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
+
+    private NotificationBlockingHelperManager mBlockingHelperManager;
+
+    private NotificationTestHelper mHelper;
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private NotificationGutsManager mGutsManager;
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private NotificationMenuRow mMenuRow;
+    @Mock private NotificationMenuRowPlugin.MenuItem mMenuItem;
+
+    @Before
+    public void setUp() {
+        mBlockingHelperManager = new NotificationBlockingHelperManager(mContext);
+
+        mHelper = new NotificationTestHelper(mContext);
+        when(mGutsManager.openGuts(
+                any(View.class),
+                anyInt(),
+                anyInt(),
+                any(NotificationMenuRowPlugin.MenuItem.class)))
+                .thenReturn(true);
+        mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
+        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
+        when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
+    }
+
+    @Test
+    public void testDismissCurrentBlockingHelper_nullBlockingHelperRow() {
+        // By default, this shouldn't dismiss (no pointers/vars set up!)
+        assertFalse(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+    }
+
+    @Test
+    public void testDismissCurrentBlockingHelper_withDetachedBlockingHelperRow() throws Exception {
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        row.setBlockingHelperShowing(true);
+        when(row.isAttachedToWindow()).thenReturn(false);
+        mBlockingHelperManager.setBlockingHelperRowForTest(row);
+
+        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+
+        verify(mEntryManager, times(0)).updateNotifications();
+    }
+
+    @Test
+    public void testDismissCurrentBlockingHelper_withAttachedBlockingHelperRow() throws Exception {
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        row.setBlockingHelperShowing(true);
+        when(row.isAttachedToWindow()).thenReturn(true);
+        mBlockingHelperManager.setBlockingHelperRowForTest(row);
+
+        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+
+        verify(mEntryManager).updateNotifications();
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_shown() throws Exception {
+        ExpandableNotificationRow row = mHelper.createRow();
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+
+        verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
+    }
+
+
+    @Test
+    public void testPerhapsShowBlockingHelper_shownForLargeGroup() throws Exception {
+        ExpandableNotificationRow groupRow = mHelper.createGroup(10);
+        groupRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(groupRow, mMenuRow));
+
+        verify(mGutsManager).openGuts(groupRow, 0, 0, mMenuItem);
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_shownForOnlyChildNotification()
+            throws Exception {
+        ExpandableNotificationRow groupRow = mHelper.createGroup(1);
+        // Explicitly get the children container & call getViewAtPosition on it instead of the row
+        // as other factors such as view expansion may cause us to get the parent row back instead
+        // of the child row.
+        ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
+        childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
+
+        verify(mGutsManager).openGuts(childRow, 0, 0, mMenuItem);
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToNeutralUserSentiment() throws Exception {
+        ExpandableNotificationRow row = mHelper.createRow();
+        row.getEntry().userSentiment = USER_SENTIMENT_NEUTRAL;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToPositiveUserSentiment()
+            throws Exception {
+        ExpandableNotificationRow row = mHelper.createRow();
+        row.getEntry().userSentiment = USER_SENTIMENT_POSITIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToShadeVisibility() throws Exception {
+        ExpandableNotificationRow row = mHelper.createRow();
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        // Hide the shade
+        mBlockingHelperManager.setNotificationShadeExpanded(0f);
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownAsNotificationIsInMultipleChildGroup()
+            throws Exception {
+        ExpandableNotificationRow groupRow = mHelper.createGroup(2);
+        // Explicitly get the children container & call getViewAtPosition on it instead of the row
+        // as other factors such as view expansion may cause us to get the parent row back instead
+        // of the child row.
+        ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
+        childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
+    }
+
+    @Test
+    public void testBlockingHelperShowAndDismiss() throws Exception{
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        when(row.isAttachedToWindow()).thenReturn(true);
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        // Show check
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+
+        verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
+
+        // Dismiss check
+        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+
+        verify(mEntryManager).updateNotifications();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
index 6209d59..21f6750 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
@@ -20,6 +20,7 @@
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static junit.framework.Assert.assertNotNull;
 
 import static org.junit.Assert.assertEquals;
@@ -27,6 +28,8 @@
 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.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -34,31 +37,24 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.times;
 
+import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
-import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.ArraySet;
-import android.view.LayoutInflater;
 import android.view.View;
 
-import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationGuts;
-import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.NotificationInflater;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import org.junit.Before;
@@ -70,15 +66,18 @@
 import org.mockito.junit.MockitoRule;
 import org.mockito.junit.MockitoJUnit;
 
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tests for {@link NotificationGutsManager}.
+ */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class NotificationGutsManagerTest extends SysuiTestCase {
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
 
-    private final String mPackageName = mContext.getPackageName();
-    private final int mUid = Binder.getCallingUid();
-
     private NotificationChannel mTestNotificationChannel = new NotificationChannel(
             TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
     private TestableLooper mTestableLooper;
@@ -87,7 +86,6 @@
     private NotificationGutsManager mGutsManager;
 
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
-    @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
     @Mock private NotificationPresenter mPresenter;
     @Mock private NotificationEntryManager mEntryManager;
     @Mock private NotificationStackScrollLayout mStackScroller;
@@ -118,7 +116,12 @@
         });
 
         // Test doesn't support animation since the guts view is not attached.
-        doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+        doNothing().when(guts).openControls(
+                eq(true) /* shouldDoCircularReveal */,
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
 
         ExpandableNotificationRow realRow = createTestNotificationRow();
         NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
@@ -130,7 +133,12 @@
         mGutsManager.openGuts(row, 0, 0, menuItem);
         assertEquals(View.INVISIBLE, guts.getVisibility());
         mTestableLooper.processAllMessages();
-        verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+        verify(guts).openControls(
+                eq(true),
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
 
         assertEquals(View.VISIBLE, guts.getVisibility());
         mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
@@ -148,7 +156,12 @@
         });
 
         // Test doesn't support animation since the guts view is not attached.
-        doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+        doNothing().when(guts).openControls(
+                eq(true) /* shouldDoCircularReveal */,
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
 
         ExpandableNotificationRow realRow = createTestNotificationRow();
         NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
@@ -160,7 +173,12 @@
 
         mGutsManager.openGuts(row, 0, 0, menuItem);
         mTestableLooper.processAllMessages();
-        verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+        verify(guts).openControls(
+                eq(true),
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
 
         row.onDensityOrFontScaleChanged();
         mGutsManager.onDensityOrFontScaleChanged(row);
@@ -247,6 +265,56 @@
         assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
     }
 
+    @Test
+    public void testInitializeNotificationInfoView_showBlockingHelper() throws Exception {
+        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
+        ExpandableNotificationRow row = mHelper.createRow();
+        row.setBlockingHelperShowing(true);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+
+        verify(notificationInfoView).bindNotification(
+                any(PackageManager.class),
+                any(INotificationManager.class),
+                eq(statusBarNotification.getPackageName()),
+                isNull(),
+                anyInt(),
+                eq(statusBarNotification),
+                any(NotificationInfo.CheckSaveListener.class),
+                any(NotificationInfo.OnSettingsClickListener.class),
+                any(NotificationInfo.OnAppSettingsClickListener.class),
+                any(),
+                eq(true) /* isForBlockingHelper */,
+                eq(true) /* isUserSentimentNegative */);
+    }
+
+    @Test
+    public void testInitializeNotificationInfoView_dontShowBlockingHelper() throws Exception {
+        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
+        ExpandableNotificationRow row = mHelper.createRow();
+        row.setBlockingHelperShowing(false);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+
+        verify(notificationInfoView).bindNotification(
+                any(PackageManager.class),
+                any(INotificationManager.class),
+                eq(statusBarNotification.getPackageName()),
+                isNull(),
+                anyInt(),
+                eq(statusBarNotification),
+                any(NotificationInfo.CheckSaveListener.class),
+                any(NotificationInfo.OnSettingsClickListener.class),
+                any(NotificationInfo.OnAppSettingsClickListener.class),
+                any(),
+                eq(false) /* isForBlockingHelper */,
+                eq(true) /* isUserSentimentNegative */);
+    }
+
     ////////////////////////////////////////////////////////////////////////////////////////////////
     // Utility methods:
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 01664b2..7dda8b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -67,9 +67,13 @@
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -87,14 +91,21 @@
     private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
 
     private NotificationInfo mNotificationInfo;
-    private final INotificationManager mMockINotificationManager = mock(INotificationManager.class);
-    private final PackageManager mMockPackageManager = mock(PackageManager.class);
     private NotificationChannel mNotificationChannel;
     private NotificationChannel mDefaultNotificationChannel;
     private StatusBarNotification mSbn;
 
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private INotificationManager mMockINotificationManager;
+    @Mock private PackageManager mMockPackageManager;
+    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
+
     @Before
     public void setUp() throws Exception {
+        mDependency.injectTestDependency(
+                NotificationBlockingHelperManager.class,
+                mBlockingHelperManager);
+
         // Inflate the layout
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
@@ -311,7 +322,7 @@
     public void testbindNotification_BlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null,
-                null, null, true);
+                null, null, false, true);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -386,6 +397,27 @@
     }
 
     @Test
+    public void testCloseControls_blockingHelperDismissedIfShown() throws Exception {
+        mNotificationInfo.bindNotification(
+                mMockPackageManager,
+                mMockINotificationManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                1 /* numChannels */,
+                mSbn,
+                null /* checkSaveListener */,
+                null /* onSettingsClick */,
+                null /* onAppSettingsClick */,
+                null /* nonBlockablePkgs */,
+                true /* isForBlockingHelper */,
+                false /* isUserSentimentNegative */);
+
+        mNotificationInfo.closeControls(mNotificationInfo);
+
+        verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
+    }
+
+    @Test
     public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 2764254..de2e6bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.Notification;
@@ -23,33 +24,32 @@
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
-import android.widget.FrameLayout;
 import android.widget.RemoteViews;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
-import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.NotificationInflaterTest;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 /**
- * A helper class to create {@link ExpandableNotificationRow}
+ * A helper class to create {@link ExpandableNotificationRow} (for both individual and group
+ * notifications).
  */
 public class NotificationTestHelper {
 
+    static final String PKG = "com.android.systemui";
+    static final int UID = 1000;
+    private static final String GROUP_KEY = "gruKey";
+
     private final Context mContext;
     private final Instrumentation mInstrumentation;
     private int mId;
     private final NotificationGroupManager mGroupManager = new NotificationGroupManager();
     private ExpandableNotificationRow mRow;
-    private InflationException mException;
     private HeadsUpManager mHeadsUpManager;
-    protected static final String PKG = "com.android.systemui";
-    protected static final int UID = 1000;
 
     public NotificationTestHelper(Context context) {
         mContext = context;
@@ -57,57 +57,125 @@
         mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
     }
 
+    public ExpandableNotificationRow createRow() throws Exception {
+        return createRow(PKG, UID);
+    }
+
     public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
+        return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */);
+    }
+
+    public ExpandableNotificationRow createRow(Notification notification) throws Exception {
+        return generateRow(notification, PKG, UID, false /* isGroupRow */);
+    }
+
+    /**
+     * Returns an {@link ExpandableNotificationRow} group with the given number of child
+     * notifications.
+     */
+    public ExpandableNotificationRow createGroup(int numChildren) throws Exception {
+        ExpandableNotificationRow row = createGroupSummary(GROUP_KEY);
+        for (int i = 0; i < numChildren; i++) {
+            ExpandableNotificationRow childRow = createGroupChild(GROUP_KEY);
+            row.addChildNotification(childRow);
+        }
+        return row;
+    }
+
+    /** Returns a group notification with 2 child notifications. */
+    public ExpandableNotificationRow createGroup() throws Exception {
+        return createGroup(2);
+    }
+
+    private ExpandableNotificationRow createGroupSummary(String groupkey) throws Exception {
+        return createRow(PKG, UID, true /* isGroupSummary */, groupkey);
+    }
+
+    private ExpandableNotificationRow createGroupChild(String groupkey) throws Exception {
+        return createRow(PKG, UID, false /* isGroupSummary */, groupkey);
+    }
+
+    /**
+     * Creates a notification row with the given details.
+     *
+     * @param pkg package used for creating a {@link StatusBarNotification}
+     * @param uid uid used for creating a {@link StatusBarNotification}
+     * @param isGroupSummary whether the notification row is a group summary
+     * @param groupKey the group key for the notification group used across notifications
+     * @return a row with that's either a standalone notification or a group notification if the
+     *         groupKey is non-null
+     * @throws Exception
+     */
+    private ExpandableNotificationRow createRow(
+            String pkg,
+            int uid,
+            boolean isGroupSummary,
+            @Nullable String groupKey)
+            throws Exception {
         Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
                 R.drawable.ic_person)
                 .setCustomContentView(new RemoteViews(mContext.getPackageName(),
                         R.layout.custom_view_dark))
                 .build();
-        Notification notification = new Notification.Builder(mContext).setSmallIcon(
-                R.drawable.ic_person)
-                .setContentTitle("Title")
-                .setContentText("Text")
-                .setPublicVersion(publicVersion)
-                .build();
-        return createRow(notification, pkg, uid);
+        Notification.Builder notificationBuilder =
+                new Notification.Builder(mContext)
+                        .setSmallIcon(R.drawable.ic_person)
+                        .setContentTitle("Title")
+                        .setContentText("Text")
+                        .setPublicVersion(publicVersion);
+
+        // Group notification setup
+        if (isGroupSummary) {
+            notificationBuilder.setGroupSummary(true);
+        }
+        if (!TextUtils.isEmpty(groupKey)) {
+            notificationBuilder.setGroup(groupKey);
+        }
+
+        return generateRow(notificationBuilder.build(), pkg, uid, !TextUtils.isEmpty(groupKey));
     }
 
-    public ExpandableNotificationRow createRow() throws Exception {
-        return createRow(PKG, UID);
-    }
-
-    public ExpandableNotificationRow createRow(Notification notification) throws Exception {
-        return createRow(notification, PKG, UID);
-    }
-
-    public ExpandableNotificationRow createRow(Notification notification, String pkg, int uid)
+    private ExpandableNotificationRow generateRow(
+            Notification notification,
+            String pkg,
+            int uid,
+            boolean isGroupRow)
             throws Exception {
         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                 mContext.LAYOUT_INFLATER_SERVICE);
-        mInstrumentation.runOnMainSync(() -> {
-            mRow = (ExpandableNotificationRow) inflater.inflate(
-                    R.layout.status_bar_notification_row,
-                    null, false);
-        });
+        mInstrumentation.runOnMainSync(() ->
+                mRow = (ExpandableNotificationRow) inflater.inflate(
+                        R.layout.status_bar_notification_row,
+                        null /* root */,
+                        false /* attachToRoot */)
+        );
         ExpandableNotificationRow row = mRow;
         row.setGroupManager(mGroupManager);
         row.setHeadsUpManager(mHeadsUpManager);
         row.setAboveShelfChangedListener(aboveShelf -> {});
         UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
-        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, mId++, null, uid,
-                2000, notification, mUser, null, System.currentTimeMillis());
+        StatusBarNotification sbn = new StatusBarNotification(
+                pkg,
+                pkg,
+                mId++,
+                null /* tag */,
+                uid,
+                2000 /* initialPid */,
+                notification,
+                mUser,
+                null /* overrideGroupKey */,
+                System.currentTimeMillis());
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
         entry.row = row;
         entry.createIcons(mContext, sbn);
-        NotificationInflaterTest.runThenWaitForInflation(() -> row.updateNotification(entry),
+        NotificationInflaterTest.runThenWaitForInflation(
+                () -> row.updateNotification(entry),
                 row.getNotificationInflater());
-        return row;
-    }
 
-    public ExpandableNotificationRow createGroup() throws Exception {
-        ExpandableNotificationRow row = createRow();
-        row.addChildNotification(createRow());
-        row.addChildNotification(createRow());
+        // This would be done as part of onAsyncInflationFinished, but we skip large amounts of
+        // the callback chain, so we need to make up for not adding it to the group manager
+        // here.
+        mGroupManager.onEntryAdded(entry);
         return row;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index 42069e2..cd3031b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -16,7 +16,12 @@
 
 package com.android.systemui.statusbar.stack;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -25,33 +30,73 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.TestableDependency;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.FooterView;
+import com.android.systemui.statusbar.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
+/**
+ * Tests for {@link NotificationStackScrollLayout}.
+ */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class NotificationStackScrollLayoutTest extends SysuiTestCase {
 
     private NotificationStackScrollLayout mStackScroller;
-    private StatusBar mBar;
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private StatusBar mBar;
+    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
+    @Mock private NotificationGroupManager mGroupManager;
+    @Mock private ExpandHelper mExpandHelper;
+    @Mock private EmptyShadeView mEmptyShadeView;
 
     @Before
     @UiThreadTest
     public void setUp() throws Exception {
+        // Inject dependencies before initializing the layout
+        mDependency.injectTestDependency(
+                NotificationBlockingHelperManager.class,
+                mBlockingHelperManager);
+
+        NotificationShelf notificationShelf = spy(new NotificationShelf(getContext(), null));
         mStackScroller = new NotificationStackScrollLayout(getContext());
-        mBar = mock(StatusBar.class);
+        mStackScroller.setShelf(notificationShelf);
         mStackScroller.setStatusBar(mBar);
         mStackScroller.setScrimController(mock(ScrimController.class));
+        mStackScroller.setHeadsUpManager(mHeadsUpManager);
+        mStackScroller.setGroupManager(mGroupManager);
+        mStackScroller.setEmptyShadeView(mEmptyShadeView);
+
+        // Stub out functionality that isn't necessary to test.
+        doNothing().when(mBar)
+                .executeRunnableDismissingKeyguard(any(Runnable.class),
+                        any(Runnable.class),
+                        anyBoolean(),
+                        anyBoolean(),
+                        anyBoolean());
+        doNothing().when(mGroupManager).collapseAllGroups();
+        doNothing().when(mExpandHelper).cancelImmediately();
+        doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
     }
 
     @Test
@@ -75,26 +120,34 @@
 
     @Test
     public void updateEmptyView_dndSuppressing() {
-        EmptyShadeView view = mock(EmptyShadeView.class);
-        mStackScroller.setEmptyShadeView(view);
-        when(view.willBeGone()).thenReturn(true);
+        when(mEmptyShadeView.willBeGone()).thenReturn(true);
         when(mBar.areNotificationsHidden()).thenReturn(true);
 
         mStackScroller.updateEmptyShadeView(true);
 
-        verify(view).setText(R.string.dnd_suppressing_shade_text);
+        verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
     }
 
     @Test
     public void updateEmptyView_dndNotSuppressing() {
-        EmptyShadeView view = mock(EmptyShadeView.class);
-        mStackScroller.setEmptyShadeView(view);
-        when(view.willBeGone()).thenReturn(true);
+        mStackScroller.setEmptyShadeView(mEmptyShadeView);
+        when(mEmptyShadeView.willBeGone()).thenReturn(true);
         when(mBar.areNotificationsHidden()).thenReturn(false);
 
         mStackScroller.updateEmptyShadeView(true);
 
-        verify(view).setText(R.string.empty_shade_text);
+        verify(mEmptyShadeView).setText(R.string.empty_shade_text);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
+        mStackScroller.setExpandedHeight(0f);
+        verify(mBlockingHelperManager).setNotificationShadeExpanded(0f);
+        reset(mBlockingHelperManager);
+
+        mStackScroller.setExpandedHeight(100f);
+        verify(mBlockingHelperManager).setNotificationShadeExpanded(100f);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
index 80dc2c9..50b4f3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
@@ -60,7 +60,8 @@
                 NotificationChannels.SCREENSHOTS_HEADSUP,
                 NotificationChannels.STORAGE,
                 NotificationChannels.GENERAL,
-                NotificationChannels.BATTERY
+                NotificationChannels.BATTERY,
+                NotificationChannels.HINTS
         ));
         NotificationChannels.createAll(mContext);
         ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..1aa1af3
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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
+  -->
+
+<resources>
+    <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
+    <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..1aa1af3
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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
+  -->
+
+<resources>
+    <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
+    <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..1aa1af3
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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
+  -->
+
+<resources>
+    <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
+    <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..1aa1af3
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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
+  -->
+
+<resources>
+    <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
+    <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..1aa1af3
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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
+  -->
+
+<resources>
+    <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
+    <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 608970f..f9af31c 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -208,6 +208,10 @@
     // Package: android
     NOTE_ZEN_UPGRADE = 48;
 
+    // Notification to suggest automatic battery saver.
+    // Package: android
+    NOTE_AUTO_SAVER_SUGGESTION = 49;
+
     // 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
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 009e723..5114308 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -1036,6 +1036,18 @@
         }
 
         @Override
+        public void setAutofillFailure(int sessionId, @NonNull List<AutofillId> ids, int userId) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    service.setAutofillFailureLocked(sessionId, getCallingUid(), ids);
+                } else if (sVerbose) {
+                    Slog.v(TAG, "setAutofillFailure(): no service for " + userId);
+                }
+            }
+        }
+
+        @Override
         public void finishSession(int sessionId, int userId) {
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index ef0d7e6..0bb29a7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -404,6 +404,19 @@
     }
 
     @GuardedBy("mLock")
+    void setAutofillFailureLocked(int sessionId, int uid, @NonNull List<AutofillId> ids) {
+        if (!isEnabledLocked()) {
+            return;
+        }
+        final Session session = mSessions.get(sessionId);
+        if (session == null || uid != session.uid) {
+            Slog.v(TAG, "setAutofillFailure(): no session for " + sessionId + "(" + uid + ")");
+            return;
+        }
+        session.setAutofillFailureLocked(ids);
+    }
+
+    @GuardedBy("mLock")
     void finishSessionLocked(int sessionId, int uid) {
         if (!isEnabledLocked()) {
             return;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index bcb0bc4..706fb1a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1827,6 +1827,11 @@
             }
             viewState.setState(ViewState.STATE_STARTED_PARTITION);
             requestNewFillResponseLocked(flags);
+        } else {
+            if (sVerbose) {
+                Slog.v(TAG, "Not starting new partition for view " + id + ": "
+                        + viewState.getStateAsString());
+            }
         }
     }
 
@@ -2194,6 +2199,27 @@
         }
     }
 
+    /**
+     * Sets the state of views that failed to autofill.
+     */
+    @GuardedBy("mLock")
+    void setAutofillFailureLocked(@NonNull List<AutofillId> ids) {
+        for (int i = 0; i < ids.size(); i++) {
+            final AutofillId id = ids.get(i);
+            final ViewState viewState = mViewStates.get(id);
+            if (viewState == null) {
+                Slog.w(TAG, "setAutofillFailure(): no view for id " + id);
+                continue;
+            }
+            viewState.resetState(ViewState.STATE_AUTOFILLED);
+            final int state = viewState.getState();
+            viewState.setState(state | ViewState.STATE_AUTOFILL_FAILED);
+            if (sVerbose) {
+                Slog.v(TAG, "Changed state of " + id + " to " + viewState.getStateAsString());
+            }
+        }
+    }
+
     @GuardedBy("mLock")
     private void replaceResponseLocked(@NonNull FillResponse oldResponse,
             @NonNull FillResponse newResponse, @Nullable Bundle newClientState) {
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 97ab97b..9210de2 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -69,6 +69,8 @@
     public static final int STATE_RESTARTED_SESSION = 0x100;
     /** View is the URL bar of a package on compat mode. */
     public  static final int STATE_URL_BAR = 0x200;
+    /** View was asked to autofil but failed to do so. */
+    public static final int STATE_AUTOFILL_FAILED = 0x400;
 
     public final AutofillId id;
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5b5b80b..1037561 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13050,6 +13050,22 @@
         return mSleeping;
     }
 
+    void reportGlobalUsageEventLocked(int event) {
+        mUsageStatsService.reportEvent("android", mUserController.getCurrentUserId(), event);
+        int[] profiles = mUserController.getCurrentProfileIds();
+        if (profiles != null) {
+            for (int i = profiles.length - 1; i >= 0; i--) {
+                mUsageStatsService.reportEvent((String)null, profiles[i], event);
+            }
+        }
+    }
+
+    void reportCurWakefulnessUsageEventLocked() {
+        reportGlobalUsageEventLocked(mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE
+                ? UsageEvents.Event.SCREEN_INTERACTIVE
+                : UsageEvents.Event.SCREEN_NON_INTERACTIVE);
+    }
+
     void onWakefulnessChanged(int wakefulness) {
         synchronized(this) {
             boolean wasAwake = mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
@@ -13059,6 +13075,7 @@
             if (wasAwake != isAwake) {
                 // Also update state in a special way for running foreground services UI.
                 mServices.updateScreenStateLocked(isAwake);
+                reportCurWakefulnessUsageEventLocked();
                 mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
                         .sendToTarget();
             }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3c49ece..b338029 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -17,10 +17,15 @@
 package com.android.server.am;
 
 import android.app.ActivityManager;
+import android.app.job.JobProtoEnums;
 import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.hardware.usb.UsbManager;
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerSaveState;
@@ -34,6 +39,7 @@
 import android.os.ParcelFormatException;
 import android.os.PowerManagerInternal;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -70,7 +76,6 @@
 import java.nio.charset.CharsetDecoder;
 import java.nio.charset.CodingErrorAction;
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
@@ -438,6 +443,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteSyncStartLocked(name, uid);
+            StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, uid, null, name,
+                    StatsLog.SYNC_STATE_CHANGED__STATE__ON);
         }
     }
 
@@ -445,6 +452,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteSyncFinishLocked(name, uid);
+            StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, uid, null, name,
+                    StatsLog.SYNC_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -452,6 +461,9 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteJobStartLocked(name, uid);
+            StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uid, null,
+                    name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
+                    JobProtoEnums.STOP_REASON_UNKNOWN);
         }
     }
 
@@ -459,6 +471,9 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteJobFinishLocked(name, uid, stopReason);
+            StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uid, null,
+                    name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
+                    stopReason);
         }
     }
 
@@ -573,6 +588,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteStartSensorLocked(uid, sensor);
+            StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, uid, null, sensor,
+                    StatsLog.SENSOR_STATE_CHANGED__STATE__ON);
         }
     }
 
@@ -580,6 +597,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteStopSensorLocked(uid, sensor);
+            StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, uid, null,
+                    sensor, StatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -684,6 +703,13 @@
         }
     }
 
+    public void noteUsbConnectionState(boolean connected) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteUsbConnectionStateLocked(connected);
+        }
+    }
+
     public void notePhoneSignalStrength(SignalStrength signalStrength) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -724,6 +750,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteAudioOnLocked(uid);
+            StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, uid, null,
+                    StatsLog.AUDIO_STATE_CHANGED__STATE__ON);
         }
     }
 
@@ -731,6 +759,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteAudioOffLocked(uid);
+            StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, uid, null,
+                    StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -738,6 +768,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteVideoOnLocked(uid);
+            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uid, null,
+                    StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON);
         }
     }
 
@@ -745,6 +777,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteVideoOffLocked(uid);
+            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uid,
+                    null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
         }
     }
 
@@ -752,6 +786,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteResetAudioLocked();
+            StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, -1, null,
+                    StatsLog.AUDIO_STATE_CHANGED__STATE__RESET);
         }
     }
 
@@ -759,6 +795,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteResetVideoLocked();
+            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, -1, null,
+                    StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__RESET);
         }
     }
 
@@ -766,6 +804,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteFlashlightOnLocked(uid);
+            StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, uid, null,
+                    StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
         }
     }
 
@@ -773,6 +813,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteFlashlightOffLocked(uid);
+            StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, uid, null,
+                    StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -781,6 +823,8 @@
         if (DBG) Slog.d(TAG, "begin noteStartCamera");
         synchronized (mStats) {
             mStats.noteCameraOnLocked(uid);
+            StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, uid, null,
+                    StatsLog.CAMERA_STATE_CHANGED__STATE__ON);
         }
         if (DBG) Slog.d(TAG, "end noteStartCamera");
     }
@@ -789,6 +833,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteCameraOffLocked(uid);
+            StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, uid, null,
+                    StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -796,6 +842,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteResetCameraLocked();
+            StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, -1, null,
+                    StatsLog.CAMERA_STATE_CHANGED__STATE__RESET);
         }
     }
 
@@ -803,6 +851,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteResetFlashlightLocked();
+            StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, -1, null,
+                    StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__RESET);
         }
     }
 
@@ -1116,6 +1166,35 @@
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
     }
 
+    public final static class UsbConnectionReceiver extends BroadcastReceiver {
+        private static final String TAG = UsbConnectionReceiver.class.getSimpleName();
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+                final Intent usbState = context.registerReceiver(null, new IntentFilter(UsbManager.ACTION_USB_STATE));
+                if (usbState != null) {
+                    handleUsbState(usbState);
+                }
+            } else if (UsbManager.ACTION_USB_STATE.equals(action)) {
+                handleUsbState(intent);
+            }
+        }
+        private void handleUsbState(Intent intent) {
+            IBatteryStats bs = getService();
+            if (bs == null) {
+                Slog.w(TAG, "Could not access batterystats");
+                return;
+            }
+            boolean connected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
+            try {
+                bs.noteUsbConnectionState(connected);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not access batterystats: ", e);
+            }
+        }
+    }
+
     final class WakeupReasonThread extends Thread {
         private static final int MAX_REASON_SIZE = 512;
         private CharsetDecoder mDecoder;
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index da56ffd..73a7c3eb 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -168,7 +168,7 @@
                         "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
                 mWindowManager.deferSurfaceLayout();
                 try {
-                    mWindowManager.cleanupRecentsAnimation();
+                    mWindowManager.cleanupRecentsAnimation(moveHomeToTop);
 
                     // Move the home stack to the front
                     final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 0d125e0..f710690 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -47,6 +47,7 @@
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
 import android.app.KeyguardManager;
+import android.app.usage.UsageEvents;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
@@ -959,6 +960,8 @@
                 mInjector.getUserManagerInternal().setUserState(userId, uss.state);
             }
             if (foreground) {
+                // Make sure the old user is no longer considering the display to be on.
+                mInjector.reportGlobalUsageEventLocked(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
                 synchronized (mLock) {
                     mCurrentUserId = userId;
                     mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
@@ -966,6 +969,7 @@
                 mInjector.updateUserConfiguration();
                 updateCurrentProfileIds();
                 mInjector.getWindowManager().setCurrentUser(userId, getCurrentProfileIds());
+                mInjector.reportCurWakefulnessUsageEvent();
                 // Once the internal notion of the active user has switched, we lock the device
                 // with the option to show the user switcher on the keyguard.
                 if (mUserSwitchUiEnabled) {
@@ -2183,6 +2187,18 @@
             d.show();
         }
 
+        void reportGlobalUsageEventLocked(int event) {
+            synchronized (mService) {
+                mService.reportGlobalUsageEventLocked(event);
+            }
+        }
+
+        void reportCurWakefulnessUsageEvent() {
+            synchronized (mService) {
+                mService.reportCurWakefulnessUsageEventLocked();
+            }
+        }
+
         void stackSupervisorRemoveUser(int userId) {
             synchronized (mService) {
                 mService.mStackSupervisor.removeUserLocked(userId);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 041764f..c8b6b50 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1422,21 +1422,24 @@
                 direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
                         .append("/").append(caller).append(" uid:").append(uid).toString()));
         final int streamType;
-        if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
-            streamType = mVolumeControlStream;
-        } else {
-            final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
-            final boolean activeForReal;
-            if (maybeActiveStreamType == AudioSystem.STREAM_RING
-                    || maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
-                activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
-            } else {
-                activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
-            }
-            if (activeForReal || mVolumeControlStream == -1) {
-                streamType = maybeActiveStreamType;
-            } else {
+        synchronized (mForceControlStreamLock) {
+            // Request lock in case mVolumeControlStream is changed by other thread.
+            if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
                 streamType = mVolumeControlStream;
+            } else {
+                final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
+                final boolean activeForReal;
+                if (maybeActiveStreamType == AudioSystem.STREAM_RING
+                        || maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
+                    activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
+                } else {
+                    activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
+                }
+                if (activeForReal || mVolumeControlStream == -1) {
+                    streamType = maybeActiveStreamType;
+                } else {
+                    streamType = mVolumeControlStream;
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index a52dd0b..644e0a8 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -54,7 +54,7 @@
     private final FingerprintManager mFingerprintManager;
     protected boolean mDialogDismissed;
 
-    // Receives events from SystemUI
+    // Receives events from SystemUI and handles them before forwarding them to FingerprintDialog
     protected IFingerprintDialogReceiver mDialogReceiver = new IFingerprintDialogReceiver.Stub() {
         @Override // binder call
         public void onDialogDismissed(int reason) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index a5f8dc7..2ef754e 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -16,9 +16,9 @@
 
 package com.android.server.net;
 
-import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
+import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
 import static android.net.NetworkStats.ROAMING_NO;
@@ -42,9 +42,11 @@
 import android.service.NetworkStatsCollectionProto;
 import android.service.NetworkStatsCollectionStatsProto;
 import android.telephony.SubscriptionPlan;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.IntArray;
+import android.util.MathUtils;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -245,7 +247,10 @@
                     + " is forbidden for caller " + callerUid);
         }
 
-        final int bucketEstimate = (int) ((end - start) / mBucketDuration);
+        // 180 days of history should be enough for anyone; if we end up needing
+        // more, we'll dynamically grow the history object.
+        final int bucketEstimate = (int) MathUtils.constrain(((end - start) / mBucketDuration), 0,
+                (180 * DateUtils.DAY_IN_MILLIS) / mBucketDuration);
         final NetworkStatsHistory combined = new NetworkStatsHistory(
                 mBucketDuration, bucketEstimate, fields);
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 59cd561..210857e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5969,6 +5969,7 @@
     }
 
     private boolean isPackageSuspendedForUser(String pkg, int uid) {
+        final long identity = Binder.clearCallingIdentity();
         int userId = UserHandle.getUserId(uid);
         try {
             return mPackageManager.isPackageSuspendedForUser(pkg, userId);
@@ -5977,6 +5978,8 @@
         } catch (IllegalArgumentException ex) {
             // Package not found.
             return false;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5a63f29..9b67bbd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18890,7 +18890,7 @@
         return true;
     }
 
-    private static final class ClearStorageConnection implements ServiceConnection {
+    private final class ClearStorageConnection implements ServiceConnection {
         IMediaContainerService mContainerService;
 
         @Override
@@ -21611,37 +21611,35 @@
                 // the given package is involved with.
                 if (dumpState.onTitlePrinted()) pw.println();
 
-                try (final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120)) {
-                    ipw.println();
-                    ipw.println("Frozen packages:");
-                    ipw.increaseIndent();
-                    if (mFrozenPackages.size() == 0) {
-                        ipw.println("(none)");
-                    } else {
-                        for (int i = 0; i < mFrozenPackages.size(); i++) {
-                            ipw.println(mFrozenPackages.valueAt(i));
-                        }
+                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+                ipw.println();
+                ipw.println("Frozen packages:");
+                ipw.increaseIndent();
+                if (mFrozenPackages.size() == 0) {
+                    ipw.println("(none)");
+                } else {
+                    for (int i = 0; i < mFrozenPackages.size(); i++) {
+                        ipw.println(mFrozenPackages.valueAt(i));
                     }
-                    ipw.decreaseIndent();
                 }
+                ipw.decreaseIndent();
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
                 if (dumpState.onTitlePrinted()) pw.println();
 
-                try (final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120)) {
-                    ipw.println();
-                    ipw.println("Loaded volumes:");
-                    ipw.increaseIndent();
-                    if (mLoadedVolumes.size() == 0) {
-                        ipw.println("(none)");
-                    } else {
-                        for (int i = 0; i < mLoadedVolumes.size(); i++) {
-                            ipw.println(mLoadedVolumes.valueAt(i));
-                        }
+                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+                ipw.println();
+                ipw.println("Loaded volumes:");
+                ipw.increaseIndent();
+                if (mLoadedVolumes.size() == 0) {
+                    ipw.println("(none)");
+                } else {
+                    for (int i = 0; i < mLoadedVolumes.size(); i++) {
+                        ipw.println(mLoadedVolumes.valueAt(i));
                     }
-                    ipw.decreaseIndent();
                 }
+                ipw.decreaseIndent();
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
@@ -21770,63 +21768,61 @@
     }
 
     private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
-        try (final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ")) {
-            ipw.println();
-            ipw.println("Dexopt state:");
-            ipw.increaseIndent();
-            Collection<PackageParser.Package> packages = null;
-            if (packageName != null) {
-                PackageParser.Package targetPackage = mPackages.get(packageName);
-                if (targetPackage != null) {
-                    packages = Collections.singletonList(targetPackage);
-                } else {
-                    ipw.println("Unable to find package: " + packageName);
-                    return;
-                }
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println();
+        ipw.println("Dexopt state:");
+        ipw.increaseIndent();
+        Collection<PackageParser.Package> packages = null;
+        if (packageName != null) {
+            PackageParser.Package targetPackage = mPackages.get(packageName);
+            if (targetPackage != null) {
+                packages = Collections.singletonList(targetPackage);
             } else {
-                packages = mPackages.values();
+                ipw.println("Unable to find package: " + packageName);
+                return;
             }
+        } else {
+            packages = mPackages.values();
+        }
 
-            for (PackageParser.Package pkg : packages) {
-                ipw.println("[" + pkg.packageName + "]");
-                ipw.increaseIndent();
-                mPackageDexOptimizer.dumpDexoptState(ipw, pkg,
-                        mDexManager.getPackageUseInfoOrDefault(pkg.packageName));
-                ipw.decreaseIndent();
-            }
+        for (PackageParser.Package pkg : packages) {
+            ipw.println("[" + pkg.packageName + "]");
+            ipw.increaseIndent();
+            mPackageDexOptimizer.dumpDexoptState(ipw, pkg,
+                    mDexManager.getPackageUseInfoOrDefault(pkg.packageName));
+            ipw.decreaseIndent();
         }
     }
 
     private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
-        try (final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ")) {
-            ipw.println();
-            ipw.println("Compiler stats:");
-            ipw.increaseIndent();
-            Collection<PackageParser.Package> packages = null;
-            if (packageName != null) {
-                PackageParser.Package targetPackage = mPackages.get(packageName);
-                if (targetPackage != null) {
-                    packages = Collections.singletonList(targetPackage);
-                } else {
-                    ipw.println("Unable to find package: " + packageName);
-                    return;
-                }
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println();
+        ipw.println("Compiler stats:");
+        ipw.increaseIndent();
+        Collection<PackageParser.Package> packages = null;
+        if (packageName != null) {
+            PackageParser.Package targetPackage = mPackages.get(packageName);
+            if (targetPackage != null) {
+                packages = Collections.singletonList(targetPackage);
             } else {
-                packages = mPackages.values();
+                ipw.println("Unable to find package: " + packageName);
+                return;
             }
+        } else {
+            packages = mPackages.values();
+        }
 
-            for (PackageParser.Package pkg : packages) {
-                ipw.println("[" + pkg.packageName + "]");
-                ipw.increaseIndent();
+        for (PackageParser.Package pkg : packages) {
+            ipw.println("[" + pkg.packageName + "]");
+            ipw.increaseIndent();
 
-                CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.packageName);
-                if (stats == null) {
-                    ipw.println("(No recorded stats)");
-                } else {
-                    stats.dump(ipw);
-                }
-                ipw.decreaseIndent();
+            CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.packageName);
+            if (stats == null) {
+                ipw.println("(No recorded stats)");
+            } else {
+                stats.dump(ipw);
             }
+            ipw.decreaseIndent();
         }
     }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 8901b04..d123099c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1428,7 +1428,7 @@
 
     void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
-            throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
+            throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
         }
 
         WallpaperData wallpaper = null;
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index d000bb6..1f95868 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -110,6 +110,11 @@
         SurfaceAnimator mSurfaceAnimator;
 
         /**
+         * Determines whether the dim layer should animate before destroying.
+         */
+        boolean mAnimateExit = true;
+
+        /**
          * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
          * details on Dim lifecycle.
          */
@@ -260,6 +265,12 @@
         }
     }
 
+    void dontAnimateExit() {
+        if (mDimState != null) {
+            mDimState.mAnimateExit = false;
+        }
+    }
+
     /**
      * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
      * described in {@link #resetDimStates}.
@@ -274,7 +285,11 @@
         }
 
         if (!mDimState.mDimming) {
-            startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
+            if (!mDimState.mAnimateExit) {
+                t.destroy(mDimState.mDimLayer);
+            } else {
+                startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
+            }
             mDimState = null;
             return false;
         } else {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 87a7a74..1018848 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -41,7 +41,6 @@
 import android.util.Slog;import android.util.proto.ProtoOutputStream;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
-import android.util.proto.ProtoOutputStream;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationTarget;
@@ -309,12 +308,15 @@
         mCallbacks.onAnimationFinished(false /* moveHomeToTop */);
     }
 
-    void cleanupAnimation() {
+    void cleanupAnimation(boolean moveHomeToTop) {
         if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations="
                 + mPendingAnimations.size());
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
             adapter.mTask.setCanAffectSystemUiFlags(true);
+            if (moveHomeToTop) {
+                adapter.mTask.dontAnimateDimExit();
+            }
             adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
         }
         mPendingAnimations.clear();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e4722f9..e8d3210 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -645,6 +645,10 @@
         return mCanAffectSystemUiFlags;
     }
 
+    void dontAnimateDimExit() {
+        mDimmer.dontAnimateExit();
+    }
+
     @Override
     public String toString() {
         return "{taskId=" + mTaskId + " appTokens=" + mChildren + " mdr=" + mDeferRemoval + "}";
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 449aa2c..0b5c006 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2700,10 +2700,10 @@
         }
     }
 
-    public void cleanupRecentsAnimation() {
+    public void cleanupRecentsAnimation(boolean moveHomeToTop) {
         synchronized (mWindowMap) {
             if (mRecentsAnimationController != null) {
-                mRecentsAnimationController.cleanupAnimation();
+                mRecentsAnimationController.cleanupAnimation(moveHomeToTop);
                 mRecentsAnimationController = null;
                 mAppTransition.updateBooster();
             }
diff --git a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
index e9d4482..ccb4f59 100644
--- a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
+++ b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
@@ -19,6 +19,7 @@
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
+#include "android-base/strings.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
 
@@ -33,24 +34,36 @@
 
 #define DRIVER_NAME "/dev/usb_accessory"
 
-#define USB_IN_JACK_NAME "USB in Jack"
-#define USB_OUT_JACK_NAME "USB out Jack"
+#define USB_IN_JACK_SUFFIX "Input Jack"
+#define USB_OUT_JACK_SUFFIX "Output Jack"
 
 namespace android
 {
 
-static jboolean is_jack_connected(jint card, const char* control) {
+static struct mixer_ctl* find_mixer_with_suffix(struct mixer* card_mixer, const char* suffix) {
+    int id = 0;
+    struct mixer_ctl* ctl;
+    while ((ctl = mixer_get_ctl(card_mixer, id++)) != NULL) {
+        const char* name = mixer_ctl_get_name(ctl);
+        if (android::base::EndsWith(name, suffix)) {
+          return ctl;
+        }
+    }
+    return NULL;
+}
+
+static jboolean is_jack_connected(jint card, const char* suffix) {
   struct mixer* card_mixer = mixer_open(card);
   if (card_mixer == NULL) {
     return true;
   }
-  struct mixer_ctl* ctl = mixer_get_ctl_by_name(card_mixer, control);
+  struct mixer_ctl* ctl = find_mixer_with_suffix(card_mixer, suffix);
   if (!ctl) {
     return true;
   }
   mixer_ctl_update(ctl);
   int val = mixer_ctl_get_value(ctl, 0);
-  ALOGI("JACK %s - value %d\n", control, val);
+  ALOGI("%s - value %d\n", mixer_ctl_get_name(ctl), val);
   mixer_close(card_mixer);
 
   return val != 0;
@@ -66,33 +79,31 @@
     }
 
     jboolean has_jack = false;
-    if ((mixer_get_ctl_by_name(card_mixer, USB_IN_JACK_NAME) != NULL) ||
-            (mixer_get_ctl_by_name(card_mixer, USB_OUT_JACK_NAME) != NULL)) {
+    if ((find_mixer_with_suffix(card_mixer, USB_IN_JACK_SUFFIX) != NULL) ||
+            (find_mixer_with_suffix(card_mixer, USB_OUT_JACK_SUFFIX) != NULL)) {
         has_jack = true;
     }
     mixer_close(card_mixer);
     return has_jack;
 }
 
-
 static jboolean android_server_UsbAlsaJackDetector_inputJackConnected(JNIEnv* /* env */,
                                                                       jobject /* thiz */,
                                                                       jint card)
 {
-    return is_jack_connected(card, USB_IN_JACK_NAME);
+    return is_jack_connected(card, USB_IN_JACK_SUFFIX);
 }
 
-
 static jboolean android_server_UsbAlsaJackDetector_outputJackConnected(JNIEnv* /* env */,
                                                                        jobject /* thiz */,
                                                                        jint card)
 {
-    return is_jack_connected(card, USB_OUT_JACK_NAME);
+    return is_jack_connected(card, USB_OUT_JACK_SUFFIX);
 }
 
 static void android_server_UsbAlsaJackDetector_jackDetect(JNIEnv* env,
-                                                                                                        jobject thiz,
-                                                                                                        jint card) {
+                                                          jobject thiz,
+                                                          jint card) {
     jclass jdclass = env->GetObjectClass(thiz);
     jmethodID method_jackDetectCallback = env->GetMethodID(jdclass, "jackDetectCallback", "()Z");
     if (method_jackDetectCallback == NULL) {
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 6fdb029..3c10b03 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -389,6 +389,14 @@
             sentIntents.add(intent);
             return 0;
         }
+
+        @Override
+        void reportGlobalUsageEventLocked(int event) {
+        }
+
+        @Override
+        void reportCurWakefulnessUsageEvent() {
+        }
     }
 
     private static class TestHandler extends Handler {
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 57dd808..6769e40 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -18,8 +18,12 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -99,16 +103,23 @@
     private MockSurfaceBuildingContainer mHost;
     private Dimmer mDimmer;
     private SurfaceControl.Transaction mTransaction;
+    private Dimmer.SurfaceAnimatorStarter mSurfaceAnimatorStarter;
+
+    private static class SurfaceAnimatorStarterImpl implements Dimmer.SurfaceAnimatorStarter {
+        @Override
+        public void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
+                AnimationAdapter anim, boolean hidden) {
+            surfaceAnimator.mAnimationFinishedCallback.run();
+        }
+    }
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
         mHost = new MockSurfaceBuildingContainer();
-
+        mSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl());
         mTransaction = mock(SurfaceControl.Transaction.class);
-        mDimmer = new Dimmer(mHost,
-                (surfaceAnimator, t, anim, hidden) -> surfaceAnimator.mAnimationFinishedCallback
-                        .run());
+        mDimmer = new Dimmer(mHost, mSurfaceAnimatorStarter);
     }
 
     @Test
@@ -202,6 +213,8 @@
         mDimmer.resetDimStates();
 
         mDimmer.updateDims(mTransaction, new Rect());
+        verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any(
+                SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
         verify(dimLayer).destroy();
     }
 
@@ -243,6 +256,25 @@
         verify(mTransaction).setPosition(dimLayer, 10, 10);
     }
 
+    @Test
+    public void testRemoveDimImmediately() throws Exception {
+        TestWindowContainer child = new TestWindowContainer();
+        mHost.addChild(child, 0);
+
+        mDimmer.dimAbove(mTransaction, child, 1);
+        SurfaceControl dimLayer = getDimLayer();
+        mDimmer.updateDims(mTransaction, new Rect());
+        verify(mTransaction, times(1)).show(dimLayer);
+
+        reset(mSurfaceAnimatorStarter);
+        mDimmer.dontAnimateExit();
+        mDimmer.resetDimStates();
+        mDimmer.updateDims(mTransaction, new Rect());
+        verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any(
+                SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
+        verify(mTransaction).destroy(dimLayer);
+    }
+
     private SurfaceControl getDimLayer() {
         return mDimmer.mDimState.mDimLayer;
     }
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 00826e0..b070e03 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -16,6 +16,7 @@
 package com.android.server.usage;
 
 import android.app.usage.ConfigurationStats;
+import android.app.usage.EventStats;
 import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
@@ -23,10 +24,18 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
+import java.util.List;
+
 class IntervalStats {
     public long beginTime;
     public long endTime;
     public long lastTimeSaved;
+    public long lastInteractiveTime;
+    public long lastNonInteractiveTime;
+    public long interactiveDuration;
+    public int interactiveCount;
+    public long nonInteractiveDuration;
+    public int nonInteractiveCount;
     public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
     public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
     public Configuration activeConfiguration;
@@ -171,6 +180,60 @@
         usageStats.mAppLaunchCount += 1;
     }
 
+    private void commitInteractiveTime(long timeStamp) {
+        if (lastInteractiveTime != 0) {
+            interactiveDuration += timeStamp - lastInteractiveTime;
+            lastInteractiveTime = 0;
+        }
+        if (lastNonInteractiveTime != 0) {
+            nonInteractiveDuration += timeStamp - lastNonInteractiveTime;
+            lastNonInteractiveTime = 0;
+        }
+    }
+
+    void commitTime(long timeStamp) {
+        commitInteractiveTime(timeStamp);
+    }
+
+    void updateScreenInteractive(long timeStamp) {
+        if (lastInteractiveTime != 0) {
+            // Already interactive, just keep running.
+            return;
+        }
+        commitInteractiveTime(timeStamp);
+        lastInteractiveTime = timeStamp;
+        interactiveCount++;
+    }
+
+    void updateScreenNonInteractive(long timeStamp) {
+        if (lastNonInteractiveTime != 0) {
+            // Already non-interactive, just keep running.
+            return;
+        }
+        commitInteractiveTime(timeStamp);
+        lastNonInteractiveTime = timeStamp;
+        nonInteractiveCount++;
+    }
+
+    private void addOneEventStats(List<EventStats> out, int event, int count, long duration) {
+        if (count != 0 || duration != 0) {
+            EventStats ev = new EventStats();
+            ev.mEventType = event;
+            ev.mCount = count;
+            ev.mTotalTime = duration;
+            ev.mBeginTimeStamp = beginTime;
+            ev.mEndTimeStamp = endTime;
+            out.add(ev);
+        }
+    }
+
+    void addEventStatsTo(List<EventStats> out) {
+        addOneEventStats(out, UsageEvents.Event.SCREEN_INTERACTIVE, interactiveCount,
+                interactiveDuration);
+        addOneEventStats(out, UsageEvents.Event.SCREEN_NON_INTERACTIVE, nonInteractiveCount,
+                nonInteractiveDuration);
+    }
+
     private String getCachedStringRef(String str) {
         final int index = mStringCache.indexOf(str);
         if (index < 0) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b8317b4..2258b24 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -25,6 +25,7 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.ConfigurationStats;
+import android.app.usage.EventStats;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManager;
@@ -486,6 +487,23 @@
     /**
      * Called by the Binder stub.
      */
+    List<EventStats> queryEventStats(int userId, int bucketType, long beginTime,
+            long endTime) {
+        synchronized (mLock) {
+            final long timeNow = checkAndGetTimeLocked();
+            if (!validRange(timeNow, beginTime, endTime)) {
+                return null;
+            }
+
+            final UserUsageStatsService service =
+                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+            return service.queryEventStats(bucketType, beginTime, endTime);
+        }
+    }
+
+    /**
+     * Called by the Binder stub.
+     */
     UsageEvents queryEvents(int userId, long beginTime, long endTime,
             boolean shouldObfuscateInstantApps) {
         synchronized (mLock) {
@@ -713,6 +731,28 @@
         }
 
         @Override
+        public ParceledListSlice<EventStats> queryEventStats(int bucketType,
+                long beginTime, long endTime, String callingPackage) throws RemoteException {
+            if (!hasPermission(callingPackage)) {
+                return null;
+            }
+
+            final int userId = UserHandle.getCallingUserId();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final List<EventStats> results =
+                        UsageStatsService.this.queryEventStats(userId, bucketType,
+                                beginTime, endTime);
+                if (results != null) {
+                    return new ParceledListSlice<>(results);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return null;
+        }
+
+        @Override
         public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
             if (!hasPermission(callingPackage)) {
                 return null;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index bcfc421..2287b27 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -27,6 +27,7 @@
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
 import android.util.ArrayMap;
+import android.util.Pair;
 
 import java.io.IOException;
 import java.net.ProtocolException;
@@ -37,6 +38,9 @@
 final class UsageStatsXmlV1 {
     private static final String TAG = "UsageStatsXmlV1";
 
+    private static final String INTERACTIVE_TAG = "interactive";
+    private static final String NON_INTERACTIVE_TAG = "non-interactive";
+
     private static final String PACKAGES_TAG = "packages";
     private static final String PACKAGE_TAG = "package";
 
@@ -99,6 +103,14 @@
         }
     }
 
+    private static Pair<Integer, Long> loadCountAndTime(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        int count = XmlUtils.readIntAttribute(parser, COUNT_ATTR, 0);
+        long time = XmlUtils.readLongAttribute(parser, TIME_ATTR, 0);
+        XmlUtils.skipCurrentTag(parser);
+        return new Pair<>(count, time);
+    }
+
     private static void loadChooserCounts(
             XmlPullParser parser, UsageStats usageStats, String action)
             throws XmlPullParserException, IOException {
@@ -202,6 +214,14 @@
         xml.endTag(null, PACKAGE_TAG);
     }
 
+    private static void writeCountAndTime(XmlSerializer xml, String tag, int count, long time)
+            throws IOException {
+        xml.startTag(null, tag);
+        XmlUtils.writeIntAttribute(xml, COUNT_ATTR, count);
+        XmlUtils.writeLongAttribute(xml, TIME_ATTR, time);
+        xml.endTag(null, tag);
+    }
+
     private static void writeChooserCounts(XmlSerializer xml, final UsageStats usageStats)
             throws IOException {
         if (usageStats == null || usageStats.mChooserCounts == null ||
@@ -320,6 +340,18 @@
 
             final String tag = parser.getName();
             switch (tag) {
+                case INTERACTIVE_TAG: {
+                    Pair<Integer, Long> result = loadCountAndTime(parser);
+                    statsOut.interactiveCount = result.first;
+                    statsOut.interactiveDuration = result.second;
+                } break;
+
+                case NON_INTERACTIVE_TAG: {
+                    Pair<Integer, Long> result = loadCountAndTime(parser);
+                    statsOut.nonInteractiveCount = result.first;
+                    statsOut.nonInteractiveDuration = result.second;
+                } break;
+
                 case PACKAGE_TAG:
                     loadUsageStats(parser, statsOut);
                     break;
@@ -346,6 +378,11 @@
     public static void write(XmlSerializer xml, IntervalStats stats) throws IOException {
         XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime);
 
+        writeCountAndTime(xml, INTERACTIVE_TAG, stats.interactiveCount, stats.interactiveDuration);
+
+        writeCountAndTime(xml, NON_INTERACTIVE_TAG, stats.nonInteractiveCount,
+                stats.nonInteractiveDuration);
+
         xml.startTag(null, PACKAGES_TAG);
         final int statsCount = stats.packageStats.size();
         for (int i = 0; i < statsCount; i++) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index afc3d59..6ad374b 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -17,6 +17,7 @@
 package com.android.server.usage;
 
 import android.app.usage.ConfigurationStats;
+import android.app.usage.EventStats;
 import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
@@ -191,21 +192,31 @@
         }
 
         for (IntervalStats stats : mCurrentStats) {
-            if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
-                stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
-            } else if (event.mEventType == UsageEvents.Event.CHOOSER_ACTION) {
-                stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
-                String[] annotations = event.mContentAnnotations;
-                if (annotations != null) {
-                    for (String annotation : annotations) {
-                        stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
+            switch (event.mEventType) {
+                case UsageEvents.Event.CONFIGURATION_CHANGE: {
+                    stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
+                } break;
+                case UsageEvents.Event.CHOOSER_ACTION: {
+                    stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
+                    String[] annotations = event.mContentAnnotations;
+                    if (annotations != null) {
+                        for (String annotation : annotations) {
+                            stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
+                        }
                     }
-                }
-            } else {
-                stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
-                if (incrementAppLaunch) {
-                    stats.incrementAppLaunchCount(event.mPackage);
-                }
+                } break;
+                case UsageEvents.Event.SCREEN_INTERACTIVE: {
+                    stats.updateScreenInteractive(event.mTimeStamp);
+                } break;
+                case UsageEvents.Event.SCREEN_NON_INTERACTIVE: {
+                    stats.updateScreenNonInteractive(event.mTimeStamp);
+                } break;
+                default: {
+                    stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+                    if (incrementAppLaunch) {
+                        stats.incrementAppLaunchCount(event.mPackage);
+                    }
+                } break;
             }
         }
 
@@ -246,6 +257,15 @@
                 }
             };
 
+    private static final StatCombiner<EventStats> sEventStatsCombiner =
+            new StatCombiner<EventStats>() {
+                @Override
+                public void combine(IntervalStats stats, boolean mutable,
+                        List<EventStats> accResult) {
+                    stats.addEventStatsTo(accResult);
+                }
+            };
+
     /**
      * Generic query method that selects the appropriate IntervalStats for the specified time range
      * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
@@ -325,6 +345,10 @@
         return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
     }
 
+    List<EventStats> queryEventStats(int bucketType, long beginTime, long endTime) {
+        return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner);
+    }
+
     UsageEvents queryEvents(final long beginTime, final long endTime,
             boolean obfuscateInstantApps) {
         final ArraySet<String> names = new ArraySet<>();
@@ -448,6 +472,7 @@
             }
 
             stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1);
+            stat.commitTime(mDailyExpiryDate.getTimeInMillis() - 1);
         }
 
         persistActiveStats();
@@ -640,6 +665,18 @@
         }
     }
 
+    void printEventAggregation(IndentingPrintWriter pw, String label, int count, long duration,
+            boolean prettyDates) {
+        if (count != 0 || duration != 0) {
+            pw.print(label);
+            pw.print(": ");
+            pw.print(count);
+            pw.print("x for ");
+            pw.print(formatElapsedTime(duration, prettyDates));
+            pw.println();
+        }
+    }
+
     void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
             boolean prettyDates, boolean skipEvents, String pkg) {
         if (prettyDates) {
@@ -713,6 +750,13 @@
                 pw.println();
             }
             pw.decreaseIndent();
+            pw.println("event aggregations");
+            pw.increaseIndent();
+            printEventAggregation(pw, "screen-interactive", stats.interactiveCount,
+                    stats.interactiveDuration, prettyDates);
+            printEventAggregation(pw, "screen-non-interactive", stats.nonInteractiveCount,
+                    stats.nonInteractiveDuration, prettyDates);
+            pw.decreaseIndent();
         }
 
         // The last 24 hours of events is already printed in the non checkin dump
@@ -781,6 +825,10 @@
                 return "SLICE_PINNED";
             case UsageEvents.Event.SLICE_PINNED_PRIV:
                 return "SLICE_PINNED_PRIV";
+            case UsageEvents.Event.SCREEN_INTERACTIVE:
+                return "SCREEN_INTERACTIVE";
+            case UsageEvents.Event.SCREEN_NON_INTERACTIVE:
+                return "SCREEN_NON_INTERACTIVE";
             default:
                 return "UNKNOWN";
         }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c79eec0..8b8310f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -951,6 +951,8 @@
      * If user has explicitly disabled some packages in the list, won't re-enable.
      * Other carrier specific apps which are not in this list may be disabled for current carrier,
      * and only be re-enabled when this config for another carrier includes it.
+     *
+     * @hide
      */
     public static final String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a12a7a0..da5bd84 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6349,34 +6349,39 @@
      *
      * @param enable Whether to enable mobile data.
      *
-     * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
      */
-    @Deprecated
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(boolean enable) {
-        setUserMobileDataEnabled(enable);
+        setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
     }
 
     /**
      * @hide
-     * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
+     * @deprecated use {@link #setDataEnabled(boolean)} instead.
     */
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(int subId, boolean enable) {
-        setUserMobileDataEnabled(subId, enable);
+        try {
+            Log.d(TAG, "setDataEnabled: enabled=" + enable);
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setUserDataEnabled(subId, enable);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
+        }
     }
 
     /**
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+     * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @SystemApi
     @Deprecated
     public boolean getDataEnabled() {
-        return isUserMobileDataEnabled();
+        return isDataEnabled();
     }
 
     /**
@@ -6396,22 +6401,28 @@
      * {@link ConnectivityManager#getRestrictBackgroundStatus}.
      *
      * @return true if mobile data is enabled.
-     *
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
      */
-    @Deprecated
     public boolean isDataEnabled() {
-        return isUserMobileDataEnabled();
+        return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
     }
 
     /**
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+     * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @Deprecated
     @SystemApi
     public boolean getDataEnabled(int subId) {
-        return isUserMobileDataEnabled(subId);
+        boolean retVal = false;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                retVal = telephony.isUserDataEnabled(subId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
+        } catch (NullPointerException e) {
+        }
+        return retVal;
     }
 
     /** @hide */
@@ -7684,56 +7695,12 @@
     }
 
     /**
-     * Turns mobile data on or off.
-     * If the {@link TelephonyManager} object has been created with
-     * {@link #createForSubscriptionId}, this API applies to the given subId.
-     * Otherwise, it applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
-     *
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
-     * app has carrier privileges (see {@link #hasCarrierPrivileges}.
-     *
-     * @param enable Whether to enable mobile data.
-     */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setUserMobileDataEnabled(boolean enable) {
-        setUserMobileDataEnabled(
-                getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
-    }
-
-    /**
-     * Returns whether mobile data is enabled or not per user setting. There are other factors
-     * that could disable mobile data, but they are not considered here.
-     *
-     * If this object has been created with {@link #createForSubscriptionId}, applies to the given
-     * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
-     *
-     * <p>Requires one of the following permissions:
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}.
-     *
-     * <p>Note that this does not take into account any data restrictions that may be present on the
-     * calling app. Such restrictions may be inspected with
-     * {@link ConnectivityManager#getRestrictBackgroundStatus}.
-     *
-     * @return true if mobile data is enabled.
-     */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.ACCESS_NETWORK_STATE,
-            android.Manifest.permission.MODIFY_PHONE_STATE
-    })
-    public boolean isUserMobileDataEnabled() {
-        return isUserMobileDataEnabled(
-                getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
-    }
-
-    /**
      * @hide
-     * Unlike isUserMobileDataEnabled, this API also evaluates carrierDataEnabled,
-     * policyDataEnabled etc to give a final decision.
+     * It's similar to isDataEnabled, but unlike isDataEnabled, this API also evaluates
+     * carrierDataEnabled, policyDataEnabled etc to give a final decision of whether mobile data is
+     * capable of using.
      */
-    public boolean isMobileDataEnabled() {
+    public boolean isDataCapable() {
         boolean retVal = false;
         try {
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
@@ -7748,35 +7715,6 @@
     }
 
     /**
-     * Utility class of {@link #isUserMobileDataEnabled()};
-     */
-    private boolean isUserMobileDataEnabled(int subId) {
-        boolean retVal = false;
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                retVal = telephony.isUserDataEnabled(subId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
-        } catch (NullPointerException e) {
-        }
-        return retVal;
-    }
-
-    /** Utility method of {@link #setUserMobileDataEnabled(boolean)} */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    private void setUserMobileDataEnabled(int subId, boolean enable) {
-        try {
-            Log.d(TAG, "setUserMobileDataEnabled: enabled=" + enable);
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.setUserDataEnabled(subId, enable);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
-        }
-    }
-
-    /**
      * In this mode, modem will not send specified indications when screen is off.
      * @hide
      */
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
index 8b81b0d..bcad554 100644
--- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -26,6 +26,7 @@
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -49,10 +50,11 @@
     /**
      * Handle preinstalled carrier apps which should be disabled until a matching SIM is inserted.
      *
-     * Evaluates the list of applications in config_disabledUntilUsedPreinstalledCarrierApps. We
-     * want to disable each such application which is present on the system image until the user
-     * inserts a SIM which causes that application to gain carrier privilege (indicating a "match"),
-     * without interfering with the user if they opt to enable/disable the app explicitly.
+     * Evaluates the list of applications in
+     * {@link SystemConfig#getDisabledUntilUsedPreinstalledCarrierApps()}. We want to disable each
+     * such application which is present on the system image until the user inserts a SIM which
+     * causes that application to gain carrier privilege (indicating a "match"), without interfering
+     * with the user if they opt to enable/disable the app explicitly.
      *
      * So, for each such app, we either disable until used IFF the app is not carrier privileged AND
      * in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if
@@ -76,8 +78,8 @@
             Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
         }
         SystemConfig config = SystemConfig.getInstance();
-        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
-                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        ArraySet<String> systemCarrierAppsDisabledUntilUsed =
+                config.getDisabledUntilUsedPreinstalledCarrierApps();
         ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
                 config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
         disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager,
@@ -102,8 +104,10 @@
             Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
         }
         SystemConfig config = SystemConfig.getInstance();
-        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
-                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        ArraySet<String> systemCarrierAppsDisabledUntilUsed =
+                config.getDisabledUntilUsedPreinstalledCarrierApps();
+
+
         ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
                 config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
         disableCarrierAppsUntilPrivileged(callingPackage, packageManager,
@@ -116,7 +120,7 @@
     public static void disableCarrierAppsUntilPrivileged(String callingPackage,
             IPackageManager packageManager, @Nullable TelephonyManager telephonyManager,
             ContentResolver contentResolver, int userId,
-            String[] systemCarrierAppsDisabledUntilUsed,
+            ArraySet<String> systemCarrierAppsDisabledUntilUsed,
             ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) {
         List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
                 userId, systemCarrierAppsDisabledUntilUsed);
@@ -286,8 +290,8 @@
      */
     public static List<ApplicationInfo> getDefaultCarrierAppCandidates(
             IPackageManager packageManager, int userId) {
-        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
-                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        ArraySet<String> systemCarrierAppsDisabledUntilUsed =
+                SystemConfig.getInstance().getDisabledUntilUsedPreinstalledCarrierApps();
         return getDefaultCarrierAppCandidatesHelper(packageManager, userId,
                 systemCarrierAppsDisabledUntilUsed);
     }
@@ -295,14 +299,19 @@
     private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
             IPackageManager packageManager,
             int userId,
-            String[] systemCarrierAppsDisabledUntilUsed) {
-        if (systemCarrierAppsDisabledUntilUsed == null
-                || systemCarrierAppsDisabledUntilUsed.length == 0) {
+            ArraySet<String> systemCarrierAppsDisabledUntilUsed) {
+        if (systemCarrierAppsDisabledUntilUsed == null) {
             return null;
         }
-        List<ApplicationInfo> apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length);
-        for (int i = 0; i < systemCarrierAppsDisabledUntilUsed.length; i++) {
-            String packageName = systemCarrierAppsDisabledUntilUsed[i];
+
+        int size = systemCarrierAppsDisabledUntilUsed.size();
+        if (size == 0) {
+            return null;
+        }
+
+        List<ApplicationInfo> apps = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            String packageName = systemCarrierAppsDisabledUntilUsed.valueAt(i);
             ApplicationInfo ai =
                     getApplicationInfoIfSystemApp(packageManager, userId, packageName);
             if (ai != null) {