Merge "Make autofill save UI RTL compliant" into pi-dev
diff --git a/Android.bp b/Android.bp
index 1caa497..037c29c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -462,6 +462,8 @@
         "media/java/android/media/session/ISessionController.aidl",
         "media/java/android/media/session/ISessionControllerCallback.aidl",
         "media/java/android/media/session/ISessionManager.aidl",
+        "media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl",
+        "media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl",
         "media/java/android/media/tv/ITvInputClient.aidl",
         "media/java/android/media/tv/ITvInputHardware.aidl",
         "media/java/android/media/tv/ITvInputHardwareCallback.aidl",
diff --git a/Android.mk b/Android.mk
index e2f88e8..ee8fbe0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -861,39 +861,42 @@
 
 # ==== hiddenapi lists =======================================
 
-# Copy blacklist and light greylist over into the build folder.
+# Copy light and dark greylist over into the build folder.
 # This is for ART buildbots which need to mock these lists and have alternative
 # rules for building them. Other rules in the build system should depend on the
 # files in the build folder.
 
-$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
-                            $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
-
 # Temporarily merge light greylist from two files. Vendor list will become dark
 # grey once we remove the UI toast.
 $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): frameworks/base/config/hiddenapi-light-greylist.txt \
                                                frameworks/base/config/hiddenapi-vendor-list.txt
 	sort $^ > $@
 
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\
+                            $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)))
+
 # Generate dark greylist as private API minus (blacklist plus light greylist).
 
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
-                                              $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
-                                              $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
-	if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(LIGHT_GREYLIST))`" ]; then \
-		echo "There should be no overlap between $(BLACKLIST) and $(LIGHT_GREYLIST)" 1>&2; \
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
+                                          $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
+                                          $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+	if [ ! -z "`comm -12 <(sort $(LIGHT_GREYLIST)) <(sort $(DARK_GREYLIST))`" ]; then \
+		echo "There should be no overlap between $(LIGHT_GREYLIST) and $(DARK_GREYLIST)" 1>&2; \
+		comm -12 <(sort $(LIGHT_GREYLIST)) <(sort $(DARK_GREYLIST)) 1>&2; \
 		exit 1; \
-	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \
-		echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
-		exit 2; \
 	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST))`" ]; then \
 		echo "$(LIGHT_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+		comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST)) 1>&2; \
+		exit 2; \
+	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \
+		echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+		comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST)) 1>&2; \
 		exit 3; \
 	fi
-	comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(LIGHT_GREYLIST)) > $@
+	comm -23 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST) $(DARK_GREYLIST)) > $@
 
 # Include subdirectory makefiles
 # ============================================================
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
index 73e1724..ccbccca 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
+++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
@@ -119,8 +119,12 @@
         // Report median of randomly generated PrecomputedText.
         for (int i = 0; i < TRIAL_COUNT; ++i) {
             CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
-            memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
-                .getMemoryUsage();
+            PrecomputedText.ParagraphInfo[] paragraphInfo =
+                    PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false);
+            memories[i] = 0;
+            for (PrecomputedText.ParagraphInfo info : paragraphInfo) {
+                memories[i] += info.measured.getMemoryUsage();
+            }
         }
         reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly");
     }
@@ -136,8 +140,12 @@
         // Report median of randomly generated PrecomputedText.
         for (int i = 0; i < TRIAL_COUNT; ++i) {
             CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
-            memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
-                .getMemoryUsage();
+            PrecomputedText.ParagraphInfo[] paragraphInfo =
+                    PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false);
+            memories[i] = 0;
+            for (PrecomputedText.ParagraphInfo info : paragraphInfo) {
+                memories[i] += info.measured.getMemoryUsage();
+            }
         }
         reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly");
     }
diff --git a/api/current.txt b/api/current.txt
index 414491c..2ca424c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5401,6 +5401,7 @@
     method public android.app.Notification.Builder extend(android.app.Notification.Extender);
     method public android.os.Bundle getExtras();
     method public deprecated android.app.Notification getNotification();
+    method public android.app.Notification.Style getStyle();
     method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAutoCancel(boolean);
@@ -7192,6 +7193,7 @@
     field public static final deprecated java.lang.String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE";
     field public static final java.lang.String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
     field public static final java.lang.String HINT_ACTIONS = "actions";
+    field public static final java.lang.String HINT_ERROR = "error";
     field public static final java.lang.String HINT_HORIZONTAL = "horizontal";
     field public static final java.lang.String HINT_KEY_WORDS = "key_words";
     field public static final java.lang.String HINT_LARGE = "large";
@@ -7217,29 +7219,22 @@
   }
 
   public static class Slice.Builder {
-    ctor public Slice.Builder(android.net.Uri);
+    ctor public deprecated Slice.Builder(android.net.Uri);
+    ctor public Slice.Builder(android.net.Uri, android.app.slice.SliceSpec);
     ctor public Slice.Builder(android.app.slice.Slice.Builder);
-    method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice);
     method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice, java.lang.String);
-    method public android.app.slice.Slice.Builder addBundle(android.os.Bundle, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addBundle(android.os.Bundle, java.lang.String, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addHints(java.lang.String...);
     method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addInt(int, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addInt(int, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addLong(long, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.lang.String...);
-    method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice);
     method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice, java.lang.String);
-    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.lang.String...);
-    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>);
+    method public deprecated android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice build();
     method public android.app.slice.Slice.Builder setCallerNeeded(boolean);
-    method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
+    method public deprecated android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
   }
 
   public final class SliceItem implements android.os.Parcelable {
@@ -7262,10 +7257,11 @@
     field public static final java.lang.String FORMAT_BUNDLE = "bundle";
     field public static final java.lang.String FORMAT_IMAGE = "image";
     field public static final java.lang.String FORMAT_INT = "int";
+    field public static final java.lang.String FORMAT_LONG = "long";
     field public static final java.lang.String FORMAT_REMOTE_INPUT = "input";
     field public static final java.lang.String FORMAT_SLICE = "slice";
     field public static final java.lang.String FORMAT_TEXT = "text";
-    field public static final java.lang.String FORMAT_TIMESTAMP = "timestamp";
+    field public static final deprecated java.lang.String FORMAT_TIMESTAMP = "long";
   }
 
   public class SliceManager {
@@ -7281,6 +7277,13 @@
     field public static final java.lang.String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
   }
 
+  public class SliceMetrics {
+    ctor public SliceMetrics(android.content.Context, android.net.Uri);
+    method public void logHidden();
+    method public void logTouch(android.net.Uri);
+    method public void logVisible();
+  }
+
   public abstract class SliceProvider extends android.content.ContentProvider {
     ctor public SliceProvider();
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
@@ -15804,6 +15807,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> INFO_SUPPORTED_HARDWARE_LEVEL;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.String> INFO_VERSION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_DISTORTION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_FACING;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_APERTURES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_FILTER_DENSITIES;
@@ -15816,7 +15820,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_POSE_REFERENCE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION;
-    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION;
+    field public static final deprecated android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REPROCESS_MAX_CAPTURE_STALL;
@@ -16274,6 +16278,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> JPEG_THUMBNAIL_QUALITY;
     field public static final android.hardware.camera2.CaptureResult.Key<android.util.Size> JPEG_THUMBNAIL_SIZE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_APERTURE;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_DISTORTION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FILTER_DENSITY;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FOCAL_LENGTH;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FOCUS_DISTANCE;
@@ -16282,7 +16287,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_ROTATION;
     field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_TRANSLATION;
-    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_RADIAL_DISTORTION;
+    field public static final deprecated android.hardware.camera2.CaptureResult.Key<float[]> LENS_RADIAL_DISTORTION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
@@ -23783,10 +23788,8 @@
     field public static final java.lang.String KEY_DURATION = "durationUs";
     field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
     field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
-    field public static final java.lang.String KEY_GRID_COLS = "grid-cols";
-    field public static final java.lang.String KEY_GRID_HEIGHT = "grid-height";
+    field public static final java.lang.String KEY_GRID_COLUMNS = "grid-cols";
     field public static final java.lang.String KEY_GRID_ROWS = "grid-rows";
-    field public static final java.lang.String KEY_GRID_WIDTH = "grid-width";
     field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
     field public static final java.lang.String KEY_HEIGHT = "height";
     field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
@@ -23815,6 +23818,8 @@
     field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height";
     field public static final java.lang.String KEY_STRIDE = "stride";
     field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema";
+    field public static final java.lang.String KEY_TILE_HEIGHT = "tile-height";
+    field public static final java.lang.String KEY_TILE_WIDTH = "tile-width";
     field public static final java.lang.String KEY_TRACK_ID = "track-id";
     field public static final java.lang.String KEY_WIDTH = "width";
     field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
@@ -24064,12 +24069,16 @@
     method public java.lang.String extractMetadata(int);
     method public byte[] getEmbeddedPicture();
     method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
+    method public android.graphics.Bitmap getFrameAtIndex(int);
     method public android.graphics.Bitmap getFrameAtTime(long, int);
     method public android.graphics.Bitmap getFrameAtTime(long);
     method public android.graphics.Bitmap getFrameAtTime();
     method public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int, android.media.MediaMetadataRetriever.BitmapParams);
+    method public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int);
     method public android.graphics.Bitmap getImageAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
+    method public android.graphics.Bitmap getImageAtIndex(int);
     method public android.graphics.Bitmap getPrimaryImage(android.media.MediaMetadataRetriever.BitmapParams);
+    method public android.graphics.Bitmap getPrimaryImage();
     method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
     method public void release();
     method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
@@ -29475,7 +29484,7 @@
     field public static final java.lang.String EXTRA_ID = "android.nfc.extra.ID";
     field public static final java.lang.String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
     field public static final java.lang.String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
-    field public static final java.lang.String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
+    field public static final java.lang.String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
     field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
     field public static final int FLAG_READER_NFC_A = 1; // 0x1
     field public static final int FLAG_READER_NFC_B = 2; // 0x2
@@ -40048,6 +40057,7 @@
     method public int getSuppressedVisualEffects();
     method public int getUserSentiment();
     method public boolean isAmbient();
+    method public boolean isSuspended();
     method public boolean matchesInterruptionFilter();
     field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
     field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0
@@ -43344,21 +43354,21 @@
   public class ApnSetting implements android.os.Parcelable {
     method public int describeContents();
     method public java.lang.String getApnName();
+    method public int getApnTypeBitmask();
     method public int getAuthType();
     method public java.lang.String getEntryName();
     method public int getId();
-    method public int getMmsPort();
-    method public java.net.InetAddress getMmsProxy();
-    method public java.net.URL getMmsc();
-    method public java.lang.String getMvnoType();
+    method public java.net.InetAddress getMmsProxyAddress();
+    method public int getMmsProxyPort();
+    method public android.net.Uri getMmsc();
+    method public int getMvnoType();
     method public int getNetworkTypeBitmask();
     method public java.lang.String getOperatorNumeric();
     method public java.lang.String getPassword();
-    method public int getPort();
-    method public java.lang.String getProtocol();
-    method public java.net.InetAddress getProxy();
-    method public java.lang.String getRoamingProtocol();
-    method public java.util.List<java.lang.String> getTypes();
+    method public int getProtocol();
+    method public java.net.InetAddress getProxyAddress();
+    method public int getProxyPort();
+    method public int getRoamingProtocol();
     method public java.lang.String getUser();
     method public boolean isEnabled();
     method public void writeToParcel(android.os.Parcel, int);
@@ -43367,46 +43377,45 @@
     field public static final int AUTH_TYPE_PAP = 1; // 0x1
     field public static final int AUTH_TYPE_PAP_OR_CHAP = 3; // 0x3
     field public static final android.os.Parcelable.Creator<android.telephony.data.ApnSetting> CREATOR;
-    field public static final java.lang.String MVNO_TYPE_GID = "gid";
-    field public static final java.lang.String MVNO_TYPE_ICCID = "iccid";
-    field public static final java.lang.String MVNO_TYPE_IMSI = "imsi";
-    field public static final java.lang.String MVNO_TYPE_SPN = "spn";
-    field public static final java.lang.String PROTOCOL_IP = "IP";
-    field public static final java.lang.String PROTOCOL_IPV4V6 = "IPV4V6";
-    field public static final java.lang.String PROTOCOL_IPV6 = "IPV6";
-    field public static final java.lang.String PROTOCOL_PPP = "PPP";
-    field public static final java.lang.String TYPE_ALL = "*";
-    field public static final java.lang.String TYPE_CBS = "cbs";
-    field public static final java.lang.String TYPE_DEFAULT = "default";
-    field public static final java.lang.String TYPE_DUN = "dun";
-    field public static final java.lang.String TYPE_EMERGENCY = "emergency";
-    field public static final java.lang.String TYPE_FOTA = "fota";
-    field public static final java.lang.String TYPE_HIPRI = "hipri";
-    field public static final java.lang.String TYPE_IA = "ia";
-    field public static final java.lang.String TYPE_IMS = "ims";
-    field public static final java.lang.String TYPE_MMS = "mms";
-    field public static final java.lang.String TYPE_SUPL = "supl";
+    field public static final int MVNO_TYPE_GID = 2; // 0x2
+    field public static final int MVNO_TYPE_ICCID = 3; // 0x3
+    field public static final int MVNO_TYPE_IMSI = 1; // 0x1
+    field public static final int MVNO_TYPE_SPN = 0; // 0x0
+    field public static final int PROTOCOL_IP = 0; // 0x0
+    field public static final int PROTOCOL_IPV4V6 = 2; // 0x2
+    field public static final int PROTOCOL_IPV6 = 1; // 0x1
+    field public static final int PROTOCOL_PPP = 3; // 0x3
+    field public static final int TYPE_CBS = 128; // 0x80
+    field public static final int TYPE_DEFAULT = 17; // 0x11
+    field public static final int TYPE_DUN = 8; // 0x8
+    field public static final int TYPE_EMERGENCY = 512; // 0x200
+    field public static final int TYPE_FOTA = 32; // 0x20
+    field public static final int TYPE_HIPRI = 16; // 0x10
+    field public static final int TYPE_IA = 256; // 0x100
+    field public static final int TYPE_IMS = 64; // 0x40
+    field public static final int TYPE_MMS = 2; // 0x2
+    field public static final int TYPE_SUPL = 4; // 0x4
   }
 
   public static class ApnSetting.Builder {
     ctor public ApnSetting.Builder();
     method public android.telephony.data.ApnSetting build();
     method public android.telephony.data.ApnSetting.Builder setApnName(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setApnTypeBitmask(int);
     method public android.telephony.data.ApnSetting.Builder setAuthType(int);
     method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean);
     method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setMmsPort(int);
-    method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress);
-    method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL);
-    method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setMmsProxyAddress(java.net.InetAddress);
+    method public android.telephony.data.ApnSetting.Builder setMmsProxyPort(int);
+    method public android.telephony.data.ApnSetting.Builder setMmsc(android.net.Uri);
+    method public android.telephony.data.ApnSetting.Builder setMvnoType(int);
     method public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int);
     method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setPort(int);
-    method public android.telephony.data.ApnSetting.Builder setProtocol(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setProxy(java.net.InetAddress);
-    method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setTypes(java.util.List<java.lang.String>);
+    method public android.telephony.data.ApnSetting.Builder setProtocol(int);
+    method public android.telephony.data.ApnSetting.Builder setProxyAddress(java.net.InetAddress);
+    method public android.telephony.data.ApnSetting.Builder setProxyPort(int);
+    method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(int);
     method public android.telephony.data.ApnSetting.Builder setUser(java.lang.String);
   }
 
@@ -51990,6 +51999,7 @@
     method public android.webkit.WebChromeClient getWebChromeClient();
     method public static java.lang.ClassLoader getWebViewClassLoader();
     method public android.webkit.WebViewClient getWebViewClient();
+    method public android.os.Looper getWebViewLooper();
     method public void goBack();
     method public void goBackOrForward(int);
     method public void goForward();
diff --git a/api/system-current.txt b/api/system-current.txt
index 0e33c17..1e2fe3d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -32,6 +32,7 @@
     field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
     field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
     field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
+    field public static final java.lang.String BIND_SOUND_TRIGGER_DETECTION_SERVICE = "android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE";
     field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
     field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
     field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
@@ -99,6 +100,7 @@
     field public static final java.lang.String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE";
     field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
     field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
+    field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
     field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
@@ -723,6 +725,9 @@
     method public java.lang.String getNotificationChannelId();
     field public static final int NOTIFICATION_INTERRUPTION = 12; // 0xc
     field public static final int NOTIFICATION_SEEN = 10; // 0xa
+    field public static final int SLICE_PINNED = 14; // 0xe
+    field public static final int SLICE_PINNED_PRIV = 13; // 0xd
+    field public static final int SYSTEM_INTERACTION = 6; // 0x6
   }
 
   public final class UsageStatsManager {
@@ -1232,7 +1237,9 @@
 
   public final class DisplayManager {
     method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
+    method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
     method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
+    method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
     method public android.graphics.Point getStableDisplaySize();
     method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
   }
@@ -2219,6 +2226,21 @@
 
 }
 
+package android.hardware.soundtrigger {
+
+  public class SoundTrigger {
+    field public static final int STATUS_OK = 0; // 0x0
+  }
+
+  public static class SoundTrigger.RecognitionEvent {
+    method public android.media.AudioFormat getCaptureFormat();
+    method public int getCaptureSession();
+    method public byte[] getData();
+    method public boolean isCaptureAvailable();
+  }
+
+}
+
 package android.hardware.usb {
 
   public class UsbDeviceConnection {
@@ -2734,6 +2756,16 @@
 
 package android.media.soundtrigger {
 
+  public abstract class SoundTriggerDetectionService extends android.app.Service {
+    ctor public SoundTriggerDetectionService();
+    method public void onConnected(java.util.UUID, android.os.Bundle);
+    method public void onDisconnected(java.util.UUID, android.os.Bundle);
+    method public void onError(java.util.UUID, android.os.Bundle, int, int);
+    method public void onGenericRecognitionEvent(java.util.UUID, android.os.Bundle, int, android.hardware.soundtrigger.SoundTrigger.RecognitionEvent);
+    method public abstract void onStopOperation(java.util.UUID, android.os.Bundle, int);
+    method public final void operationFinished(java.util.UUID, int);
+  }
+
   public final class SoundTriggerDetector {
     method public boolean startRecognition(int);
     method public boolean stopRecognition();
@@ -2758,6 +2790,7 @@
   public final class SoundTriggerManager {
     method public android.media.soundtrigger.SoundTriggerDetector createSoundTriggerDetector(java.util.UUID, android.media.soundtrigger.SoundTriggerDetector.Callback, android.os.Handler);
     method public void deleteModel(java.util.UUID);
+    method public int getDetectionServiceOperationsTimeout();
     method public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID);
     method public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model);
   }
@@ -5213,10 +5246,10 @@
     method public boolean handlePinMmi(java.lang.String);
     method public boolean handlePinMmiForSubscriber(int, java.lang.String);
     method public boolean isDataConnectivityPossible();
-    method public deprecated boolean isIdle();
-    method public deprecated boolean isOffhook();
-    method public deprecated boolean isRadioOn();
-    method public deprecated boolean isRinging();
+    method public boolean isIdle();
+    method public boolean isOffhook();
+    method public boolean isRadioOn();
+    method public boolean isRinging();
     method public boolean isVideoCallingEnabled();
     method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
@@ -5442,6 +5475,7 @@
 
   public class EuiccManager {
     method public void continueOperation(android.content.Intent, android.os.Bundle);
+    method public void eraseSubscriptions(android.app.PendingIntent);
     method public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
     method public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
     method public int getOtaStatus();
diff --git a/api/test-current.txt b/api/test-current.txt
index e8d27a1..70e3cf3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -370,7 +370,9 @@
 
   public final class DisplayManager {
     method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
+    method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
     method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
+    method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
     method public android.graphics.Point getStableDisplaySize();
     method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
   }
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 5d6a1d1a..ca0611b 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -177,6 +177,7 @@
     tests/LogEvent_test.cpp \
     tests/MetricsManager_test.cpp \
     tests/StatsLogProcessor_test.cpp \
+    tests/StatsService_test.cpp \
     tests/UidMap_test.cpp \
     tests/FieldValue_test.cpp \
     tests/condition/CombinationConditionTracker_test.cpp \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 652ec9d..82274a6 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -112,8 +112,8 @@
 }
 
 void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
-    if (android::util::kAtomsWithAttributionChain.find(event->GetTagId()) !=
-        android::util::kAtomsWithAttributionChain.end()) {
+    if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(event->GetTagId()) !=
+        android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
         for (auto& value : *(event->getMutableValues())) {
             if (value.mField.getPosAtDepth(0) > kAttributionField) {
                 break;
@@ -123,12 +123,20 @@
                 updateUid(&value.mValue, hostUid);
             }
         }
-    } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) !=
-                       android::util::kAtomsWithUidField.end() &&
-               event->getValues().size() > 0 && (event->getValues())[0].mValue.getType() == INT) {
-        Value& value = (*event->getMutableValues())[0].mValue;
-        const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
-        updateUid(&value, hostUid);
+    } else {
+        auto it = android::util::AtomsInfo::kAtomsWithUidField.find(event->GetTagId());
+        if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+            int uidField = it->second;  // uidField is the field number in proto,
+                                        // starting from 1
+            if (uidField > 0 && (int)event->getValues().size() >= uidField &&
+                (event->getValues())[uidField - 1].mValue.getType() == INT) {
+                Value& value = (*event->getMutableValues())[uidField - 1].mValue;
+                const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
+                updateUid(&value, hostUid);
+            } else {
+                ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
+            }
+        }
     }
 }
 
@@ -231,14 +239,13 @@
     }
 }
 
+/*
+ * onDumpReport dumps serialized ConfigMetricsReportList into outData.
+ */
 void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t dumpTimeStampNs,
                                      vector<uint8_t>* outData) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
-    onDumpReportLocked(key, dumpTimeStampNs, outData);
-}
 
-void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeStampNs,
-                                           vector<uint8_t>* outData) {
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end()) {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
@@ -261,35 +268,14 @@
     // Start of ConfigMetricsReport (reports).
     uint64_t reportsToken =
             proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
-
-    int64_t lastReportTimeNs = it->second->getLastReportTimeNs();
-    int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs();
-
-    // First, fill in ConfigMetricsReport using current data on memory, which
-    // starts from filling in StatsLogReport's.
-    it->second->onDumpReport(dumpTimeStampNs, &proto);
-
-    // Fill in UidMap.
-    vector<uint8_t> uidMap;
-    mUidMap->getOutput(key, &uidMap);
-    proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMap.data());
-
-    // Fill in the timestamps.
-    proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS,
-                (long long)lastReportTimeNs);
-    proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS,
-                (long long)dumpTimeStampNs);
-    proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS,
-                (long long)lastReportWallClockNs);
-    proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS,
-                (long long)getWallClockNs());
-
-    // End of ConfigMetricsReport (reports).
+    onConfigMetricsReportLocked(key, dumpTimeStampNs, &proto);
     proto.end(reportsToken);
+    // End of ConfigMetricsReport (reports).
+
 
     // Then, check stats-data directory to see there's any file containing
     // ConfigMetricsReport from previous shutdowns to concatenate to reports.
-    StorageManager::appendConfigMetricsReport(proto);
+    StorageManager::appendConfigMetricsReport(key, &proto);
 
     if (outData != nullptr) {
         outData->clear();
@@ -307,6 +293,40 @@
     StatsdStats::getInstance().noteMetricsReportSent(key);
 }
 
+/*
+ * onConfigMetricsReportLocked dumps serialized ConfigMetricsReport into outData.
+ */
+void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
+                                                    const uint64_t dumpTimeStampNs,
+                                                    ProtoOutputStream* proto) {
+    // We already checked whether key exists in mMetricsManagers in
+    // WriteDataToDisk.
+    auto it = mMetricsManagers.find(key);
+    int64_t lastReportTimeNs = it->second->getLastReportTimeNs();
+    int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs();
+
+    // First, fill in ConfigMetricsReport using current data on memory, which
+    // starts from filling in StatsLogReport's.
+    it->second->onDumpReport(dumpTimeStampNs, proto);
+
+    // Fill in UidMap.
+    uint64_t uidMapToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP);
+    mUidMap->appendUidMap(key, proto);
+    proto->end(uidMapToken);
+
+    // Fill in the timestamps.
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS,
+                (long long)lastReportTimeNs);
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS,
+                (long long)dumpTimeStampNs);
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS,
+                (long long)lastReportWallClockNs);
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS,
+                (long long)getWallClockNs());
+
+
+}
+
 void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     auto it = mMetricsManagers.find(key);
@@ -360,11 +380,17 @@
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     for (auto& pair : mMetricsManagers) {
         const ConfigKey& key = pair.first;
-        vector<uint8_t> data;
-        onDumpReportLocked(key, getElapsedRealtimeNs(), &data);
+        ProtoOutputStream proto;
+        onConfigMetricsReportLocked(key, getElapsedRealtimeNs(), &proto);
         string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
              (long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
-        StorageManager::writeFile(file_name.c_str(), &data[0], data.size());
+        android::base::unique_fd fd(open(file_name.c_str(),
+                                    O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR));
+        if (fd == -1) {
+            VLOG("Attempt to write %s but failed", file_name.c_str());
+            return;
+        }
+        proto.flush(fd.get());
     }
 }
 
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 8b42146..1be4dc5 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -86,8 +86,8 @@
 
     sp<AlarmMonitor> mPeriodicAlarmMonitor;
 
-    void onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeNs,
-                            vector<uint8_t>* outData);
+    void onConfigMetricsReportLocked(const ConfigKey& key, const uint64_t dumpTimeStampNs,
+                                     util::ProtoOutputStream* proto);
 
     /* Check if we should send a broadcast if approaching memory limits and if we're over, we
      * actually delete the data. */
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index c5dfc44..b86646a 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -867,14 +867,11 @@
                                       bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
     if (checkCallingPermission(String16(kPermissionDump))) {
-        ConfigKey configKey(ipc->getCallingUid(), key);
-        StatsdConfig cfg;
-        if (config.empty() || !cfg.ParseFromArray(&config[0], config.size())) {
+        if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
+            *success = true;
+        } else {
             *success = false;
-            return Status::ok();
         }
-        mConfigManager->UpdateConfig(configKey, cfg);
-        *success = true;
         return Status::ok();
     } else {
         *success = false;
@@ -882,6 +879,18 @@
     }
 }
 
+bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config) {
+    ConfigKey configKey(uid, key);
+    StatsdConfig cfg;
+    if (config.size() > 0) {  // If the config is empty, skip parsing.
+        if (!cfg.ParseFromArray(&config[0], config.size())) {
+            return false;
+        }
+    }
+    mConfigManager->UpdateConfig(configKey, cfg);
+    return true;
+}
+
 Status StatsService::removeDataFetchOperation(int64_t key, bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
     if (checkCallingPermission(String16(kPermissionDump))) {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 02b6124..0ec31ef 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -17,6 +17,7 @@
 #ifndef STATS_SERVICE_H
 #define STATS_SERVICE_H
 
+#include <gtest/gtest_prod.h>
 #include "StatsLogProcessor.h"
 #include "anomaly/AlarmMonitor.h"
 #include "config/ConfigManager.h"
@@ -216,6 +217,11 @@
     status_t cmd_clear_puller_cache(FILE* out);
 
     /**
+     * Adds a configuration after checking permissions and obtaining UID from binder call.
+     */
+    bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
+
+    /**
      * Update a configuration.
      */
     void set_config(int uid, const string& name, const StatsdConfig& config);
@@ -254,6 +260,10 @@
      * Whether this is an eng build.
      */
     bool mEngBuild;
+
+    FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
+    FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
+    FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 19d00b7..a2a03b1 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -67,4 +67,7 @@
 extend google.protobuf.FieldOptions {
     // Flags to decorate an atom that presents a state change.
     optional StateAtomFieldOption stateFieldOption = 50000;
+
+    // Flags to decorate the uid fields in an atom.
+    optional bool is_uid = 50001 [default = false];
 }
\ No newline at end of file
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f74188f..40eff4c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -209,7 +209,7 @@
  *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message UidProcessStateChanged {
-    optional int32 uid = 1 [(stateFieldOption).option = PRIMARY];
+    optional int32 uid = 1 [(stateFieldOption).option = PRIMARY, (is_uid) = true];
 
     // The state, from frameworks/base/core/proto/android/app/enums.proto.
     optional android.app.ProcessStateEnum state = 2 [(stateFieldOption).option = EXCLUSIVE];
@@ -223,7 +223,7 @@
  */
 message ProcessLifeCycleStateChanged {
     // TODO: should be a string tagged w/ uid annotation
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The process name (usually same as the app name).
     optional string name = 2;
@@ -593,7 +593,7 @@
  */
 message MobileRadioPowerStateChanged {
     // TODO: Add attribution instead of uid?
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // Power state, from frameworks/base/core/proto/android/telephony/enums.proto.
     optional android.telephony.DataConnectionPowerStateEnum state = 2;
@@ -608,7 +608,7 @@
  */
 message WifiRadioPowerStateChanged {
     // TODO: Add attribution instead of uid?
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // Power state, from frameworks/base/core/proto/android/telephony/enums.proto.
     optional android.telephony.DataConnectionPowerStateEnum state = 2;
@@ -1130,7 +1130,8 @@
  */
 message DaveyOccurred {
     // The UID that logged this atom.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
+    ;
 
     // Amount of time it took to render the frame. Should be >=700ms.
     optional int64 jank_duration_millis = 2;
@@ -1150,7 +1151,7 @@
 /**
  * Logs that a setting was updated.
  * Logged from:
- *   frameworks/base/core/java/android/provider/Settings.java
+ *   frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
  * The tag and is_default allow resetting of settings to default values based on the specified
  * tag. See Settings#putString(ContentResolver, String, String, String, boolean) for more details.
  */
@@ -1176,8 +1177,14 @@
     // True if this setting with tag should be resettable.
     optional bool is_default = 6;
 
-    // The user ID associated. Defined in android/os/UserHandle.java
+    // The associated user (for multi-user feature). Defined in android/os/UserHandle.java
     optional int32 user = 7;
+
+    enum ChangeReason {
+        UPDATED = 1; // Updated can be an insertion or an update.
+        DELETED = 2;
+    }
+    optional ChangeReason reason = 8;
 }
 
 /**
@@ -1187,7 +1194,7 @@
   *   frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
  */
 message ActivityForegroundStateChanged {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     optional string pkg_name = 2;
     optional string class_name = 3;
 
@@ -1205,7 +1212,7 @@
  */
 message DropboxErrorChanged {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // Tag used when recording this error to dropbox. Contains data_ or system_ prefix.
     optional string tag = 2;
@@ -1236,7 +1243,7 @@
  */
 message AppBreadcrumbReported {
     // The uid of the application that sent this custom atom.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // An arbitrary label chosen by the developer. For Android P, the label should be in [0, 16).
     optional int32 label = 2;
@@ -1260,7 +1267,7 @@
  */
 message AnomalyDetected {
     // Uid that owns the config whose anomaly detection alert fired.
-    optional int32 config_uid = 1;
+    optional int32 config_uid = 1 [(is_uid) = true];
 
     // Id of the config whose anomaly detection alert fired.
     optional int64 config_id = 2;
@@ -1271,7 +1278,7 @@
 
 message AppStartChanged {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The app package name.
     optional string pkg_name = 2;
@@ -1318,7 +1325,7 @@
 
 message AppStartCancelChanged {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The app package name.
     optional string pkg_name = 2;
@@ -1338,7 +1345,7 @@
 
 message AppStartFullyDrawnChanged {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The app package name.
     optional string pkg_name = 2;
@@ -1369,7 +1376,7 @@
  */
 message PictureInPictureStateChanged {
     // -1 if it is not available
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     optional string short_name = 2;
 
@@ -1388,7 +1395,7 @@
  *     services/core/java/com/android/server/wm/Session.java
  */
 message OverlayStateChanged {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     optional string package_name = 2;
 
@@ -1409,7 +1416,7 @@
  *     //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
  */
 message ForegroundServiceStateChanged {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     // package_name + "/" + class_name
     optional string short_name = 2;
 
@@ -1429,6 +1436,7 @@
  *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
  */
 message IsolatedUidChanged {
+    // NOTE: DO NOT annotate uid field in this atom. This atom is specially handled in statsd.
     // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid.
     optional int32 parent_uid = 1;
 
@@ -1450,7 +1458,7 @@
 message PacketWakeupOccurred {
     // The uid owning the socket into which the packet was delivered, or -1 if the packet was
     // delivered nowhere.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     // The interface name on which the packet was received.
     optional string iface = 2;
     // The ethertype value of the packet.
@@ -1478,7 +1486,7 @@
  */
 message AppStartMemoryStateCaptured {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The process name.
     optional string process_name = 2;
@@ -1524,7 +1532,7 @@
  */
 message LmkKillOccurred {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The process name.
     optional string process_name = 2;
@@ -1556,7 +1564,7 @@
  */
 message AppDied {
     // timestamp(elapsedRealtime) of record creation
-    optional uint64 timestamp_millis = 1;
+    optional uint64 timestamp_millis = 1 [(stateFieldOption).option = EXCLUSIVE];
 }
 
 //////////////////////////////////////////////////////////////////////
@@ -1570,7 +1578,7 @@
  *   StatsCompanionService (using BatteryStats to get which interfaces are wifi)
  */
 message WifiBytesTransfer {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     optional int64 rx_bytes = 2;
 
@@ -1588,9 +1596,10 @@
  *   StatsCompanionService (using BatteryStats to get which interfaces are wifi)
  */
 message WifiBytesTransferByFgBg {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
-    // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+    // 1 denotes foreground and 0 denotes background. This is called Set in
+    // NetworkStats.
     optional int32 is_foreground = 2;
 
     optional int64 rx_bytes = 3;
@@ -1609,7 +1618,7 @@
  *   StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
  */
 message MobileBytesTransfer {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     optional int64 rx_bytes = 2;
 
@@ -1627,9 +1636,10 @@
  *   StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
  */
 message MobileBytesTransferByFgBg {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
-    // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+    // 1 denotes foreground and 0 denotes background. This is called Set in
+    // NetworkStats.
     optional int32 is_foreground = 2;
 
     optional int64 rx_bytes = 3;
@@ -1648,7 +1658,7 @@
  *   StatsCompanionService
  */
 message BluetoothBytesTransfer {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     optional int64 rx_bytes = 2;
 
@@ -1708,7 +1718,7 @@
  * Note that isolated process uid time should be attributed to host uids.
  */
 message CpuTimePerUid {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     optional uint64 user_time_millis = 2;
     optional uint64 sys_time_millis = 3;
 }
@@ -1719,7 +1729,7 @@
  * For each uid, we order the time by descending frequencies.
  */
 message CpuTimePerUidFreq {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     optional uint32 freq_index = 2;
     optional uint64 time_millis = 3;
 }
@@ -1801,7 +1811,7 @@
  */
 message ProcessMemoryState {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The process name.
     optional string process_name = 2;
@@ -1853,7 +1863,7 @@
  * The file contains a monotonically increasing count of time for a single boot.
  */
 message CpuActiveTime {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     optional uint64 time_millis = 2;
 }
 
@@ -1867,7 +1877,7 @@
  * The file contains a monotonically increasing count of time for a single boot.
  */
 message CpuClusterTime {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     optional int32 cluster_index = 2;
     optional uint64 time_millis = 3;
 }
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 72a00cb..fb0be73 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -166,6 +166,11 @@
     // Round it to the nearest minutes. This is the limit of alarm manager.
     // In practice, we should limit it higher.
     long roundedIntervalMs = intervalMs/1000/60 * 1000 * 60;
+    // Scheduled pulling should be at least 1 min apart.
+    // This can be lower in cts tests, in which case we round it to 1 min.
+    if (roundedIntervalMs < 60 * 1000) {
+        roundedIntervalMs = 60 * 1000;
+    }
     // There is only one alarm for all pulled events. So only set it to the smallest denom.
     if (roundedIntervalMs < mCurrentPullingInterval) {
         VLOG("Updating pulling interval %ld", intervalMs);
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index 0b0c5c4..ea23623 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -112,7 +112,8 @@
         VLOG("Unknown pull atom id %d", tagId);
         return;
     }
-    if (android::util::kAtomsWithUidField.find(tagId) == android::util::kAtomsWithUidField.end()) {
+    if (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
+        android::util::AtomsInfo::kAtomsWithUidField.end()) {
         VLOG("No uid to merge for atom %d", tagId);
         return;
     }
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 778eb8e..bd8b293 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -134,8 +134,8 @@
     uint64_t wrapperToken =
             mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
     const bool truncateTimestamp =
-        android::util::kNotTruncatingTimestampAtomWhiteList.find(event.GetTagId()) ==
-        android::util::kNotTruncatingTimestampAtomWhiteList.end();
+            android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.find(event.GetTagId()) ==
+            android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.end();
     if (truncateTimestamp) {
         mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS,
             (long long)truncateTimestampNsToFiveMinutes(event.GetElapsedTimestampNs()));
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 55a281e..49034ac 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -195,8 +195,9 @@
                     protoOutput->end(atomsToken);
                 }
                 const bool truncateTimestamp =
-                    android::util::kNotTruncatingTimestampAtomWhiteList.find(mTagId) ==
-                    android::util::kNotTruncatingTimestampAtomWhiteList.end();
+                        android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.find(
+                                mTagId) ==
+                        android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.end();
                 const int64_t wall_clock_ns = truncateTimestamp ?
                     truncateTimestampNsToFiveMinutes(getWallClockNs()) : getWallClockNs();
                 for (const auto& atom : bucket.mGaugeAtoms) {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 901f500..c6112fd 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -170,9 +170,10 @@
     // 1. must not have "stop". must have "dimension"
     if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
         // TODO: need to check the start atom matcher too.
-        auto it = android::util::kStateAtomsFieldOptions.find(simplePredicate.dimensions().field());
+        auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(
+                simplePredicate.dimensions().field());
         // 2. must be based on a state atom.
-        if (it != android::util::kStateAtomsFieldOptions.end()) {
+        if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
             // 3. dimension must be primary fields + state field IN ORDER
             size_t expectedDimensionCount = it->second.primaryFields.size() + 1;
             vector<Matcher> dimensions;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index e97d8b2..3ba4b7a 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -331,25 +331,25 @@
     return mBytesUsed;
 }
 
-void UidMap::getOutput(const ConfigKey& key, vector<uint8_t>* outData) {
-    getOutput(getElapsedRealtimeNs(), key, outData);
+void UidMap::appendUidMap(const ConfigKey& key, ProtoOutputStream* proto) {
+    appendUidMap(getElapsedRealtimeNs(), key, proto);
 }
 
-void UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key, vector<uint8_t>* outData) {
+void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
+                          ProtoOutputStream* proto) {
     lock_guard<mutex> lock(mMutex);  // Lock for updates
 
-    ProtoOutputStream proto;
     for (const ChangeRecord& record : mChanges) {
         if (record.timestampNs > mLastUpdatePerConfigKey[key]) {
             uint64_t changesToken =
-                    proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CHANGES);
-            proto.write(FIELD_TYPE_BOOL | FIELD_ID_CHANGE_DELETION, (bool)record.deletion);
-            proto.write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_TIMESTAMP,
-                        (long long)record.timestampNs);
-            proto.write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
-            proto.write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid);
-            proto.write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_VERSION, (int)record.version);
-            proto.end(changesToken);
+                    proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CHANGES);
+            proto->write(FIELD_TYPE_BOOL | FIELD_ID_CHANGE_DELETION, (bool)record.deletion);
+            proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_TIMESTAMP,
+                         (long long)record.timestampNs);
+            proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
+            proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid);
+            proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_VERSION, (int)record.version);
+            proto->end(changesToken);
         }
     }
 
@@ -360,13 +360,13 @@
         if ((count == mSnapshots.size() - 1 && !atLeastOneSnapshot) ||
             record.timestampNs > mLastUpdatePerConfigKey[key]) {
             uint64_t snapshotsToken =
-                    proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
+                    proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
             atLeastOneSnapshot = true;
             count++;
-            proto.write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP,
-                        (long long)record.timestampNs);
-            proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_SNAPSHOT_PACKAGE_INFO, record.bytes.data());
-            proto.end(snapshotsToken);
+            proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP,
+                         (long long)record.timestampNs);
+            proto->write(FIELD_TYPE_MESSAGE | FIELD_ID_SNAPSHOT_PACKAGE_INFO, record.bytes.data());
+            proto->end(snapshotsToken);
         }
     }
 
@@ -398,47 +398,36 @@
             // Produce another snapshot. This results in extra data being uploaded but
             // helps ensure we can re-construct the UID->app name, versionCode mapping
             // in server.
-            ProtoOutputStream proto;
-            uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
-                                          FIELD_ID_SNAPSHOT_PACKAGE_INFO);
+            ProtoOutputStream snapshotProto;
+            uint64_t token = snapshotProto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                                 FIELD_ID_SNAPSHOT_PACKAGE_INFO);
             for (const auto& it : mMap) {
-                proto.write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME,
-                            it.second.packageName);
-                proto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
-                            (int)it.second.versionCode);
-                proto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, (int)it.first);
+                snapshotProto.write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME,
+                                    it.second.packageName);
+                snapshotProto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
+                                    (int)it.second.versionCode);
+                snapshotProto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID,
+                                    (int)it.first);
             }
-            proto.end(token);
+            snapshotProto.end(token);
 
             // Copy ProtoOutputStream output to
-            auto iter = proto.data();
-            vector<char> outData(proto.size());
+            auto iter = snapshotProto.data();
+            vector<char> snapshotData(snapshotProto.size());
             size_t pos = 0;
             while (iter.readBuffer() != NULL) {
                 size_t toRead = iter.currentToRead();
-                std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+                std::memcpy(&(snapshotData[pos]), iter.readBuffer(), toRead);
                 pos += toRead;
                 iter.rp()->move(toRead);
             }
-            mSnapshots.emplace_back(timestamp, outData);
-            mBytesUsed += kBytesTimestampField + outData.size();
+            mSnapshots.emplace_back(timestamp, snapshotData);
+            mBytesUsed += kBytesTimestampField + snapshotData.size();
         }
     }
     StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
     StatsdStats::getInstance().setUidMapChanges(mChanges.size());
     StatsdStats::getInstance().setUidMapSnapshots(mSnapshots.size());
-    if (outData != nullptr) {
-        outData->clear();
-        outData->resize(proto.size());
-        size_t pos = 0;
-        auto iter = proto.data();
-        while (iter.readBuffer() != NULL) {
-            size_t toRead = iter.currentToRead();
-            std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead);
-            pos += toRead;
-            iter.rp()->move(toRead);
-        }
-    }
 }
 
 void UidMap::printUidMap(FILE* out) const {
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index b0181f7..9dc73f4 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -19,6 +19,7 @@
 #include "config/ConfigKey.h"
 #include "config/ConfigListener.h"
 #include "packages/PackageInfoListener.h"
+#include "stats_util.h"
 
 #include <binder/IResultReceiver.h>
 #include <binder/IShellCallback.h>
@@ -32,8 +33,11 @@
 #include <string>
 #include <unordered_map>
 
+using namespace android;
 using namespace std;
 
+using android::util::ProtoOutputStream;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -45,8 +49,8 @@
     AppData(const string& a, const int64_t v) : packageName(a), versionCode(v){};
 };
 
-// When calling getOutput, we retrieve all the ChangeRecords since the last
-// timestamp we called getOutput for this configuration key.
+// When calling appendUidMap, we retrieve all the ChangeRecords since the last
+// timestamp we called appendUidMap for this configuration key.
 struct ChangeRecord {
     const bool deletion;
     const int64_t timestampNs;
@@ -70,8 +74,8 @@
 // less because of varint encoding).
 const unsigned int kBytesTimestampField = 10;
 
-// When calling getOutput, we retrieve all the snapshots since the last
-// timestamp we called getOutput for this configuration key.
+// When calling appendUidMap, we retrieve all the snapshots since the last
+// timestamp we called appendUidMap for this configuration key.
 struct SnapshotRecord {
     const int64_t timestampNs;
 
@@ -135,7 +139,7 @@
     // Gets all snapshots and changes that have occurred since the last output.
     // If every config key has received a change or snapshot record, then this
     // record is deleted.
-    void getOutput(const ConfigKey& key, vector<uint8_t>* outData);
+    void appendUidMap(const ConfigKey& key, util::ProtoOutputStream* proto);
 
     // Forces the output to be cleared. We still generate a snapshot based on the current state.
     // This results in extra data uploaded but helps us reconstruct the uid mapping on the server
@@ -158,7 +162,8 @@
                    const int64_t& versionCode);
     void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
 
-    void getOutput(const int64_t& timestamp, const ConfigKey& key, vector<uint8_t>* outData);
+    void appendUidMap(const int64_t& timestamp, const ConfigKey& key,
+                      util::ProtoOutputStream* proto);
 
     void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
 
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index cd41f53..3d8aa47 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -160,38 +160,46 @@
     }
 }
 
-void StorageManager::appendConfigMetricsReport(ProtoOutputStream& proto) {
+void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto) {
     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
     if (dir == NULL) {
         VLOG("Path %s does not exist", STATS_DATA_DIR);
         return;
     }
 
+    const char* suffix =
+            StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()).c_str();
+
     dirent* de;
     while ((de = readdir(dir.get()))) {
         char* name = de->d_name;
         if (name[0] == '.') continue;
-        VLOG("file %s", name);
 
-        int64_t result[3];
-        parseFileName(name, result);
-        if (result[0] == -1) continue;
-        int64_t timestamp = result[0];
-        int64_t uid = result[1];
-        int64_t configID = result[2];
-        string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID);
-        int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
-        if (fd != -1) {
-            string content;
-            if (android::base::ReadFdToString(fd, &content)) {
-                proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
-                            content.c_str());
+        size_t nameLen = strlen(name);
+        size_t suffixLen = strlen(suffix);
+        if (suffixLen <= nameLen &&
+                strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
+            int64_t result[3];
+            parseFileName(name, result);
+            if (result[0] == -1) continue;
+            int64_t timestamp = result[0];
+            int64_t uid = result[1];
+            int64_t configID = result[2];
+
+            string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID);
+            int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
+            if (fd != -1) {
+                string content;
+                if (android::base::ReadFdToString(fd, &content)) {
+                    proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
+                                content.c_str(), content.size());
+                }
+                close(fd);
             }
-            close(fd);
-        }
 
-        // Remove file from disk after reading.
-        remove(file_name.c_str());
+            // Remove file from disk after reading.
+            remove(file_name.c_str());
+        }
     }
 }
 
@@ -275,9 +283,11 @@
                 if (android::base::ReadFdToString(fd, &content)) {
                     vector<uint8_t> vec(content.begin(), content.end());
                     if (vec == config) {
+                        close(fd);
                         return true;
                     }
                 }
+                close(fd);
             }
         }
     }
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 4b75628..fb7b085 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -66,7 +66,7 @@
      * Appends ConfigMetricsReport found on disk to the specific proto and
      * delete it.
      */
-    static void appendConfigMetricsReport(ProtoOutputStream& proto);
+    static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto);
 
     /**
      * Call to load the saved configs from disk.
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
new file mode 100644
index 0000000..a7b4136
--- /dev/null
+++ b/cmds/statsd/tests/StatsService_test.cpp
@@ -0,0 +1,67 @@
+// 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.
+
+#include "StatsService.h"
+#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+
+using namespace android;
+using namespace testing;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using android::util::ProtoOutputStream;
+
+#ifdef __ANDROID__
+
+TEST(StatsServiceTest, TestAddConfig_simple) {
+    StatsService service(nullptr);
+    StatsdConfig config;
+    config.set_id(12345);
+    string serialized = config.SerializeAsString();
+
+    EXPECT_TRUE(
+            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+}
+
+TEST(StatsServiceTest, TestAddConfig_empty) {
+    StatsService service(nullptr);
+    string serialized = "";
+
+    EXPECT_TRUE(
+            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+}
+
+TEST(StatsServiceTest, TestAddConfig_invalid) {
+    StatsService service(nullptr);
+    string serialized = "Invalid config!";
+
+    EXPECT_FALSE(
+            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index ee7d770..c9492eb 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -20,6 +20,7 @@
 #include "statslog.h"
 #include "statsd_test_util.h"
 
+#include <android/util/ProtoOutputStream.h>
 #include <gtest/gtest.h>
 
 #include <stdio.h>
@@ -30,6 +31,8 @@
 namespace os {
 namespace statsd {
 
+using android::util::ProtoOutputStream;
+
 #ifdef __ANDROID__
 const string kApp1 = "app1.sharing.1";
 const string kApp2 = "app2.sharing.1";
@@ -156,6 +159,20 @@
     EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end());
 }
 
+static void protoOutputStreamToUidMapping(ProtoOutputStream* proto, UidMapping* results) {
+    vector<uint8_t> bytes;
+    bytes.resize(proto->size());
+    size_t pos = 0;
+    auto iter = proto->data();
+    while (iter.readBuffer() != NULL) {
+        size_t toRead = iter.currentToRead();
+        std::memcpy(&((bytes)[pos]), iter.readBuffer(), toRead);
+        pos += toRead;
+        iter.rp()->move(toRead);
+    }
+    results->ParseFromArray(bytes.data(), bytes.size());
+}
+
 TEST(UidMapTest, TestClearingOutput) {
     UidMap m;
 
@@ -176,26 +193,26 @@
     m.updateMap(1, uids, versions, apps);
     EXPECT_EQ(1U, m.mSnapshots.size());
 
-    vector<uint8_t> bytes;
-    m.getOutput(2, config1, &bytes);
+    ProtoOutputStream proto;
+    m.appendUidMap(2, config1, &proto);
     UidMapping results;
-    results.ParseFromArray(bytes.data(), bytes.size());
+    protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
 
     // It should be cleared now
     EXPECT_EQ(1U, m.mSnapshots.size());
-    bytes.clear();
-    m.getOutput(2, config1, &bytes);
-    results.ParseFromArray(bytes.data(), bytes.size());
+    proto.clear();
+    m.appendUidMap(2, config1, &proto);
+    protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
 
     // Now add another configuration.
     m.OnConfigUpdated(config2);
     m.updateApp(5, String16(kApp1.c_str()), 1000, 40);
     EXPECT_EQ(1U, m.mChanges.size());
-    bytes.clear();
-    m.getOutput(6, config1, &bytes);
-    results.ParseFromArray(bytes.data(), bytes.size());
+    proto.clear();
+    m.appendUidMap(6, config1, &proto);
+    protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
     EXPECT_EQ(1, results.changes_size());
     EXPECT_EQ(1U, m.mChanges.size());
@@ -205,16 +222,16 @@
     EXPECT_EQ(2U, m.mChanges.size());
 
     // We still can't remove anything.
-    bytes.clear();
-    m.getOutput(8, config1, &bytes);
-    results.ParseFromArray(bytes.data(), bytes.size());
+    proto.clear();
+    m.appendUidMap(8, config1, &proto);
+    protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
     EXPECT_EQ(1, results.changes_size());
     EXPECT_EQ(2U, m.mChanges.size());
 
-    bytes.clear();
-    m.getOutput(9, config2, &bytes);
-    results.ParseFromArray(bytes.data(), bytes.size());
+    proto.clear();
+    m.appendUidMap(9, config2, &proto);
+    protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
     EXPECT_EQ(2, results.changes_size());
     // At this point both should be cleared.
@@ -242,11 +259,12 @@
     m.updateApp(3, String16(kApp1.c_str()), 1000, 40);
     EXPECT_TRUE(m.mBytesUsed > snapshot_bytes);
 
+    ProtoOutputStream proto;
     vector<uint8_t> bytes;
-    m.getOutput(2, config1, &bytes);
+    m.appendUidMap(2, config1, &proto);
     size_t prevBytes = m.mBytesUsed;
 
-    m.getOutput(4, config1, &bytes);
+    m.appendUidMap(4, config1, &proto);
     EXPECT_TRUE(m.mBytesUsed < prevBytes);
 }
 
@@ -286,4 +304,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index e2121dd..e238f06 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -20,8 +20,20 @@
 Landroid/app/ActivityManager;->PROCESS_STATE_IMPORTANT_BACKGROUND:I
 Landroid/app/ActivityManager;->PROCESS_STATE_TOP:I
 Landroid/app/ActivityManager$RecentTaskInfo;->firstActiveTime:J
+Landroid/app/ActivityManager$RecentTaskInfo;->lastActiveTime:J
+Landroid/app/ActivityManager$RecentTaskInfo;->resizeMode:I
+Landroid/app/ActivityManager$RecentTaskInfo;->supportsSplitScreenMultiWindow:Z
+Landroid/app/ActivityManager$RecentTaskInfo;->userId:I
 Landroid/app/ActivityManager$RunningAppProcessInfo;->flags:I
 Landroid/app/ActivityManager$RunningAppProcessInfo;->processState:I
+Landroid/app/ActivityManager;->setPersistentVrThread(I)V
+Landroid/app/ActivityManager$TaskDescription;->getBackgroundColor()I
+Landroid/app/ActivityManager$TaskDescription;->getInMemoryIcon()Landroid/graphics/Bitmap;
+Landroid/app/ActivityManager$TaskSnapshot;->getContentInsets()Landroid/graphics/Rect;
+Landroid/app/ActivityManager$TaskSnapshot;->getOrientation()I
+Landroid/app/ActivityManager$TaskSnapshot;->getScale()F
+Landroid/app/ActivityManager$TaskSnapshot;->isRealSnapshot()Z
+Landroid/app/ActivityManager$TaskSnapshot;->isReducedResolution()Z
 Landroid/app/Activity;->mApplication:Landroid/app/Application;
 Landroid/app/Activity;->mComponent:Landroid/content/ComponentName;
 Landroid/app/Activity;->mFragments:Landroid/app/FragmentController;
@@ -35,6 +47,7 @@
 Landroid/app/Activity;->mToken:Landroid/os/IBinder;
 Landroid/app/Activity;->mWindow:Landroid/view/Window;
 Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager;
+Landroid/app/ActivityOptions;->makeMultiThumbFutureAspectScaleAnimation(Landroid/content/Context;Landroid/os/Handler;Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/app/ActivityOptions$OnAnimationStartedListener;Z)Landroid/app/ActivityOptions;
 Landroid/app/Activity;->setDisablePreviewScreenshots(Z)V
 Landroid/app/Activity;->setPersistent(Z)V
 Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo;
@@ -317,6 +330,13 @@
 Landroid/app/usage/UsageStatsManager;->mService:Landroid/app/usage/IUsageStatsManager;
 Landroid/app/usage/UsageStats;->mLastEvent:I
 Landroid/app/usage/UsageStats;->mTotalTimeInForeground:J
+Landroid/app/Vr2dDisplayProperties$Builder;->build()Landroid/app/Vr2dDisplayProperties;
+Landroid/app/Vr2dDisplayProperties$Builder;-><init>()V
+Landroid/app/Vr2dDisplayProperties$Builder;->setEnabled(Z)Landroid/app/Vr2dDisplayProperties$Builder;
+Landroid/app/Vr2dDisplayProperties;-><init>(III)V
+Landroid/app/VrManager;->getPersistentVrModeEnabled()Z
+Landroid/app/VrManager;->registerVrStateCallback(Landroid/app/VrStateCallback;Landroid/os/Handler;)V
+Landroid/app/VrManager;->setVr2dDisplayProperties(Landroid/app/Vr2dDisplayProperties;)V
 Landroid/app/WallpaperColors;->getColorHints()I
 Landroid/app/WallpaperManager;->getBitmap()Landroid/graphics/Bitmap;
 Landroid/app/WallpaperManager;->getBitmap(Z)Landroid/graphics/Bitmap;
@@ -497,6 +517,7 @@
 Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z
 Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V
 Landroid/content/res/ColorStateList$ColorStateListFactory;-><init>(Landroid/content/res/ColorStateList;)V
+Landroid/content/res/ColorStateList;->getColors()[I
 Landroid/content/res/ColorStateList;->mColors:[I
 Landroid/content/res/ColorStateList;->mDefaultColor:I
 Landroid/content/res/ColorStateList;->mFactory:Landroid/content/res/ColorStateList$ColorStateListFactory;
@@ -563,6 +584,7 @@
 Landroid/database/sqlite/SQLiteDebug$PagerStats;->pageCacheOverflow:I
 Landroid/database/sqlite/SQLiteOpenHelper;->mName:Ljava/lang/String;
 Landroid/ddm/DdmHandleAppName;->getAppName()Ljava/lang/String;
+Landroid/graphics/AvoidXfermode$Mode;->TARGET:Landroid/graphics/AvoidXfermode$Mode;
 Landroid/graphics/BaseCanvas;->mNativeCanvasWrapper:J
 Landroid/graphics/Bitmap$Config;->nativeInt:I
 Landroid/graphics/Bitmap$Config;->nativeToConfig(I)Landroid/graphics/Bitmap$Config;
@@ -643,6 +665,7 @@
 Landroid/graphics/fonts/FontVariationAxis;->mStyleValue:F
 Landroid/graphics/fonts/FontVariationAxis;->mTag:I
 Landroid/graphics/GraphicBuffer;->createFromExisting(IIIIJ)Landroid/graphics/GraphicBuffer;
+Landroid/graphics/GraphicBuffer;->CREATOR:Landroid/os/Parcelable$Creator;
 Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V
 Landroid/graphics/GraphicBuffer;->mNativeObject:J
 Landroid/graphics/ImageDecoder;-><init>(JIIZ)V
@@ -671,8 +694,13 @@
 Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V
 Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map;
 Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
+Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
 Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
 Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
+Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_TIMESTAMPS:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_X_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_Y_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key;
 Landroid/hardware/camera2/impl/CameraMetadataNative;->mMetadataPtr:J
 Landroid/hardware/Camera;->addCallbackBuffer([BI)V
 Landroid/hardware/Camera;->mNativeContext:J
@@ -829,6 +857,7 @@
 Landroid/media/AudioManager;->forceVolumeControlStream(I)V
 Landroid/media/AudioManager;->getOutputLatency(I)I
 Landroid/media/AudioManager;->getService()Landroid/media/IAudioService;
+Landroid/media/AudioManager;-><init>(Landroid/content/Context;)V
 Landroid/media/AudioManager;->mAudioFocusIdListenerMap:Ljava/util/concurrent/ConcurrentHashMap;
 Landroid/media/AudioManager;->setMasterMute(ZI)V
 Landroid/media/AudioManager;->startBluetoothScoVirtualCall()V
@@ -891,6 +920,7 @@
 Landroid/media/IAudioService;->setStreamVolume(IIILjava/lang/String;)V
 Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService;
 Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController;
 Landroid/media/JetPlayer;->mNativePlayerInJavaObj:J
 Landroid/media/JetPlayer;->postEventFromNative(Ljava/lang/Object;III)V
 Landroid/media/MediaCodec$CodecException;-><init>(IILjava/lang/String;)V
@@ -1128,6 +1158,7 @@
 Landroid/os/Binder;->execTransact(IJJI)Z
 Landroid/os/Binder;->mObject:J
 Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/Build;->IS_DEBUGGABLE:Z
 Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String;
 Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder;
 Landroid/os/Bundle;->putIBinder(Ljava/lang/String;Landroid/os/IBinder;)V
@@ -1212,8 +1243,10 @@
 Landroid/os/ParcelFileDescriptor;-><init>(Ljava/io/FileDescriptor;)V
 Landroid/os/Parcel;->mNativePtr:J
 Landroid/os/Parcel;->readArrayMap(Landroid/util/ArrayMap;Ljava/lang/ClassLoader;)V
+Landroid/os/Parcel;->readParcelableList(Ljava/util/List;Ljava/lang/ClassLoader;)Ljava/util/List;
 Landroid/os/Parcel$ReadWriteHelper;-><init>()V
 Landroid/os/Parcel;->writeArrayMap(Landroid/util/ArrayMap;)V
+Landroid/os/Parcel;->writeParcelableList(Ljava/util/List;I)V
 Landroid/os/PowerManager;->getDefaultScreenBrightnessSetting()I
 Landroid/os/PowerManager;->getMaximumScreenBrightnessSetting()I
 Landroid/os/PowerManager;->getMinimumScreenBrightnessSetting()I
@@ -1584,6 +1617,8 @@
 Landroid/R$styleable;->View_fitsSystemWindows:I
 Landroid/R$styleable;->View_focusable:I
 Landroid/R$styleable;->View_focusableInTouchMode:I
+Landroid/R$styleable;->ViewGroup_Layout:[I
+Landroid/R$styleable;->ViewGroup_MarginLayout:[I
 Landroid/R$styleable;->View_hapticFeedbackEnabled:I
 Landroid/R$styleable;->View:[I
 Landroid/R$styleable;->View_id:I
@@ -1673,6 +1708,7 @@
 Landroid/service/notification/NotificationListenerService;->unregisterAsSystemService()V
 Landroid/service/voice/AlwaysOnHotwordDetector$EventPayload;->getCaptureSession()Ljava/lang/Integer;
 Landroid/service/voice/VoiceInteractionService;->isKeyphraseAndLocaleSupportedForHotword(Ljava/lang/String;Ljava/util/Locale;)Z
+Landroid/service/vr/IVrManager;->getVr2dDisplayId()I
 Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V
 Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String;
 Landroid/system/Int32Ref;->value:I
@@ -1814,6 +1850,7 @@
 Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout;
 Landroid/text/Html;->withinStyle(Ljava/lang/StringBuilder;Ljava/lang/CharSequence;II)V
 Landroid/text/Layout;->DIRS_ALL_LEFT_TO_RIGHT:Landroid/text/Layout$Directions;
+Landroid/text/Layout;->getPrimaryHorizontal(IZ)F
 Landroid/text/method/LinkMovementMethod;->sInstance:Landroid/text/method/LinkMovementMethod;
 Landroid/text/SpannableStringBuilder;->mGapLength:I
 Landroid/text/SpannableStringBuilder;->mGapStart:I
@@ -1962,9 +1999,11 @@
 Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V
 Landroid/view/inputmethod/InputMethodSubtypeArray;-><init>(Ljava/util/List;)V
 Landroid/view/InputQueue;->finishInputEvent(JZ)V
+Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V
 Landroid/view/IWindowManager;->getAnimationScale(I)F
 Landroid/view/IWindowManager;->hasNavigationBar()Z
 Landroid/view/IWindowManager;->setAnimationScale(IF)V
+Landroid/view/IWindowManager;->setShelfHeight(ZI)V
 Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
 Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
 Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I
@@ -2012,6 +2051,18 @@
 Landroid/view/PointerIcon;->mHotSpotX:F
 Landroid/view/PointerIcon;->mHotSpotY:F
 Landroid/view/PointerIcon;->mType:I
+Landroid/view/RemoteAnimationDefinition;->addRemoteAnimation(IILandroid/view/RemoteAnimationAdapter;)V
+Landroid/view/RemoteAnimationTarget;->clipRect:Landroid/graphics/Rect;
+Landroid/view/RemoteAnimationTarget;->contentInsets:Landroid/graphics/Rect;
+Landroid/view/RemoteAnimationTarget;->isNotInRecents:Z
+Landroid/view/RemoteAnimationTarget;->isTranslucent:Z
+Landroid/view/RemoteAnimationTarget;->leash:Landroid/view/SurfaceControl;
+Landroid/view/RemoteAnimationTarget;->mode:I
+Landroid/view/RemoteAnimationTarget;->position:Landroid/graphics/Point;
+Landroid/view/RemoteAnimationTarget;->prefixOrderIndex:I
+Landroid/view/RemoteAnimationTarget;->sourceContainerBounds:Landroid/graphics/Rect;
+Landroid/view/RemoteAnimationTarget;->taskId:I
+Landroid/view/RemoteAnimationTarget;->windowConfiguration:Landroid/app/WindowConfiguration;
 Landroid/view/RenderNodeAnimator;->callOnFinished(Landroid/view/RenderNodeAnimator;)V
 Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener;
 Landroid/view/ScaleGestureDetector;->mMinSpan:I
@@ -2035,6 +2086,7 @@
 Landroid/view/SurfaceView;->mSurfaceHolder:Landroid/view/SurfaceHolder;
 Landroid/view/SurfaceView;->surfacePositionLost_uiRtSync(J)V
 Landroid/view/SurfaceView;->updateSurfacePosition_renderWorker(JIIII)V
+Landroid/view/textclassifier/Logger;->DISABLED:Landroid/view/textclassifier/Logger;
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker;-><init>(Landroid/content/Context;I)V
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker;->logEvent(Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;)V
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(III)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
@@ -2061,6 +2113,7 @@
 Landroid/view/View;->clearAccessibilityFocus()V
 Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z
 Landroid/view/View;->computeOpaqueFlags()V
+Landroid/view/ViewConfiguration;->getDeviceGlobalActionKeyTimeout()J
 Landroid/view/ViewConfiguration;->getDoubleTapMinTime()I
 Landroid/view/ViewConfiguration;->mFadingMarqueeEnabled:Z
 Landroid/view/ViewConfiguration;->sHasPermanentMenuKeySet:Z
@@ -2072,11 +2125,15 @@
 Landroid/view/View;->dispatchDetachedFromWindow()V
 Landroid/view/View;->fitsSystemWindows()Z
 Landroid/view/View;->getAccessibilityDelegate()Landroid/view/View$AccessibilityDelegate;
+Landroid/view/View;->getBoundsOnScreen(Landroid/graphics/Rect;)V
 Landroid/view/View;->getInverseMatrix()Landroid/graphics/Matrix;
 Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo;
 Landroid/view/View;->getLocationOnScreen()[I
+Landroid/view/View;->getRawTextAlignment()I
+Landroid/view/View;->getRawTextDirection()I
 Landroid/view/View;->getTransitionAlpha()F
 Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl;
+Landroid/view/View;->getWindowDisplayFrame(Landroid/graphics/Rect;)V
 Landroid/view/ViewGroup;->dispatchViewAdded(Landroid/view/View;)V
 Landroid/view/ViewGroup;->dispatchViewRemoved(Landroid/view/View;)V
 Landroid/view/ViewGroup;->FLAG_SUPPORT_STATIC_TRANSFORMATIONS:I
@@ -2089,6 +2146,11 @@
 Landroid/view/ViewGroup;->mFirstTouchTarget:Landroid/view/ViewGroup$TouchTarget;
 Landroid/view/ViewGroup;->mGroupFlags:I
 Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener;
+Landroid/view/ViewGroup;->resetResolvedDrawables()V
+Landroid/view/ViewGroup;->resetResolvedLayoutDirection()V
+Landroid/view/ViewGroup;->resetResolvedPadding()V
+Landroid/view/ViewGroup;->resetResolvedTextAlignment()V
+Landroid/view/ViewGroup;->resetResolvedTextDirection()V
 Landroid/view/ViewGroup;->suppressLayout(Z)V
 Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V
 Landroid/view/View;->internalSetPadding(IIII)V
@@ -2128,10 +2190,17 @@
 Landroid/view/View;->requestAccessibilityFocus()Z
 Landroid/view/View;->resetDisplayList()V
 Landroid/view/View;->resetPaddingToInitialValues()V
+Landroid/view/View;->resetResolvedDrawables()V
+Landroid/view/View;->resetResolvedLayoutDirection()V
+Landroid/view/View;->resetResolvedPadding()V
+Landroid/view/View;->resetResolvedTextAlignment()V
+Landroid/view/View;->resetResolvedTextDirection()V
+Landroid/view/View;->resetRtlProperties()V
 Landroid/view/ViewRootImpl;->detachFunctor(J)V
 Landroid/view/ViewRootImpl;->enqueueInputEvent(Landroid/view/InputEvent;)V
 Landroid/view/ViewRootImpl;->invokeFunctor(JZ)V
 Landroid/view/ViewRootImpl;->mStopped:Z
+Landroid/view/ViewRootImpl;->mSurface:Landroid/view/Surface;
 Landroid/view/ViewRootImpl;->mView:Landroid/view/View;
 Landroid/view/View$ScrollabilityCache;->scrollBar:Landroid/widget/ScrollBarDrawable;
 Landroid/view/View;->setAlphaNoInvalidation(F)Z
@@ -2241,6 +2310,10 @@
 Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow;
 Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V
 Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/CursorAdapter;->mChangeObserver:Landroid/widget/CursorAdapter$ChangeObserver;
+Landroid/widget/CursorAdapter;->mDataSetObserver:Landroid/database/DataSetObserver;
+Landroid/widget/CursorAdapter;->mDataValid:Z
+Landroid/widget/CursorAdapter;->mRowIDColumn:I
 Landroid/widget/DatePicker;->mDelegate:Landroid/widget/DatePicker$DatePickerDelegate;
 Landroid/widget/EdgeEffect;->mPaint:Landroid/graphics/Paint;
 Landroid/widget/Editor;->invalidateTextDisplayList()V
@@ -2290,6 +2363,8 @@
 Landroid/widget/ListView;->fillDown(II)Landroid/view/View;
 Landroid/widget/ListView;->fillSpecific(II)Landroid/view/View;
 Landroid/widget/ListView;->fillUp(II)Landroid/view/View;
+Landroid/widget/ListView;->findViewTraversal(I)Landroid/view/View;
+Landroid/widget/ListView;->findViewWithTagTraversal(Ljava/lang/Object;)Landroid/view/View;
 Landroid/widget/ListView;->mAreAllItemsSelectable:Z
 Landroid/widget/ListView;->setSelectionInt(I)V
 Landroid/widget/MediaController;->mAnchor:Landroid/view/View;
@@ -2375,6 +2450,8 @@
 Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V
 Landroid/widget/TextView;->assumeLayout()V
 Landroid/widget/TextView;->createEditorIfNeeded()V
+Landroid/widget/TextView;->getHorizontallyScrolling()Z
+Landroid/widget/TextView;->getTextColor(Landroid/content/Context;Landroid/content/res/TypedArray;I)I
 Landroid/widget/TextView;->isSingleLine()Z
 Landroid/widget/TextView;->mCursorDrawableRes:I
 Landroid/widget/TextView;->mCurTextColor:I
@@ -2388,6 +2465,10 @@
 Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;ZI)V
 Landroid/widget/Toast;->getService()Landroid/app/INotificationManager;
 Landroid/widget/Toast;->sService:Landroid/app/INotificationManager;
+Landroid/widget/VideoView2;->getMediaController()Landroid/media/session/MediaController;
+Landroid/widget/VideoView2$OnViewTypeChangedListener;->onViewTypeChanged(Landroid/view/View;I)V
+Landroid/widget/VideoView2;->setOnViewTypeChangedListener(Landroid/widget/VideoView2$OnViewTypeChangedListener;)V
+Landroid/widget/VideoView2;->setVideoPath(Ljava/lang/String;)V
 Landroid/widget/VideoView;->mCurrentBufferPercentage:I
 Landroid/widget/VideoView;->mMediaController:Landroid/widget/MediaController;
 Landroid/widget/VideoView;->mSHCallback:Landroid/view/SurfaceHolder$Callback;
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 952b28b..fe9a5db 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -1,9 +1,24 @@
+Landroid/accounts/AccountManager;-><init>(Landroid/content/Context;Landroid/accounts/IAccountManager;Landroid/os/Handler;)V
+Landroid/app/Activity;->managedQuery(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
+Landroid/app/ActivityManagerNative;->broadcastStickyIntent(Landroid/content/Intent;Ljava/lang/String;I)V
 Landroid/app/ActivityManager$RecentTaskInfo;->configuration:Landroid/content/res/Configuration;
 Landroid/app/ActivityManager$TaskDescription;->loadTaskDescriptionIcon(Ljava/lang/String;I)Landroid/graphics/Bitmap;
 Landroid/app/ActivityManager$TaskSnapshot;->getSnapshot()Landroid/graphics/GraphicBuffer;
 Landroid/app/ActivityOptions;->makeRemoteAnimation(Landroid/view/RemoteAnimationAdapter;)Landroid/app/ActivityOptions;
 Landroid/app/ActivityOptions;->setSplitScreenCreateMode(I)V
 Landroid/app/Activity;->registerRemoteAnimations(Landroid/view/RemoteAnimationDefinition;)V
+Landroid/app/ActivityView;-><init>(Landroid/content/Context;)V
+Landroid/app/ActivityView;->release()V
+Landroid/app/ActivityView;->startActivity(Landroid/app/PendingIntent;)V
+Landroid/app/ActivityView;->startActivity(Landroid/content/Intent;)V
+Landroid/app/AppOpsManager;->getPackagesForOps([I)Ljava/util/List;
+Landroid/app/AppOpsManager;->getToken(Lcom/android/internal/app/IAppOpsService;)Landroid/os/IBinder;
+Landroid/app/AppOpsManager$OpEntry;->getOp()I
+Landroid/app/AppOpsManager$OpEntry;->getTime()J
+Landroid/app/AppOpsManager$OpEntry;->isRunning()Z
+Landroid/app/AppOpsManager$PackageOps;->getOps()Ljava/util/List;
+Landroid/app/AppOpsManager$PackageOps;->getPackageName()Ljava/lang/String;
+Landroid/app/AppOpsManager$PackageOps;->getUid()I
 Landroid/app/IActivityController$Stub;-><init>()V
 Landroid/app/IActivityManager;->cancelRecentsAnimation()V
 Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
@@ -11,12 +26,18 @@
 Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
 Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List;
 Landroid/app/IActivityManager;->getLockTaskModeState()I
+Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo;
 Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice;
+Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;
 Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
 Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
 Landroid/app/IActivityManager;->removeTask(I)Z
+Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
 Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
+Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
 Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V
+Landroid/app/IAlarmManager;->setTime(J)Z
+Landroid/app/IAlarmManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IAlarmManager;
 Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
 Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
 Landroid/app/IAssistDataReceiver$Stub;-><init>()V
@@ -40,6 +61,8 @@
 Landroid/app/VrStateCallback;-><init>()V
 Landroid/app/VrStateCallback;->onPersistentVrStateChanged(Z)V
 Landroid/app/WallpaperColors;-><init>(Landroid/graphics/Color;Landroid/graphics/Color;Landroid/graphics/Color;I)V
+Landroid/bluetooth/BluetoothHeadset;->phoneStateChanged(IIILjava/lang/String;I)V
+Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
 Landroid/companion/AssociationRequest;->getDeviceFilters()Ljava/util/List;
 Landroid/companion/AssociationRequest;->isSingleDevice()Z
 Landroid/companion/BluetoothDeviceFilter;->getAddress()Ljava/lang/String;
@@ -53,19 +76,31 @@
 Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
 Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
 Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V
+Landroid/content/ContentProvider;->attachInfoForTesting(Landroid/content/Context;Landroid/content/pm/ProviderInfo;)V
+Landroid/content/ContentProvider;->getIContentProvider()Landroid/content/IContentProvider;
+Landroid/content/ContentProvider;-><init>(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Landroid/content/pm/PathPermission;)V
+Landroid/content/ContentResolver;->registerContentObserver(Landroid/net/Uri;ZLandroid/database/ContentObserver;I)V
+Landroid/content/ContentValues;->getStringArrayList(Ljava/lang/String;)Ljava/util/ArrayList;
+Landroid/content/ContentValues;->putStringArrayList(Ljava/lang/String;Ljava/util/ArrayList;)V
 Landroid/content/Context;->getOpPackageName()Ljava/lang/String;
 Landroid/content/Context;->registerReceiverAsUser(Landroid/content/BroadcastReceiver;Landroid/os/UserHandle;Landroid/content/IntentFilter;Ljava/lang/String;Landroid/os/Handler;)Landroid/content/Intent;
 Landroid/content/Context;->startActivityAsUser(Landroid/content/Intent;Landroid/os/UserHandle;)V
 Landroid/content/Context;->startServiceAsUser(Landroid/content/Intent;Landroid/os/UserHandle;)Landroid/content/ComponentName;
 Landroid/content/ContextWrapper;->getThemeResId()I
+Landroid/content/Intent;->getExtra(Ljava/lang/String;)Ljava/lang/Object;
+Landroid/content/Intent;->getIBinderExtra(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/content/Intent;->resolveSystemService(Landroid/content/pm/PackageManager;I)Landroid/content/ComponentName;
 Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
 Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V
 Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
+Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
 Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName;
+Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
 Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
 Landroid/database/sqlite/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
+Landroid/database/sqlite/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
 Landroid/graphics/AvoidXfermode;-><init>(IILandroid/graphics/AvoidXfermode$Mode;)V
 Landroid/graphics/Bitmap;->createGraphicBufferHandle()Landroid/graphics/GraphicBuffer;
 Landroid/graphics/Bitmap;->createHardwareBitmap(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;
@@ -74,17 +109,25 @@
 Landroid/graphics/drawable/Drawable;->isProjected()Z
 Landroid/graphics/drawable/Drawable;->updateTintFilter(Landroid/graphics/PorterDuffColorFilter;Landroid/content/res/ColorStateList;Landroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuffColorFilter;
 Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
+Landroid/hardware/display/DisplayManagerGlobal;->getInstance()Landroid/hardware/display/DisplayManagerGlobal;
+Landroid/hardware/display/DisplayManagerGlobal;->getRealDisplay(I)Landroid/view/Display;
 Landroid/hardware/location/GeofenceHardware;-><init>(Landroid/hardware/location/IGeofenceHardware;)V
 Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
 Landroid/location/IFusedProvider;->onFusedLocationHardwareChange(Landroid/hardware/location/IFusedLocationHardware;)V
 Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
 Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
 Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V
+Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
 Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
+Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
+Landroid/location/INetInitiatedListener$Stub;-><init>()V
+Landroid/location/Location;->setExtraLocation(Ljava/lang/String;Landroid/location/Location;)V
 Landroid/media/AudioManager;->registerAudioPortUpdateListener(Landroid/media/AudioManager$OnAudioPortUpdateListener;)V
 Landroid/media/AudioManager;->unregisterAudioPortUpdateListener(Landroid/media/AudioManager$OnAudioPortUpdateListener;)V
 Landroid/media/AudioSystem;->checkAudioFlinger()I
+Landroid/media/AudioSystem;->getForceUse(I)I
 Landroid/media/AudioSystem;->getParameters(Ljava/lang/String;)Ljava/lang/String;
+Landroid/media/AudioSystem;->setForceUse(II)I
 Landroid/media/AudioSystem;->setParameters(Ljava/lang/String;)I
 Landroid/media/MediaDrm$Certificate;->getContent()[B
 Landroid/media/MediaDrm$Certificate;->getWrappedPrivateKey()[B
@@ -103,14 +146,18 @@
 Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V
 Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V
 Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V
+Landroid/net/ConnectivityManager;->getActiveNetworkQuotaInfo()Landroid/net/NetworkQuotaInfo;
 Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V
 Landroid/net/ConnectivityManager$PacketKeepaliveCallback;->onError(I)V
 Landroid/net/ConnectivityManager$PacketKeepaliveCallback;->onStarted()V
 Landroid/net/ConnectivityManager$PacketKeepaliveCallback;->onStopped()V
 Landroid/net/ConnectivityManager$PacketKeepalive;->stop()V
+Landroid/net/ConnectivityManager;->setAirplaneMode(Z)V
 Landroid/net/ConnectivityManager;->startNattKeepalive(Landroid/net/Network;ILandroid/net/ConnectivityManager$PacketKeepaliveCallback;Ljava/net/InetAddress;ILjava/net/InetAddress;)Landroid/net/ConnectivityManager$PacketKeepalive;
 Landroid/net/ConnectivityManager;->startUsingNetworkFeature(ILjava/lang/String;)I
 Landroid/net/ConnectivityManager;->stopUsingNetworkFeature(ILjava/lang/String;)I
+Landroid/net/ConnectivityManager;->tether(Ljava/lang/String;)I
+Landroid/net/ConnectivityManager;->untether(Ljava/lang/String;)I
 Landroid/net/DhcpResults;-><init>(Landroid/net/DhcpResults;)V
 Landroid/net/DhcpResults;-><init>(Landroid/net/StaticIpConfiguration;)V
 Landroid/net/DhcpResults;-><init>()V
@@ -138,6 +185,8 @@
 Landroid/net/LinkProperties;->addStackedLink(Landroid/net/LinkProperties;)Z
 Landroid/net/LinkProperties;->clear()V
 Landroid/net/LinkProperties;->compareProvisioning(Landroid/net/LinkProperties;Landroid/net/LinkProperties;)Landroid/net/LinkProperties$ProvisioningChange;
+Landroid/net/LinkProperties;->getAllInterfaceNames()Ljava/util/List;
+Landroid/net/LinkProperties;->getAllRoutes()Ljava/util/List;
 Landroid/net/LinkProperties;->getMtu()I
 Landroid/net/LinkProperties;->getStackedLinks()Ljava/util/List;
 Landroid/net/LinkProperties;->hasGlobalIPv6Address()Z
@@ -226,12 +275,23 @@
 Landroid/net/NetworkCapabilities;->hasSignalStrength()Z
 Landroid/net/NetworkCapabilities;->transportNamesOf([I)Ljava/lang/String;
 Landroid/net/Network;-><init>(I)V
+Landroid/net/Network;->netId:I
 Landroid/net/NetworkQuotaInfo;->getEstimatedBytes()J
 Landroid/net/NetworkQuotaInfo;->getHardLimitBytes()J
 Landroid/net/NetworkQuotaInfo;->getSoftLimitBytes()J
 Landroid/net/NetworkRequest$Builder;->setSignalStrength(I)Landroid/net/NetworkRequest$Builder;
+Landroid/net/NetworkRequest;->networkCapabilities:Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkState;->network:Landroid/net/Network;
 Landroid/net/NetworkStats;->combineValues(Landroid/net/NetworkStats$Entry;)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats$Entry;->iface:Ljava/lang/String;
 Landroid/net/NetworkStats$Entry;-><init>()V
+Landroid/net/NetworkStats$Entry;->rxBytes:J
+Landroid/net/NetworkStats$Entry;->rxPackets:J
+Landroid/net/NetworkStats$Entry;->set:I
+Landroid/net/NetworkStats$Entry;->tag:I
+Landroid/net/NetworkStats$Entry;->txBytes:J
+Landroid/net/NetworkStats$Entry;->txPackets:J
+Landroid/net/NetworkStats$Entry;->uid:I
 Landroid/net/NetworkStatsHistory$Entry;->txBytes:J
 Landroid/net/NetworkStatsHistory;->getStart()J
 Landroid/net/NetworkStatsHistory;->getValues(JJLandroid/net/NetworkStatsHistory$Entry;)Landroid/net/NetworkStatsHistory$Entry;
@@ -245,7 +305,10 @@
 Landroid/net/NetworkUtils;->protectFromVpn(Ljava/io/FileDescriptor;)Z
 Landroid/net/RouteInfo;->hasGateway()Z
 Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;Ljava/net/InetAddress;Ljava/lang/String;)V
+Landroid/net/RouteInfo;->selectBestRoute(Ljava/util/Collection;Ljava/net/InetAddress;)Landroid/net/RouteInfo;
 Landroid/net/SntpClient;->getNtpTime()J
+Landroid/net/SntpClient;->getNtpTimeReference()J
+Landroid/net/SntpClient;->getRoundTripTime()J
 Landroid/net/SntpClient;->requestTime(Ljava/lang/String;I)Z
 Landroid/net/StaticIpConfiguration;->dnsServers:Ljava/util/ArrayList;
 Landroid/net/StaticIpConfiguration;->domains:Ljava/lang/String;
@@ -268,27 +331,36 @@
 Landroid/os/BatteryStats$HistoryItem;-><init>()V
 Landroid/os/BatteryStats$HistoryItem;->states:I
 Landroid/os/BatteryStats$HistoryItem;->time:J
+Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
 Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
 Landroid/os/BatteryStats$Uid;-><init>()V
 Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
+Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V
+Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V
+Landroid/os/Broadcaster;-><init>()V
+Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V
+Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File;
 Landroid/os/Handler;->getMain()Landroid/os/Handler;
 Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V
 Landroid/os/HwBinder;->reportSyspropChanged()V
 Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z
 Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V
 Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V
 Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V
 Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V
 Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService;
 Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V
+Landroid/os/IPowerManager;->goToSleep(JII)V
 Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
 Landroid/os/IRemoteCallback$Stub;-><init>()V
 Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message;
 Landroid/os/Parcel;->readBlob()[B
 Landroid/os/Parcel;->readStringArray()[Ljava/lang/String;
 Landroid/os/Parcel;->writeBlob([B)V
+Landroid/os/PowerManager;->goToSleep(J)V
 Landroid/os/PowerManager;->isScreenBrightnessBoosted()Z
 Landroid/os/Registrant;->clear()V
 Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
@@ -303,10 +375,35 @@
 Landroid/os/Registrant;->notifyRegistrant()V
 Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
 Landroid/os/ServiceSpecificException;->errorCode:I
+Landroid/os/storage/DiskInfo;->getId()Ljava/lang/String;
+Landroid/os/storage/StorageEventListener;-><init>()V
+Landroid/os/storage/StorageManager;->findVolumeById(Ljava/lang/String;)Landroid/os/storage/VolumeInfo;
+Landroid/os/storage/StorageManager;->from(Landroid/content/Context;)Landroid/os/storage/StorageManager;
+Landroid/os/storage/StorageManager;->registerListener(Landroid/os/storage/StorageEventListener;)V
+Landroid/os/storage/StorageManager;->unregisterListener(Landroid/os/storage/StorageEventListener;)V
+Landroid/os/storage/StorageVolume;->getId()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getId()Ljava/lang/String;
 Landroid/os/SystemProperties;->reportSyspropChanged()V
+Landroid/os/SystemService;->start(Ljava/lang/String;)V
+Landroid/os/SystemService;->stop(Ljava/lang/String;)V
+Landroid/os/SystemVibrator;-><init>()V
+Landroid/os/UserHandle;->isSameApp(II)Z
+Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->isAdminUser()Z
 Landroid/print/PrintDocumentAdapter$LayoutResultCallback;-><init>()V
 Landroid/print/PrintDocumentAdapter$WriteResultCallback;-><init>()V
 Landroid/provider/CalendarContract$Events;->PROVIDER_WRITABLE_COLUMNS:[Ljava/lang/String;
+Landroid/provider/ContactsContract$CommonDataKinds$Phone;->getDisplayLabel(Landroid/content/Context;ILjava/lang/CharSequence;)Ljava/lang/CharSequence;
+Landroid/provider/Settings$Global;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String;
+Landroid/provider/Settings$Secure;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String;
+Landroid/provider/Telephony$Mms;->isEmailAddress(Ljava/lang/String;)Z
+Landroid/provider/Telephony$Sms$Draft;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Outbox;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZJ)Landroid/net/Uri;
+Landroid/R$styleable;->CheckBoxPreference:[I
+Landroid/service/dreams/DreamService;->canDoze()Z
+Landroid/service/dreams/DreamService;->isDozing()Z
+Landroid/service/dreams/DreamService;->startDozing()V
+Landroid/service/dreams/DreamService;->stopDozing()V
 Landroid/service/vr/VrListenerService;->onCurrentVrActivityChanged(Landroid/content/ComponentName;ZI)V
 Landroid/system/NetlinkSocketAddress;-><init>(II)V
 Landroid/system/Os;->bind(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
@@ -316,6 +413,11 @@
 Landroid/system/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
 Landroid/system/PacketSocketAddress;-><init>(I[B)V
 Landroid/system/PacketSocketAddress;-><init>(SI)V
+Landroid/telecom/ParcelableCall;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/telecom/ParcelableCall;->getConnectTimeMillis()J
+Landroid/telecom/ParcelableCall;->getDisconnectCause()Landroid/telecom/DisconnectCause;
+Landroid/telecom/ParcelableCall;->getHandle()Landroid/net/Uri;
+Landroid/telecom/ParcelableCall;->getId()Ljava/lang/String;
 Landroid/telecom/TelecomManager;->from(Landroid/content/Context;)Landroid/telecom/TelecomManager;
 Landroid/telecom/VideoProfile$CameraCapabilities;-><init>(IIZF)V
 Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
@@ -329,6 +431,14 @@
 Landroid/telephony/ims/ImsReasonInfo;-><init>(IILjava/lang/String;)V
 Landroid/telephony/ims/ImsReasonInfo;-><init>(II)V
 Landroid/telephony/ims/ImsStreamMediaProfile;-><init>()V
+Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
+Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
+Landroid/telephony/PhoneNumberUtils;->formatNumber(Ljava/lang/String;I)Ljava/lang/String;
 Landroid/telephony/PhoneNumberUtils;->isEmergencyNumber(ILjava/lang/String;)Z
 Landroid/telephony/PhoneNumberUtils;->isPotentialEmergencyNumber(ILjava/lang/String;)Z
 Landroid/telephony/PhoneNumberUtils;->isPotentialLocalEmergencyNumber(Landroid/content/Context;ILjava/lang/String;)Z
@@ -342,12 +452,20 @@
 Landroid/telephony/Rlog;->e(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/telephony/Rlog;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
 Landroid/telephony/Rlog;->i(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/telephony/ServiceState;->getDataRegState()I
 Landroid/telephony/ServiceState;->getDataRoaming()Z
 Landroid/telephony/ServiceState;->getRilDataRadioTechnology()I
+Landroid/telephony/ServiceState;->getVoiceNetworkType()I
 Landroid/telephony/ServiceState;->getVoiceRegState()I
 Landroid/telephony/ServiceState;->isCdma(I)Z
 Landroid/telephony/ServiceState;->isEmergencyOnly()Z
+Landroid/telephony/ServiceState;->isGsm(I)Z
 Landroid/telephony/ServiceState;->mergeServiceStates(Landroid/telephony/ServiceState;Landroid/telephony/ServiceState;)Landroid/telephony/ServiceState;
+Landroid/telephony/ServiceState;->rilRadioTechnologyToString(I)Ljava/lang/String;
+Landroid/telephony/SubscriptionInfo;->setDisplayName(Ljava/lang/CharSequence;)V
+Landroid/telephony/SubscriptionInfo;->setIconTint(I)V
+Landroid/telephony/SubscriptionManager;->clearDefaultsForInactiveSubIds()V
+Landroid/telephony/SubscriptionManager;->getDefaultVoicePhoneId()I
 Landroid/telephony/SubscriptionManager;->getResourcesForSubId(Landroid/content/Context;I)Landroid/content/res/Resources;
 Landroid/telephony/SubscriptionManager;->isActiveSubId(I)Z
 Landroid/telephony/SubscriptionManager;->isUsableSubIdValue(I)Z
@@ -355,9 +473,18 @@
 Landroid/telephony/SubscriptionManager;->isValidSlotIndex(I)Z
 Landroid/telephony/SubscriptionManager;->isValidSubscriptionId(I)Z
 Landroid/telephony/SubscriptionManager;->putPhoneIdAndSubIdExtra(Landroid/content/Intent;II)V
+Landroid/telephony/SubscriptionManager;->putPhoneIdAndSubIdExtra(Landroid/content/Intent;I)V
+Landroid/telephony/SubscriptionManager;->setDefaultDataSubId(I)V
+Landroid/telephony/SubscriptionManager;->setDisplayName(Ljava/lang/String;IJ)I
+Landroid/telephony/SubscriptionManager;->setIconTint(II)I
 Landroid/telephony/TelephonyManager;->getIntAtIndex(Landroid/content/ContentResolver;Ljava/lang/String;I)I
+Landroid/telephony/TelephonyManager;->getNetworkTypeName()Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getVoiceMessageCount()I
 Landroid/telephony/TelephonyManager;->getVoiceNetworkType(I)I
+Landroid/telephony/TelephonyManager$MultiSimVariants;->DSDA:Landroid/telephony/TelephonyManager$MultiSimVariants;
+Landroid/telephony/TelephonyManager$MultiSimVariants;->DSDS:Landroid/telephony/TelephonyManager$MultiSimVariants;
 Landroid/telephony/TelephonyManager;->putIntAtIndex(Landroid/content/ContentResolver;Ljava/lang/String;II)Z
+Landroid/text/TextUtils;->isPrintableAsciiOnly(Ljava/lang/CharSequence;)Z
 Landroid/util/FloatMath;->ceil(F)F
 Landroid/util/FloatMath;->cos(F)F
 Landroid/util/FloatMath;->exp(F)F
@@ -374,6 +501,9 @@
 Landroid/util/LongArray;->get(I)J
 Landroid/util/LongArray;-><init>()V
 Landroid/util/LongArray;->size()I
+Landroid/util/Slog;->e(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/util/Slog;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
+Landroid/util/Slog;->println(ILjava/lang/String;Ljava/lang/String;)I
 Landroid/util/Slog;->wtf(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/view/AppTransitionAnimationSpec;-><init>(ILandroid/graphics/GraphicBuffer;Landroid/graphics/Rect;)V
 Landroid/view/BatchedInputEventReceiver;-><init>(Landroid/view/InputChannel;Landroid/os/Looper;Landroid/view/Choreographer;)V
@@ -525,6 +655,19 @@
 Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z
 Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z
 Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
+Lcom/android/internal/app/AlertController$AlertParams;->mIconId:I
+Lcom/android/internal/app/AlertController$AlertParams;->mMessage:Ljava/lang/CharSequence;
+Lcom/android/internal/app/AlertController$AlertParams;->mNegativeButtonListener:Landroid/content/DialogInterface$OnClickListener;
+Lcom/android/internal/app/AlertController$AlertParams;->mNegativeButtonText:Ljava/lang/CharSequence;
+Lcom/android/internal/app/AlertController$AlertParams;->mPositiveButtonListener:Landroid/content/DialogInterface$OnClickListener;
+Lcom/android/internal/app/AlertController$AlertParams;->mPositiveButtonText:Ljava/lang/CharSequence;
+Lcom/android/internal/app/AlertController$AlertParams;->mTitle:Ljava/lang/CharSequence;
+Lcom/android/internal/app/AlertController$AlertParams;->mView:Landroid/view/View;
+Lcom/android/internal/app/AlertController;->getButton(I)Landroid/widget/Button;
+Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V
+Lcom/android/internal/content/PackageMonitor;-><init>()V
+Lcom/android/internal/content/PackageMonitor;->register(Landroid/content/Context;Landroid/os/Looper;Landroid/os/UserHandle;Z)V
+Lcom/android/internal/content/PackageMonitor;->unregister()V
 Lcom/android/internal/location/ILocationProvider;->disable()V
 Lcom/android/internal/location/ILocationProvider;->enable()V
 Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
@@ -532,6 +675,11 @@
 Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
 Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
 Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
+Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
+Lcom/android/internal/location/ProviderRequest;-><init>()V
+Lcom/android/internal/location/ProviderRequest;->interval:J
+Lcom/android/internal/location/ProviderRequest;->locationRequests:Ljava/util/List;
+Lcom/android/internal/location/ProviderRequest;->reportLocation:Z
 Lcom/android/internal/os/BatteryStatsImpl;->getDischargeCurrentLevel()I
 Lcom/android/internal/os/BatteryStatsImpl;->getDischargeStartLevel()I
 Lcom/android/internal/os/BatteryStatsImpl;->getPhoneOnTime(JI)J
@@ -541,7 +689,22 @@
 Lcom/android/internal/os/BatteryStatsImpl;->getWifiOnTime(JI)J
 Lcom/android/internal/os/SomeArgs;->obtain()Lcom/android/internal/os/SomeArgs;
 Lcom/android/internal/os/SomeArgs;->recycle()V
+Lcom/android/internal/R$styleable;->NumberPicker:[I
+Lcom/android/internal/R$styleable;->TwoLineListItem:[I
+Lcom/android/internal/telephony/GsmAlphabet;->gsm7BitPackedToString([BII)Ljava/lang/String;
+Lcom/android/internal/telephony/GsmAlphabet;->stringToGsm7BitPacked(Ljava/lang/String;)[B
 Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
+Lcom/android/internal/telephony/OperatorInfo;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/telephony/OperatorInfo;->getOperatorAlphaLong()Ljava/lang/String;
+Lcom/android/internal/telephony/OperatorInfo;->getOperatorAlphaShort()Ljava/lang/String;
+Lcom/android/internal/telephony/OperatorInfo;->getOperatorNumeric()Ljava/lang/String;
+Lcom/android/internal/telephony/OperatorInfo;->getState()Lcom/android/internal/telephony/OperatorInfo$State;
+Lcom/android/internal/telephony/OperatorInfo;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/OperatorInfo$State;->CURRENT:Lcom/android/internal/telephony/OperatorInfo$State;
+Lcom/android/internal/telephony/OperatorInfo$State;->FORBIDDEN:Lcom/android/internal/telephony/OperatorInfo$State;
+Lcom/android/internal/util/AsyncChannel;->connect(Landroid/content/Context;Landroid/os/Handler;Landroid/os/Messenger;)V
+Lcom/android/internal/util/AsyncChannel;-><init>()V
+Lcom/android/internal/util/AsyncChannel;->sendMessage(Landroid/os/Message;)V
 Lcom/android/internal/util/IndentingPrintWriter;->decreaseIndent()Lcom/android/internal/util/IndentingPrintWriter;
 Lcom/android/internal/util/IndentingPrintWriter;->increaseIndent()Lcom/android/internal/util/IndentingPrintWriter;
 Lcom/android/internal/util/IndentingPrintWriter;-><init>(Ljava/io/Writer;Ljava/lang/String;)V
@@ -550,3 +713,4 @@
 Ljava/lang/System;->arraycopy([BI[BII)V
 Ljava/net/Inet4Address;->ALL:Ljava/net/InetAddress;
 Ljava/net/Inet4Address;->ANY:Ljava/net/InetAddress;
+Ljava/net/Inet6Address;->ANY:Ljava/net/InetAddress;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 0b10a35..452225c 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,8 @@
 
 package android.accessibilityservice;
 
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
 import android.annotation.IntDef;
 import android.content.ComponentName;
 import android.content.Context;
@@ -50,8 +52,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
 /**
  * This class describes an {@link AccessibilityService}. The system notifies an
  * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
@@ -410,6 +410,15 @@
     public int flags;
 
     /**
+     * Whether or not the service has crashed and is awaiting restart. Only valid from {@link
+     * android.view.accessibility.AccessibilityManager#getEnabledAccessibilityServiceList(int)},
+     * because that is populated from the internal list of running services.
+     *
+     * @hide
+     */
+    public boolean crashed;
+
+    /**
      * The component name the accessibility service.
      */
     private ComponentName mComponentName;
@@ -757,6 +766,7 @@
         parcel.writeInt(feedbackType);
         parcel.writeLong(notificationTimeout);
         parcel.writeInt(flags);
+        parcel.writeInt(crashed ? 1 : 0);
         parcel.writeParcelable(mComponentName, flagz);
         parcel.writeParcelable(mResolveInfo, 0);
         parcel.writeString(mSettingsActivityName);
@@ -773,6 +783,7 @@
         feedbackType = parcel.readInt();
         notificationTimeout = parcel.readLong();
         flags = parcel.readInt();
+        crashed = parcel.readInt() != 0;
         mComponentName = parcel.readParcelable(this.getClass().getClassLoader());
         mResolveInfo = parcel.readParcelable(null);
         mSettingsActivityName = parcel.readString();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 459a587..1d069bc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -30,12 +30,15 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
+import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
+import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PendingTransactionActions;
 import android.app.servertransaction.PendingTransactionActions.StopInfo;
 import android.app.servertransaction.TransactionExecutor;
+import android.app.servertransaction.TransactionExecutorHelper;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -520,6 +523,10 @@
             return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
         }
 
+        public boolean isVisibleFromServer() {
+            return activity != null && activity.mVisibleFromServer;
+        }
+
         public String toString() {
             ComponentName componentName = intent != null ? intent.getComponent() : null;
             return "ActivityRecord{"
@@ -1797,6 +1804,7 @@
                         // message is handled.
                         transaction.recycle();
                     }
+                    // TODO(lifecycler): Recycle locally scheduled transactions.
                     break;
             }
             Object obj = msg.obj;
@@ -4034,9 +4042,11 @@
         r.setState(ON_PAUSE);
     }
 
+    /** Called from {@link LocalActivityManager}. */
     final void performStopActivity(IBinder token, boolean saveState, String reason) {
         ActivityClientRecord r = mActivities.get(token);
-        performStopActivityInner(r, null, false, saveState, reason);
+        performStopActivityInner(r, null /* stopInfo */, false /* keepShown */, saveState,
+                false /* finalStateRequest */, reason);
     }
 
     private static final class ProviderRefCount {
@@ -4068,9 +4078,16 @@
      * it the result when it is done, but the window may still be visible.
      * For the client, we want to call onStop()/onStart() to indicate when
      * the activity's UI visibility changes.
+     * @param r Target activity client record.
+     * @param info Action that will report activity stop to server.
+     * @param keepShown Flag indicating whether the activity is still shown.
+     * @param saveState Flag indicating whether the activity state should be saved.
+     * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
+     *                          request for a transaction.
+     * @param reason Reason for performing this operation.
      */
     private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
-            boolean saveState, String reason) {
+            boolean saveState, boolean finalStateRequest, String reason) {
         if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
         if (r != null) {
             if (!keepShown && r.stopped) {
@@ -4080,11 +4097,13 @@
                     // if the activity isn't resumed.
                     return;
                 }
-                RuntimeException e = new RuntimeException(
-                        "Performing stop of activity that is already stopped: "
-                        + r.intent.getComponent().toShortString());
-                Slog.e(TAG, e.getMessage(), e);
-                Slog.e(TAG, r.getStateString());
+                if (!finalStateRequest) {
+                    final RuntimeException e = new RuntimeException(
+                            "Performing stop of activity that is already stopped: "
+                                    + r.intent.getComponent().toShortString());
+                    Slog.e(TAG, e.getMessage(), e);
+                    Slog.e(TAG, r.getStateString());
+                }
             }
 
             // One must first be paused before stopped...
@@ -4177,12 +4196,13 @@
 
     @Override
     public void handleStopActivity(IBinder token, boolean show, int configChanges,
-            PendingTransactionActions pendingActions, String reason) {
+            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
         final ActivityClientRecord r = mActivities.get(token);
         r.activity.mConfigChangeFlags |= configChanges;
 
         final StopInfo stopInfo = new StopInfo();
-        performStopActivityInner(r, stopInfo, show, true, reason);
+        performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
+                reason);
 
         if (localLOGV) Slog.v(
             TAG, "Finishing stop of " + r + ": show=" + show
@@ -4234,7 +4254,8 @@
         }
 
         if (!show && !r.stopped) {
-            performStopActivityInner(r, null, show, false, "handleWindowVisibility");
+            performStopActivityInner(r, null /* stopInfo */, show, false /* saveState */,
+                    false /* finalStateRequest */, "handleWindowVisibility");
         } else if (show && r.stopped) {
             // If we are getting ready to gc after going to the background, well
             // we are back active so skip it.
@@ -4710,14 +4731,25 @@
             return;
         }
 
-        // TODO(b/73747058): Investigate converting this to use transaction to relaunch.
-        handleRelaunchActivityInner(r, 0 /* configChanges */, null /* pendingResults */,
-                null /* pendingIntents */, null /* pendingActions */, prevState != ON_RESUME,
-                r.overrideConfig, "handleRelaunchActivityLocally");
 
-        // Restore back to the previous state before relaunch if needed.
-        if (prevState != r.getLifecycleState()) {
-            mTransactionExecutor.cycleToPath(r, prevState);
+        // Initialize a relaunch request.
+        final MergedConfiguration mergedConfiguration = new MergedConfiguration(
+                r.createdConfig != null ? r.createdConfig : mConfiguration,
+                r.overrideConfig);
+        final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
+                null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */,
+                mergedConfiguration, r.mPreserveWindow);
+        // Make sure to match the existing lifecycle state in the end of the transaction.
+        final ActivityLifecycleItem lifecycleRequest =
+                TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
+        // Schedule the transaction.
+        final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+        transaction.addCallback(activityRelaunchItem);
+        transaction.setLifecycleStateRequest(lifecycleRequest);
+        try {
+            mAppThread.scheduleTransaction(transaction);
+        } catch (RemoteException e) {
+            // Local scheduling
         }
     }
 
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 5d0143a..7032a2f 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -27,6 +27,7 @@
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.MotionEvent;
@@ -308,8 +309,14 @@
             return;
         }
 
-        mInputForwarder = InputManager.getInstance().createInputForwarder(
-                mVirtualDisplay.getDisplay().getDisplayId());
+        final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
+        final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+        try {
+            wm.dontOverrideDisplayInfo(displayId);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+        mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
         mTaskStackListener = new TaskBackgroundChangeListener();
         try {
             mActivityManager.registerTaskStackListener(mTaskStackListener);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 21fb18a..b8c4ef7 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
+import android.annotation.UserIdInt;
 import android.annotation.XmlRes;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -1031,19 +1032,25 @@
     }
 
     @Override
-    public ResolveInfo resolveService(Intent intent, int flags) {
+    public ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags,
+            @UserIdInt int userId) {
         try {
             return mPM.resolveService(
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                 flags,
-                mContext.getUserId());
+                userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     @Override
+    public ResolveInfo resolveService(Intent intent, int flags) {
+        return resolveServiceAsUser(intent, flags, mContext.getUserId());
+    }
+
+    @Override
     @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
         try {
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 206495d..961bca2 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -78,9 +78,19 @@
     public abstract void handleResumeActivity(IBinder token, boolean finalStateRequest,
             boolean isForward, String reason);
 
-    /** Stop the activity. */
+    /**
+     * Stop the activity.
+     * @param token Target activity token.
+     * @param show Flag indicating whether activity is still shown.
+     * @param configChanges Activity configuration changes.
+     * @param pendingActions Pending actions to be used on this or later stages of activity
+     *                       transaction.
+     * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
+     *                          request for a transaction.
+     * @param reason Reason for performing this operation.
+     */
     public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
-            PendingTransactionActions pendingActions, String reason);
+            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
 
     /** Report that activity was stopped to server. */
     public abstract void reportStop(PendingTransactionActions pendingActions);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2ac3e23..c6568e1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -89,6 +89,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -2599,6 +2600,80 @@
     };
 
     /**
+     * @hide
+     */
+    public static boolean areActionsVisiblyDifferent(Notification first, Notification second) {
+        Notification.Action[] firstAs = first.actions;
+        Notification.Action[] secondAs = second.actions;
+        if (firstAs == null && secondAs != null || firstAs != null && secondAs == null) {
+            return true;
+        }
+        if (firstAs != null && secondAs != null) {
+            if (firstAs.length != secondAs.length) {
+                return true;
+            }
+            for (int i = 0; i < firstAs.length; i++) {
+                if (!Objects.equals(firstAs[i].title, secondAs[i].title)) {
+                    return true;
+                }
+                RemoteInput[] firstRs = firstAs[i].getRemoteInputs();
+                RemoteInput[] secondRs = secondAs[i].getRemoteInputs();
+                if (firstRs == null) {
+                    firstRs = new RemoteInput[0];
+                }
+                if (secondRs == null) {
+                    secondRs = new RemoteInput[0];
+                }
+                if (firstRs.length != secondRs.length) {
+                    return true;
+                }
+                for (int j = 0; j < firstRs.length; j++) {
+                    if (!Objects.equals(firstRs[j].getLabel(), secondRs[j].getLabel())) {
+                        return true;
+                    }
+                    CharSequence[] firstCs = firstRs[i].getChoices();
+                    CharSequence[] secondCs = secondRs[i].getChoices();
+                    if (firstCs == null) {
+                        firstCs = new CharSequence[0];
+                    }
+                    if (secondCs == null) {
+                        secondCs = new CharSequence[0];
+                    }
+                    if (firstCs.length != secondCs.length) {
+                        return true;
+                    }
+                    for (int k = 0; k < firstCs.length; k++) {
+                        if (!Objects.equals(firstCs[k], secondCs[k])) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean areStyledNotificationsVisiblyDifferent(Builder first, Builder second) {
+        if (first.getStyle() == null) {
+            return second.getStyle() != null;
+        }
+        if (second.getStyle() == null) {
+            return true;
+        }
+        return first.getStyle().areNotificationsVisiblyDifferent(second.getStyle());
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean areRemoteViewsChanged(Builder first, Builder second) {
+        return !first.usesStandardHeader() || !second.usesStandardHeader();
+    }
+
+    /**
      * Parcelling creates multiple copies of objects in {@code extras}. Fix them.
      * <p>
      * For backwards compatibility {@code extras} holds some references to "real" member data such
@@ -4039,6 +4114,13 @@
         }
 
         /**
+         * Returns the style set by {@link #setStyle(Style)}.
+         */
+        public Style getStyle() {
+            return mStyle;
+        }
+
+        /**
          * Specify the value of {@link #visibility}.
          *
          * @return The same Builder.
@@ -5495,6 +5577,24 @@
         public void setRebuildStyledRemoteViews(boolean rebuild) {
             mRebuildStyledRemoteViews = rebuild;
         }
+
+        /**
+         * Get the text that should be displayed in the statusBar when heads upped. This is
+         * usually just the app name, but may be different depending on the style.
+         *
+         * @param publicMode If true, return a text that is safe to display in public.
+         *
+         * @hide
+         */
+        public CharSequence getHeadsUpStatusBarText(boolean publicMode) {
+            if (mStyle != null && !publicMode) {
+                CharSequence text = mStyle.getHeadsUpStatusBarText();
+                if (!TextUtils.isEmpty(text)) {
+                    return text;
+                }
+            }
+            return loadHeaderAppName();
+        }
     }
 
     /**
@@ -5867,6 +5967,21 @@
          */
         public void validate(Context context) {
         }
+
+        /**
+         * @hide
+         */
+        public abstract boolean areNotificationsVisiblyDifferent(Style other);
+        
+        /**
+         * @return the the text that should be displayed in the statusBar when heads-upped.
+         * If {@code null} is returned, the default implementation will be used.
+         *
+         * @hide
+         */
+        public CharSequence getHeadsUpStatusBarText() {
+            return null;
+        }
     }
 
     /**
@@ -5920,6 +6035,13 @@
         }
 
         /**
+         * @hide
+         */
+        public Bitmap getBigPicture() {
+            return mPicture;
+        }
+
+        /**
          * Provide the bitmap to be used as the payload for the BigPicture notification.
          */
         public BigPictureStyle bigPicture(Bitmap b) {
@@ -6059,6 +6181,18 @@
         public boolean hasSummaryInHeader() {
             return false;
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            BigPictureStyle otherS = (BigPictureStyle) other;
+            return !Objects.equals(getBigPicture(), otherS.getBigPicture());
+        }
     }
 
     /**
@@ -6122,6 +6256,13 @@
         /**
          * @hide
          */
+        public CharSequence getBigText() {
+            return mBigText;
+        }
+
+        /**
+         * @hide
+         */
         public void addExtras(Bundle extras) {
             super.addExtras(extras);
 
@@ -6191,6 +6332,18 @@
             return contentView;
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            BigTextStyle newS = (BigTextStyle) other;
+            return !Objects.equals(getBigText(), newS.getBigText());
+        }
+
         static void applyBigTextContentView(Builder builder,
                 RemoteViews contentView, CharSequence bigTextText) {
             contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
@@ -6278,6 +6431,23 @@
         }
 
         /**
+         * @return the the text that should be displayed in the statusBar when heads upped.
+         * If {@code null} is returned, the default implementation will be used.
+         *
+         * @hide
+         */
+        @Override
+        public CharSequence getHeadsUpStatusBarText() {
+            CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
+                    ? super.mBigContentTitle
+                    : mConversationTitle;
+            if (!TextUtils.isEmpty(conversationTitle) && !hasOnlyWhiteSpaceSenders()) {
+                return conversationTitle;
+            }
+            return null;
+        }
+
+        /**
          * @return the user to be displayed for any replies sent by the user
          */
         public Person getUser() {
@@ -6533,6 +6703,58 @@
             return remoteViews;
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            MessagingStyle newS = (MessagingStyle) other;
+            List<MessagingStyle.Message> oldMs = getMessages();
+            List<MessagingStyle.Message> newMs = newS.getMessages();
+
+            if (oldMs == null) {
+                oldMs = new ArrayList<>();
+            }
+            if (newMs == null) {
+                newMs = new ArrayList<>();
+            }
+
+            int n = oldMs.size();
+            if (n != newMs.size()) {
+                return true;
+            }
+            for (int i = 0; i < n; i++) {
+                MessagingStyle.Message oldM = oldMs.get(i);
+                MessagingStyle.Message newM = newMs.get(i);
+                if (!Objects.equals(oldM.getText(), newM.getText())) {
+                    return true;
+                }
+                if (!Objects.equals(oldM.getDataUri(), newM.getDataUri())) {
+                    return true;
+                }
+                CharSequence oldSender = oldM.getSenderPerson() == null ? oldM.getSender()
+                        : oldM.getSenderPerson().getName();
+                CharSequence newSender = newM.getSenderPerson() == null ? newM.getSender()
+                        : newM.getSenderPerson().getName();
+                if (!Objects.equals(oldSender, newSender)) {
+                    return true;
+                }
+
+                String oldKey = oldM.getSenderPerson() == null
+                        ? null : oldM.getSenderPerson().getKey();
+                String newKey = newM.getSenderPerson() == null
+                        ? null : newM.getSenderPerson().getKey();
+                if (!Objects.equals(oldKey, newKey)) {
+                    return true;
+                }
+                // Other fields (like timestamp) intentionally excluded
+            }
+            return false;
+        }
+
         private Message findLatestIncomingMessage() {
             return findLatestIncomingMessage(mMessages);
         }
@@ -6964,6 +7186,13 @@
         /**
          * @hide
          */
+        public ArrayList<CharSequence> getLines() {
+            return mTexts;
+        }
+
+        /**
+         * @hide
+         */
         public void addExtras(Bundle extras) {
             super.addExtras(extras);
 
@@ -7042,6 +7271,18 @@
             return contentView;
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            InboxStyle newS = (InboxStyle) other;
+            return !Objects.equals(getLines(), newS.getLines());
+        }
+
         private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
             int endMargin = 0;
             if (first) {
@@ -7205,6 +7446,18 @@
             }
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            // All fields to compare are on the Notification object
+            return false;
+        }
+
         private RemoteViews generateMediaActionButton(Action action, int color) {
             final boolean tombstone = (action.actionIntent == null);
             RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
@@ -7414,6 +7667,18 @@
             }
             remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            // Comparison done for all custom RemoteViews, independent of style
+            return false;
+        }
     }
 
     /**
@@ -7504,6 +7769,18 @@
             return makeBigContentViewWithCustomContent(customRemoteView);
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            // Comparison done for all custom RemoteViews, independent of style
+            return false;
+        }
+
         private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
                 RemoteViews customContent) {
             if (customContent != null) {
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index 0a61fab..8db38d3 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -39,7 +39,7 @@
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
         client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions,
-                "STOP_ACTIVITY_ITEM");
+                true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 0d995e8..553c3ae 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -204,7 +204,8 @@
                     break;
                 case ON_STOP:
                     mTransactionHandler.handleStopActivity(r.token, false /* show */,
-                            0 /* configChanges */, mPendingActions, "LIFECYCLER_STOP_ACTIVITY");
+                            0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
+                            "LIFECYCLER_STOP_ACTIVITY");
                     break;
                 case ON_DESTROY:
                     mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 7e66fd7..01b13a2 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -26,7 +26,7 @@
 import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
 
-import android.app.ActivityThread;
+import android.app.ActivityThread.ActivityClientRecord;
 import android.util.IntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -124,7 +124,7 @@
      *         {@link ActivityLifecycleItem#UNDEFINED} if there is not path.
      */
     @VisibleForTesting
-    public int getClosestPreExecutionState(ActivityThread.ActivityClientRecord r,
+    public int getClosestPreExecutionState(ActivityClientRecord r,
             int postExecutionState) {
         switch (postExecutionState) {
             case UNDEFINED:
@@ -147,7 +147,7 @@
      *         were provided or there is not path.
      */
     @VisibleForTesting
-    public int getClosestOfStates(ActivityThread.ActivityClientRecord r, int[] finalStates) {
+    public int getClosestOfStates(ActivityClientRecord r, int[] finalStates) {
         if (finalStates == null || finalStates.length == 0) {
             return UNDEFINED;
         }
@@ -168,6 +168,27 @@
         return closestState;
     }
 
+    /** Get the lifecycle state request to match the current state in the end of a transaction. */
+    public static ActivityLifecycleItem getLifecycleRequestForCurrentState(ActivityClientRecord r) {
+        final int prevState = r.getLifecycleState();
+        final ActivityLifecycleItem lifecycleItem;
+        switch (prevState) {
+            // TODO(lifecycler): Extend to support all possible states.
+            case ON_PAUSE:
+                lifecycleItem = PauseActivityItem.obtain();
+                break;
+            case ON_STOP:
+                lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(),
+                        0 /* configChanges */);
+                break;
+            default:
+                lifecycleItem = ResumeActivityItem.obtain(false /* isForward */);
+                break;
+        }
+
+        return lifecycleItem;
+    }
+
     /**
      * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence
      * that involves destruction and recreation if there is another path.
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index d6f2352..61679cb 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -66,10 +66,27 @@
             HINT_HORIZONTAL,
             HINT_PARTIAL,
             HINT_SEE_MORE,
-            HINT_KEY_WORDS
+            HINT_KEY_WORDS,
+            HINT_ERROR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SliceHint {}
+    /**
+     * @hide
+     */
+    @StringDef(prefix = { "SUBTYPE_" }, value = {
+            SUBTYPE_COLOR,
+            SUBTYPE_CONTENT_DESCRIPTION,
+            SUBTYPE_MAX,
+            SUBTYPE_MESSAGE,
+            SUBTYPE_PRIORITY,
+            SUBTYPE_RANGE,
+            SUBTYPE_SOURCE,
+            SUBTYPE_TOGGLE,
+            SUBTYPE_VALUE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SliceSubtype {}
 
     /**
      * Hint that this content is a title of other content in the slice. This can also indicate that
@@ -149,9 +166,14 @@
     /**
      * A hint to indicate that the contents of this subslice represent a list of keywords
      * related to the parent slice.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE}.
      */
     public static final String HINT_KEY_WORDS = "key_words";
     /**
+     * A hint to indicate that this slice represents an error.
+     */
+    public static final String HINT_ERROR = "error";
+    /**
      * Key to retrieve an extra added to an intent when a control is changed.
      */
     public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
@@ -168,14 +190,18 @@
     /**
      * Subtype to indicate that this is a message as part of a communication
      * sequence in this slice.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE}.
      */
     public static final String SUBTYPE_MESSAGE = "message";
     /**
      * Subtype to tag the source (i.e. sender) of a {@link #SUBTYPE_MESSAGE}.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_TEXT},
+     * {@link SliceItem#FORMAT_IMAGE} or an {@link SliceItem#FORMAT_SLICE} containing them.
      */
     public static final String SUBTYPE_SOURCE = "source";
     /**
      * Subtype to tag an item as representing a color.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
      */
     public static final String SUBTYPE_COLOR = "color";
     /**
@@ -186,14 +212,18 @@
     public static final String SUBTYPE_SLIDER = "slider";
     /**
      * Subtype to tag an item as representing a range.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE} containing
+     * a {@link #SUBTYPE_VALUE} and possibly a {@link #SUBTYPE_MAX}.
      */
     public static final String SUBTYPE_RANGE = "range";
     /**
      * Subtype to tag an item as representing the max int value for a {@link #SUBTYPE_RANGE}.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
      */
     public static final String SUBTYPE_MAX = "max";
     /**
      * Subtype to tag an item as representing the current int value for a {@link #SUBTYPE_RANGE}.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
      */
     public static final String SUBTYPE_VALUE = "value";
     /**
@@ -205,10 +235,12 @@
     public static final String SUBTYPE_TOGGLE = "toggle";
     /**
      * Subtype to tag an item representing priority.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
      */
     public static final String SUBTYPE_PRIORITY = "priority";
     /**
      * Subtype to tag an item to use as a content description.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_TEXT}.
      */
     public static final String SUBTYPE_CONTENT_DESCRIPTION = "content_description";
 
@@ -305,14 +337,24 @@
         private SliceSpec mSpec;
 
         /**
-         * Create a builder which will construct a {@link Slice} for the Given Uri.
-         * @param uri Uri to tag for this slice.
+         * @deprecated TO BE REMOVED
          */
+        @Deprecated
         public Builder(@NonNull Uri uri) {
             mUri = uri;
         }
 
         /**
+         * Create a builder which will construct a {@link Slice} for the given Uri.
+         * @param uri Uri to tag for this slice.
+         * @param spec the spec for this slice.
+         */
+        public Builder(@NonNull Uri uri, SliceSpec spec) {
+            mUri = uri;
+            mSpec = spec;
+        }
+
+        /**
          * Create a builder for a {@link Slice} that is a sub-slice of the slice
          * being constructed by the provided builder.
          * @param parent The builder constructing the parent slice
@@ -340,20 +382,13 @@
         /**
          * Add hints to the Slice being constructed
          */
-        public Builder addHints(@SliceHint String... hints) {
-            mHints.addAll(Arrays.asList(hints));
+        public Builder addHints(@SliceHint List<String> hints) {
+            mHints.addAll(hints);
             return this;
         }
 
         /**
-         * Add hints to the Slice being constructed
-         */
-        public Builder addHints(@SliceHint List<String> hints) {
-            return addHints(hints.toArray(new String[hints.size()]));
-        }
-
-        /**
-         * Add the spec for this slice.
+         * @deprecated TO BE REMOVED
          */
         public Builder setSpec(SliceSpec spec) {
             mSpec = spec;
@@ -362,17 +397,10 @@
 
         /**
          * Add a sub-slice to the slice being constructed
-         */
-        public Builder addSubSlice(@NonNull Slice slice) {
-            return addSubSlice(slice, null);
-        }
-
-        /**
-         * Add a sub-slice to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addSubSlice(@NonNull Slice slice, @Nullable String subType) {
+        public Builder addSubSlice(@NonNull Slice slice, @Nullable @SliceSubtype String subType) {
             mItems.add(new SliceItem(slice, SliceItem.FORMAT_SLICE, subType,
                     slice.getHints().toArray(new String[slice.getHints().size()])));
             return this;
@@ -380,18 +408,11 @@
 
         /**
          * Add an action to the slice being constructed
-         */
-        public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) {
-            return addAction(action, s, null);
-        }
-
-        /**
-         * Add an action to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
         public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s,
-                @Nullable String subType) {
+                @Nullable @SliceSubtype String subType) {
             List<String> hints = s.getHints();
             s.mSpec = null;
             mItems.add(new SliceItem(action, s, SliceItem.FORMAT_ACTION, subType, hints.toArray(
@@ -404,58 +425,31 @@
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addText(CharSequence text, @Nullable String subType,
-                @SliceHint String... hints) {
+        public Builder addText(CharSequence text, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(text, SliceItem.FORMAT_TEXT, subType, hints));
             return this;
         }
 
         /**
-         * Add text to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Builder addText(CharSequence text, @Nullable String subType,
-                @SliceHint List<String> hints) {
-            return addText(text, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Add an image to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addIcon(Icon icon, @Nullable String subType, @SliceHint String... hints) {
+        public Builder addIcon(Icon icon, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(icon, SliceItem.FORMAT_IMAGE, subType, hints));
             return this;
         }
 
         /**
-         * Add an image to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Builder addIcon(Icon icon, @Nullable String subType, @SliceHint List<String> hints) {
-            return addIcon(icon, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Add remote input to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType,
+        public Slice.Builder addRemoteInput(RemoteInput remoteInput,
+                @Nullable @SliceSubtype String subType,
                 @SliceHint List<String> hints) {
-            return addRemoteInput(remoteInput, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
-         * Add remote input to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType,
-                @SliceHint String... hints) {
             mItems.add(new SliceItem(remoteInput, SliceItem.FORMAT_REMOTE_INPUT,
                     subType, hints));
             return this;
@@ -466,70 +460,48 @@
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addInt(int value, @Nullable String subType, @SliceHint String... hints) {
+        public Builder addInt(int value, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(value, SliceItem.FORMAT_INT, subType, hints));
             return this;
         }
 
         /**
-         * Add an integer to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
+         * @deprecated TO BE REMOVED.
          */
-        public Builder addInt(int value, @Nullable String subType,
+        @Deprecated
+        public Slice.Builder addTimestamp(long time, @Nullable @SliceSubtype String subType,
                 @SliceHint List<String> hints) {
-            return addInt(value, subType, hints.toArray(new String[hints.size()]));
+            return addLong(time, subType, hints);
         }
 
         /**
-         * Add a timestamp to the slice being constructed
+         * Add a long to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addTimestamp(long time, @Nullable String subType,
-                @SliceHint String... hints) {
-            mItems.add(new SliceItem(time, SliceItem.FORMAT_TIMESTAMP, subType,
-                    hints));
+        public Slice.Builder addLong(long value, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
+            mItems.add(new SliceItem(value, SliceItem.FORMAT_LONG, subType,
+                    hints.toArray(new String[hints.size()])));
             return this;
         }
 
         /**
-         * Add a timestamp to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Slice.Builder addTimestamp(long time, @Nullable String subType,
-                @SliceHint List<String> hints) {
-            return addTimestamp(time, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Add a bundle to the slice being constructed.
          * <p>Expected to be used for support library extension, should not be used for general
          * development
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addBundle(Bundle bundle, @Nullable String subType,
-                @SliceHint String... hints) {
+        public Slice.Builder addBundle(Bundle bundle, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(bundle, SliceItem.FORMAT_BUNDLE, subType,
                     hints));
             return this;
         }
 
         /**
-         * Add a bundle to the slice being constructed.
-         * <p>Expected to be used for support library extension, should not be used for general
-         * development
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Slice.Builder addBundle(Bundle bundle, @Nullable String subType,
-                @SliceHint List<String> hints) {
-            return addBundle(bundle, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Construct the slice.
          */
         public Slice build() {
diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java
index 9eb2bb8..019ae49 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -67,7 +67,7 @@
             FORMAT_IMAGE,
             FORMAT_ACTION,
             FORMAT_INT,
-            FORMAT_TIMESTAMP,
+            FORMAT_LONG,
             FORMAT_REMOTE_INPUT,
             FORMAT_BUNDLE,
     })
@@ -98,9 +98,14 @@
      */
     public static final String FORMAT_INT = "int";
     /**
-     * A {@link SliceItem} that contains a timestamp.
+     * A {@link SliceItem} that contains a long.
      */
-    public static final String FORMAT_TIMESTAMP = "timestamp";
+    public static final String FORMAT_LONG = "long";
+    /**
+     * @deprecated TO BE REMOVED
+     */
+    @Deprecated
+    public static final String FORMAT_TIMESTAMP = FORMAT_LONG;
     /**
      * A {@link SliceItem} that contains a {@link RemoteInput}.
      */
@@ -123,6 +128,14 @@
      * @hide
      */
     public SliceItem(Object obj, @SliceType String format, String subType,
+            List<String> hints) {
+        this(obj, format, subType, hints.toArray(new String[hints.size()]));
+    }
+
+    /**
+     * @hide
+     */
+    public SliceItem(Object obj, @SliceType String format, String subType,
             @Slice.SliceHint String[] hints) {
         mHints = hints;
         mFormat = format;
diff --git a/core/java/android/app/slice/SliceMetrics.java b/core/java/android/app/slice/SliceMetrics.java
new file mode 100644
index 0000000..a7069bc
--- /dev/null
+++ b/core/java/android/app/slice/SliceMetrics.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.slice;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.Uri;
+
+import com.android.internal.logging.MetricsLogger;
+
+/**
+ * Metrics interface for slices.
+ *
+ * This is called by SliceView, so Slice develoers should
+ * not need to reference this class.
+ *
+ * @see androidx.slice.widget.SliceView
+ */
+public class SliceMetrics {
+
+    private static final String TAG = "SliceMetrics";
+    private MetricsLogger mMetricsLogger;
+
+    /**
+     * An object to be used throughout the life of a slice to register events.
+     */
+    public SliceMetrics(@NonNull Context context, @NonNull Uri uri) {
+        mMetricsLogger = new MetricsLogger();
+    }
+
+    /**
+     * To be called whenever the slice becomes visible to the user.
+     */
+    public void logVisible() {
+    }
+
+    /**
+     * To be called whenever the slice becomes invisible to the user.
+     */
+    public void logHidden() {
+    }
+
+    /**
+     * To be called whenever the use interacts with a slice.
+     *@param subSlice The URI of the sub-slice that is the subject of the interaction.
+     */
+    public void logTouch(@NonNull Uri subSlice) {
+    }
+}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index aa2cf46..dd89293 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -41,6 +41,7 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -430,9 +431,11 @@
         return new Slice.Builder(sliceUri)
                 .addAction(createPermissionIntent(context, sliceUri, callingPackage),
                         new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
-                                .addText(getPermissionString(context, callingPackage), null)
-                                .build())
-                .addHints(Slice.HINT_LIST_ITEM)
+                                .addText(getPermissionString(context, callingPackage), null,
+                                        Collections.emptyList())
+                                .build(),
+                        null)
+                .addHints(Arrays.asList(Slice.HINT_LIST_ITEM))
                 .build();
     }
 
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 4c7e97b..b354e81 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -81,6 +81,7 @@
          * An event type denoting that a package was interacted with in some way by the system.
          * @hide
          */
+        @SystemApi
         public static final int SYSTEM_INTERACTION = 6;
 
         /**
@@ -124,6 +125,20 @@
         @SystemApi
         public static final int NOTIFICATION_INTERRUPTION = 12;
 
+        /**
+         * A Slice was pinned by the default launcher or the default assistant.
+         * @hide
+         */
+        @SystemApi
+        public static final int SLICE_PINNED_PRIV = 13;
+
+        /**
+         * A Slice was pinned by an app.
+         * @hide
+         */
+        @SystemApi
+        public static final int SLICE_PINNED = 14;
+
         /** @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 59f001c..1c384580 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -107,25 +107,35 @@
     public static final int STANDBY_BUCKET_EXEMPTED = 5;
 
     /**
-     * The app was used very recently, currently in use or likely to be used very soon.
+     * The app was used very recently, currently in use or likely to be used very soon. Standby
+     * bucket values that are &le; {@link #STANDBY_BUCKET_ACTIVE} will not be throttled by the
+     * system while they are in this bucket. Buckets &gt; {@link #STANDBY_BUCKET_ACTIVE} will most
+     * likely be restricted in some way. For instance, jobs and alarms may be deferred.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_ACTIVE = 10;
 
     /**
-     * The app was used recently and/or likely to be used in the next few hours.
+     * The app was used recently and/or likely to be used in the next few hours. Restrictions will
+     * apply to these apps, such as deferral of jobs and alarms.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_WORKING_SET = 20;
 
     /**
      * The app was used in the last few days and/or likely to be used in the next few days.
+     * Restrictions will apply to these apps, such as deferral of jobs and alarms. The delays may be
+     * greater than for apps in higher buckets (lower bucket value). Bucket values &gt;
+     * {@link #STANDBY_BUCKET_FREQUENT} may additionally have network access limited.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_FREQUENT = 30;
 
     /**
      * The app has not be used for several days and/or is unlikely to be used for several days.
+     * Apps in this bucket will have the most restrictions, including network restrictions, except
+     * during certain short periods (at a minimum, once a day) when they are allowed to execute
+     * jobs, access the network, etc.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_RARE = 40;
@@ -393,11 +403,19 @@
     /**
      * Returns the current standby bucket of the calling app. The system determines the standby
      * state of the app based on app usage patterns. Standby buckets determine how much an app will
-     * be restricted from running background tasks such as jobs, alarms and certain PendingIntent
-     * callbacks.
+     * be restricted from running background tasks such as jobs and alarms.
      * <p>Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to
      * {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
      * restrictive. The battery level of the device might also affect the restrictions.
+     * <p>Apps in buckets &le; {@link #STANDBY_BUCKET_ACTIVE} have no standby restrictions imposed.
+     * Apps in buckets &gt; {@link #STANDBY_BUCKET_FREQUENT} may have network access restricted when
+     * running in the background.
+     * <p>The standby state of an app can change at any time either due to a user interaction or a
+     * system interaction or some algorithm determining that the app can be restricted for a period
+     * of time before the user has a need for it.
+     * <p>You can also query the recent history of standby bucket changes by calling
+     * {@link #queryEventsForSelf(long, long)} and searching for
+     * {@link UsageEvents.Event#STANDBY_BUCKET_CHANGED}.
      *
      * @return the current standby bucket of the calling app. One of STANDBY_BUCKET_* constants.
      */
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index f67ec4a..20248b9 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -679,11 +679,13 @@
     }
 
     /**
-     * Updates the info for the supplied AppWidget provider.
+     * Updates the info for the supplied AppWidget provider. Apps can use this to change the default
+     * behavior of the widget based on the state of the app (for e.g., if the user is logged in
+     * or not). Calling this API completely replaces the previous definition.
      *
      * <p>
      * The manifest entry of the provider should contain an additional meta-data tag similar to
-     * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any additional definitions for
+     * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any alternative definitions for
      * the provider.
      *
      * <p>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bd7961f..3536eea 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4183,6 +4183,12 @@
     public abstract ResolveInfo resolveService(Intent intent, @ResolveInfoFlags int flags);
 
     /**
+     * @hide
+     */
+    public abstract ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags,
+            @UserIdInt int userId);
+
+    /**
      * Retrieve all services that can match the given intent.
      *
      * @param intent The desired intent as per resolveService().
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index d5b052e..390b83f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1242,7 +1242,7 @@
      * from the main sensor along the +X axis (to the right from the user's perspective) will
      * report <code>(0.03, 0, 0)</code>.</p>
      * <p>To transform a pixel coordinates between two cameras facing the same direction, first
-     * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for.  Then the source
+     * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for.  Then the source
      * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
      * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera
      * relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination
@@ -1256,10 +1256,10 @@
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      */
     @PublicKey
     public static final Key<float[]> LENS_POSE_TRANSLATION =
@@ -1305,7 +1305,7 @@
      * where <code>(0,0)</code> is the top-left of the
      * preCorrectionActiveArraySize rectangle. Once the pose and
      * intrinsic calibration transforms have been applied to a
-     * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}
+     * world point, then the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}
      * transform needs to be applied, and the result adjusted to
      * be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate
      * system (where <code>(0, 0)</code> is the top-left of the
@@ -1318,9 +1318,9 @@
      * coordinate system.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
@@ -1362,7 +1362,14 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @deprecated
+     * <p>This field was inconsistently defined in terms of its
+     * normalization. Use {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} instead.</p>
+     *
+     * @see CameraCharacteristics#LENS_DISTORTION
+
      */
+    @Deprecated
     @PublicKey
     public static final Key<float[]> LENS_RADIAL_DISTORTION =
             new Key<float[]>("android.lens.radialDistortion", float[].class);
@@ -1387,6 +1394,46 @@
             new Key<Integer>("android.lens.poseReference", int.class);
 
     /**
+     * <p>The correction coefficients to correct for this camera device's
+     * radial and tangential lens distortion.</p>
+     * <p>Replaces the deprecated {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} field, which was
+     * inconsistently defined.</p>
+     * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2,
+     * kappa_3]</code> and two tangential distortion coefficients
+     * <code>[kappa_4, kappa_5]</code> that can be used to correct the
+     * lens's geometric distortion with the mapping equations:</p>
+     * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 )
+     *  y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 )
+     * </code></pre>
+     * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the
+     * input image that correspond to the pixel values in the
+     * corrected image at the coordinate <code>[x_i, y_i]</code>:</p>
+     * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage)
+     * </code></pre>
+     * <p>The pixel coordinates are defined in a coordinate system
+     * related to the {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}
+     * calibration fields; see that entry for details of the mapping stages.
+     * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code>
+     * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and
+     * the range of the coordinates depends on the focal length
+     * terms of the intrinsic calibration.</p>
+     * <p>Finally, <code>r</code> represents the radial distance from the
+     * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p>
+     * <p>The distortion model used is the Brown-Conrady model.</p>
+     * <p><b>Units</b>:
+     * Unitless coefficients.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_DISTORTION =
+            new Key<float[]>("android.lens.distortion", float[].class);
+
+    /**
      * <p>List of noise reduction modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} that are supported
      * by this camera device.</p>
      * <p>Full-capability camera devices will always support OFF and FAST.</p>
@@ -1418,6 +1465,8 @@
      * consideration of future support.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer; replaced by better partials mechanism</p>
+
      * @hide
      */
     @Deprecated
@@ -1808,6 +1857,8 @@
      * <p>When set to YUV_420_888, application can access the YUV420 data directly.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1828,6 +1879,8 @@
      * TODO: Remove property.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1844,6 +1897,8 @@
      *
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1883,6 +1938,8 @@
      * <p><b>Units</b>: Nanoseconds</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1905,6 +1962,8 @@
      * check if it limits the maximum size for image data.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -2544,7 +2603,7 @@
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
      * <p>The currently supported fields that correct for geometric distortion are:</p>
      * <ol>
-     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}.</li>
+     * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}.</li>
      * </ol>
      * <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same
      * as the post-distortion-corrected rectangle given in
@@ -2557,7 +2616,7 @@
      * <p><b>Units</b>: Pixel coordinates on the image sensor</p>
      * <p>This key is available on all devices.</p>
      *
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 14c2865..7669c01 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -684,7 +684,7 @@
      * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
      * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
      * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
-     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+     * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li>
      * </ul>
      * </li>
      * <li>The {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} entry is listed by this device.</li>
@@ -702,12 +702,12 @@
      * rate, including depth stall time.</p>
      *
      * @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_FACING
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8;
@@ -826,7 +826,7 @@
      * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
      * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
      * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
-     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+     * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li>
      * </ul>
      * </li>
      * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
@@ -852,11 +852,11 @@
      * not slow down the frame rate of the capture, as long as the minimum frame duration
      * of the physical and logical streams are the same.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index d7c5564..b0cbec7 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2791,8 +2791,10 @@
      *   <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
      * </ul></p>
      * <p><b>Available values for this device:</b><br>
-     * android.Statistics.info.availableOisDataModes</p>
+     * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES android.statistics.info.availableOisDataModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES
      * @see #STATISTICS_OIS_DATA_MODE_OFF
      * @see #STATISTICS_OIS_DATA_MODE_ON
      */
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index e84e48f..6331942 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2783,7 +2783,7 @@
      * from the main sensor along the +X axis (to the right from the user's perspective) will
      * report <code>(0.03, 0, 0)</code>.</p>
      * <p>To transform a pixel coordinates between two cameras facing the same direction, first
-     * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for.  Then the source
+     * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for.  Then the source
      * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
      * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera
      * relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination
@@ -2797,10 +2797,10 @@
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      */
     @PublicKey
     public static final Key<float[]> LENS_POSE_TRANSLATION =
@@ -2846,7 +2846,7 @@
      * where <code>(0,0)</code> is the top-left of the
      * preCorrectionActiveArraySize rectangle. Once the pose and
      * intrinsic calibration transforms have been applied to a
-     * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}
+     * world point, then the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}
      * transform needs to be applied, and the result adjusted to
      * be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate
      * system (where <code>(0, 0)</code> is the top-left of the
@@ -2859,9 +2859,9 @@
      * coordinate system.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
@@ -2903,12 +2903,59 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @deprecated
+     * <p>This field was inconsistently defined in terms of its
+     * normalization. Use {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} instead.</p>
+     *
+     * @see CameraCharacteristics#LENS_DISTORTION
+
      */
+    @Deprecated
     @PublicKey
     public static final Key<float[]> LENS_RADIAL_DISTORTION =
             new Key<float[]>("android.lens.radialDistortion", float[].class);
 
     /**
+     * <p>The correction coefficients to correct for this camera device's
+     * radial and tangential lens distortion.</p>
+     * <p>Replaces the deprecated {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} field, which was
+     * inconsistently defined.</p>
+     * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2,
+     * kappa_3]</code> and two tangential distortion coefficients
+     * <code>[kappa_4, kappa_5]</code> that can be used to correct the
+     * lens's geometric distortion with the mapping equations:</p>
+     * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 )
+     *  y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 )
+     * </code></pre>
+     * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the
+     * input image that correspond to the pixel values in the
+     * corrected image at the coordinate <code>[x_i, y_i]</code>:</p>
+     * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage)
+     * </code></pre>
+     * <p>The pixel coordinates are defined in a coordinate system
+     * related to the {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}
+     * calibration fields; see that entry for details of the mapping stages.
+     * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code>
+     * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and
+     * the range of the coordinates depends on the focal length
+     * terms of the intrinsic calibration.</p>
+     * <p>Finally, <code>r</code> represents the radial distance from the
+     * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p>
+     * <p>The distortion model used is the Brown-Conrady model.</p>
+     * <p><b>Units</b>:
+     * Unitless coefficients.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_DISTORTION =
+            new Key<float[]>("android.lens.distortion", float[].class);
+
+    /**
      * <p>Mode of operation for the noise reduction algorithm.</p>
      * <p>The noise reduction algorithm attempts to improve image quality by removing
      * excessive noise added by the capture process, especially in dark conditions.</p>
@@ -2981,6 +3028,8 @@
      * Optional. Default value is FINAL.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -2997,6 +3046,8 @@
      * &gt; 0</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -3777,6 +3828,8 @@
      *
      * @see CaptureRequest#COLOR_CORRECTION_GAINS
      * @deprecated
+     * <p>Never fully implemented or specified; do not use</p>
+
      * @hide
      */
     @Deprecated
@@ -3801,6 +3854,8 @@
      * regardless of the android.control.* current values.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Never fully implemented or specified; do not use</p>
+
      * @hide
      */
     @Deprecated
@@ -3919,8 +3974,10 @@
      *   <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
      * </ul></p>
      * <p><b>Available values for this device:</b><br>
-     * android.Statistics.info.availableOisDataModes</p>
+     * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES android.statistics.info.availableOisDataModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES
      * @see #STATISTICS_OIS_DATA_MODE_OFF
      * @see #STATISTICS_OIS_DATA_MODE_ON
      */
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index e81fbee..a3b2d22 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -668,6 +668,45 @@
     }
 
     /**
+     * Gets the global display brightness configuration or the default curve if one hasn't been set.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+    public BrightnessConfiguration getBrightnessConfiguration() {
+        return getBrightnessConfigurationForUser(mContext.getUserId());
+    }
+
+    /**
+     * Gets the global display brightness configuration or the default curve if one hasn't been set
+     * for a specific user.
+     *
+     * Note this requires the INTERACT_ACROSS_USERS permission if getting the configuration for a
+     * user other than the one you're currently running as.
+     *
+     * @hide
+     */
+    public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+        return mGlobal.getBrightnessConfigurationForUser(userId);
+    }
+
+    /**
+     * Gets the default global display brightness configuration or null one hasn't
+     * been configured.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+    @Nullable
+    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+        return mGlobal.getDefaultBrightnessConfiguration();
+    }
+
+    /**
      * Temporarily sets the brightness of the display.
      * <p>
      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index d7f7c86..1f67a6b 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -490,6 +490,32 @@
     }
 
     /**
+     * Gets the global brightness configuration for a given user or null if one hasn't been set.
+     *
+     * @hide
+     */
+    public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+        try {
+            return mDm.getBrightnessConfigurationForUser(userId);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the default brightness configuration or null if one hasn't been configured.
+     *
+     * @hide
+     */
+    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+        try {
+            return mDm.getDefaultBrightnessConfiguration();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Temporarily sets the brightness of the display.
      * <p>
      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 0571ae1..9fcb9d3 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -96,6 +96,14 @@
     void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId,
             String packageName);
 
+    // Gets the global brightness configuration for a given user. Requires
+    // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user is not
+    // the same as the calling user.
+    BrightnessConfiguration getBrightnessConfigurationForUser(int userId);
+
+    // Gets the default brightness configuration if configured.
+    BrightnessConfiguration getDefaultBrightnessConfiguration();
+
     // Temporarily sets the display brightness.
     void setTemporaryBrightness(int brightness);
 
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index b635088..dde8a33 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -16,6 +16,14 @@
 
 package android.hardware.soundtrigger;
 
+import static android.system.OsConstants.EINVAL;
+import static android.system.OsConstants.ENODEV;
+import static android.system.OsConstants.ENOSYS;
+import static android.system.OsConstants.EPERM;
+import static android.system.OsConstants.EPIPE;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.media.AudioFormat;
 import android.os.Handler;
 import android.os.Parcel;
@@ -25,22 +33,33 @@
 import java.util.Arrays;
 import java.util.UUID;
 
-import static android.system.OsConstants.*;
-
 /**
  * The SoundTrigger class provides access via JNI to the native service managing
  * the sound trigger HAL.
  *
  * @hide
  */
+@SystemApi
 public class SoundTrigger {
 
+    private SoundTrigger() {
+    }
+
+    /**
+     * Status code used when the operation succeeded
+     */
     public static final int STATUS_OK = 0;
+    /** @hide */
     public static final int STATUS_ERROR = Integer.MIN_VALUE;
+    /** @hide */
     public static final int STATUS_PERMISSION_DENIED = -EPERM;
+    /** @hide */
     public static final int STATUS_NO_INIT = -ENODEV;
+    /** @hide */
     public static final int STATUS_BAD_VALUE = -EINVAL;
+    /** @hide */
     public static final int STATUS_DEAD_OBJECT = -EPIPE;
+    /** @hide */
     public static final int STATUS_INVALID_OPERATION = -ENOSYS;
 
     /*****************************************************************************
@@ -48,6 +67,8 @@
      * managed by the native sound trigger service. Each module has a unique
      * ID used to target any API call to this paricular module. Module
      * properties are returned by listModules() method.
+     *
+     * @hide
      ****************************************************************************/
     public static class ModuleProperties implements Parcelable {
         /** Unique module ID provided by the native service */
@@ -187,6 +208,8 @@
      * implementation to detect a particular sound pattern.
      * A specialized version {@link KeyphraseSoundModel} is defined for key phrase
      * sound models.
+     *
+     * @hide
      ****************************************************************************/
     public static class SoundModel {
         /** Undefined sound model type */
@@ -261,6 +284,8 @@
     /*****************************************************************************
      * A Keyphrase describes a key phrase that can be detected by a
      * {@link KeyphraseSoundModel}
+     *
+     * @hide
      ****************************************************************************/
     public static class Keyphrase implements Parcelable {
         /** Unique identifier for this keyphrase */
@@ -382,6 +407,8 @@
      * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
      * It contains data needed by the hardware to detect a certain number of key phrases
      * and the list of corresponding {@link Keyphrase} descriptors.
+     *
+     * @hide
      ****************************************************************************/
     public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
         /** Key phrases in this sound model */
@@ -468,6 +495,8 @@
     /*****************************************************************************
      * A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
      * patterns.
+     *
+     * @hide
      ****************************************************************************/
     public static class GenericSoundModel extends SoundModel implements Parcelable {
 
@@ -524,52 +553,115 @@
     /**
      *  Modes for key phrase recognition
      */
-    /** Simple recognition of the key phrase */
+
+    /**
+     * Simple recognition of the key phrase
+     *
+     * @hide
+     */
     public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1;
-    /** Trigger only if one user is identified */
+    /**
+     * Trigger only if one user is identified
+     *
+     * @hide
+     */
     public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2;
-    /** Trigger only if one user is authenticated */
+    /**
+     * Trigger only if one user is authenticated
+     *
+     * @hide
+     */
     public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
 
     /**
      *  Status codes for {@link RecognitionEvent}
      */
-    /** Recognition success */
+    /**
+     * Recognition success
+     *
+     * @hide
+     */
     public static final int RECOGNITION_STATUS_SUCCESS = 0;
-    /** Recognition aborted (e.g. capture preempted by anotehr use case */
+    /**
+     * Recognition aborted (e.g. capture preempted by anotehr use case
+     *
+     * @hide
+     */
     public static final int RECOGNITION_STATUS_ABORT = 1;
-    /** Recognition failure */
+    /**
+     * Recognition failure
+     *
+     * @hide
+     */
     public static final int RECOGNITION_STATUS_FAILURE = 2;
 
     /**
      *  A RecognitionEvent is provided by the
-     *  {@link StatusListener#onRecognition(RecognitionEvent)}
+     *  {@code StatusListener#onRecognition(RecognitionEvent)}
      *  callback upon recognition success or failure.
      */
-    public static class RecognitionEvent implements Parcelable {
-        /** Recognition status e.g {@link #RECOGNITION_STATUS_SUCCESS} */
+    public static class RecognitionEvent {
+        /**
+         * Recognition status e.g RECOGNITION_STATUS_SUCCESS
+         *
+         * @hide
+         */
         public final int status;
-        /** Sound Model corresponding to this event callback */
+        /**
+         *
+         * Sound Model corresponding to this event callback
+         *
+         * @hide
+         */
         public final int soundModelHandle;
-        /** True if it is possible to capture audio from this utterance buffered by the hardware */
+        /**
+         * True if it is possible to capture audio from this utterance buffered by the hardware
+         *
+         * @hide
+         */
         public final boolean captureAvailable;
-        /** Audio session ID to be used when capturing the utterance with an AudioRecord
-         * if captureAvailable() is true. */
+        /**
+         * Audio session ID to be used when capturing the utterance with an AudioRecord
+         * if captureAvailable() is true.
+         *
+         * @hide
+         */
         public final int captureSession;
-        /** Delay in ms between end of model detection and start of audio available for capture.
-         * A negative value is possible (e.g. if keyphrase is also available for capture) */
+        /**
+         * Delay in ms between end of model detection and start of audio available for capture.
+         * A negative value is possible (e.g. if keyphrase is also available for capture)
+         *
+         * @hide
+         */
         public final int captureDelayMs;
-        /** Duration in ms of audio captured before the start of the trigger. 0 if none. */
+        /**
+         * Duration in ms of audio captured before the start of the trigger. 0 if none.
+         *
+         * @hide
+         */
         public final int capturePreambleMs;
-        /** True if  the trigger (key phrase capture is present in binary data */
+        /**
+         * True if  the trigger (key phrase capture is present in binary data
+         *
+         * @hide
+         */
         public final boolean triggerInData;
-        /** Audio format of either the trigger in event data or to use for capture of the
-          * rest of the utterance */
-        public AudioFormat captureFormat;
-        /** Opaque data for use by system applications who know about voice engine internals,
-         * typically during enrollment. */
+        /**
+         * Audio format of either the trigger in event data or to use for capture of the
+         * rest of the utterance
+         *
+         * @hide
+         */
+        public final AudioFormat captureFormat;
+        /**
+         * Opaque data for use by system applications who know about voice engine internals,
+         * typically during enrollment.
+         *
+         * @hide
+         */
         public final byte[] data;
 
+        /** @hide */
         public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                 int captureSession, int captureDelayMs, int capturePreambleMs,
                 boolean triggerInData, AudioFormat captureFormat, byte[] data) {
@@ -584,6 +676,46 @@
             this.data = data;
         }
 
+        /**
+         * Check if is possible to capture audio from this utterance buffered by the hardware.
+         *
+         * @return {@code true} iff a capturing is possible
+         */
+        public boolean isCaptureAvailable() {
+            return captureAvailable;
+        }
+
+        /**
+         * Get the audio format of either the trigger in event data or to use for capture of the
+         * rest of the utterance
+         *
+         * @return the audio format
+         */
+        @Nullable public AudioFormat getCaptureFormat() {
+            return captureFormat;
+        }
+
+        /**
+         * Get Audio session ID to be used when capturing the utterance with an {@link AudioRecord}
+         * if {@link #isCaptureAvailable()} is true.
+         *
+         * @return The id of the capture session
+         */
+        public int getCaptureSession() {
+            return captureSession;
+        }
+
+        /**
+         * Get the opaque data for use by system applications who know about voice engine
+         * internals, typically during enrollment.
+         *
+         * @return The data of the event
+         */
+        public byte[] getData() {
+            return data;
+        }
+
+        /** @hide */
         public static final Parcelable.Creator<RecognitionEvent> CREATOR
                 = new Parcelable.Creator<RecognitionEvent>() {
             public RecognitionEvent createFromParcel(Parcel in) {
@@ -595,6 +727,7 @@
             }
         };
 
+        /** @hide */
         protected static RecognitionEvent fromParcel(Parcel in) {
             int status = in.readInt();
             int soundModelHandle = in.readInt();
@@ -619,12 +752,12 @@
                     captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data);
         }
 
-        @Override
+        /** @hide */
         public int describeContents() {
             return 0;
         }
 
-        @Override
+        /** @hide */
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(status);
             dest.writeInt(soundModelHandle);
@@ -726,6 +859,8 @@
      *  A RecognitionConfig is provided to
      *  {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
      *  recognition request.
+     *
+     *  @hide
      */
     public static class RecognitionConfig implements Parcelable {
         /** True if the DSP should capture the trigger sound and make it available for further
@@ -744,7 +879,7 @@
         public final byte[] data;
 
         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
-                KeyphraseRecognitionExtra keyphrases[], byte[] data) {
+                KeyphraseRecognitionExtra[] keyphrases, byte[] data) {
             this.captureRequested = captureRequested;
             this.allowMultipleTriggers = allowMultipleTriggers;
             this.keyphrases = keyphrases;
@@ -799,6 +934,8 @@
      * When used in a {@link RecognitionConfig} it indicates the minimum confidence level that
      * should trigger a recognition.
      * - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
+     *
+     * @hide
      */
     public static class ConfidenceLevel implements Parcelable {
         public final int userId;
@@ -872,6 +1009,8 @@
     /**
      *  Additional data conveyed by a {@link KeyphraseRecognitionEvent}
      *  for a key phrase detection.
+     *
+     * @hide
      */
     public static class KeyphraseRecognitionExtra implements Parcelable {
         /** The keyphrase ID */
@@ -970,8 +1109,10 @@
 
     /**
      *  Specialized {@link RecognitionEvent} for a key phrase detection.
+     *
+     *  @hide
      */
-    public static class KeyphraseRecognitionEvent extends RecognitionEvent {
+    public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable {
         /** Indicates if the key phrase is present in the buffered audio available for capture */
         public final KeyphraseRecognitionExtra[] keyphraseExtras;
 
@@ -1091,8 +1232,10 @@
     /**
      * Sub-class of RecognitionEvent specifically for sound-trigger based sound
      * models(non-keyphrase). Currently does not contain any additional fields.
+     *
+     * @hide
      */
-    public static class GenericRecognitionEvent extends RecognitionEvent {
+    public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable {
         public GenericRecognitionEvent(int status, int soundModelHandle,
                 boolean captureAvailable, int captureSession, int captureDelayMs,
                 int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat,
@@ -1140,13 +1283,19 @@
     /**
      *  Status codes for {@link SoundModelEvent}
      */
-    /** Sound Model was updated */
+    /**
+     * Sound Model was updated
+     *
+     * @hide
+     */
     public static final int SOUNDMODEL_STATUS_UPDATED = 0;
 
     /**
      *  A SoundModelEvent is provided by the
      *  {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
      *  callback when a sound model has been updated by the implementation
+     *
+     *  @hide
      */
     public static class SoundModelEvent implements Parcelable {
         /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
@@ -1231,9 +1380,17 @@
      *  Native service state. {@link StatusListener#onServiceStateChange(int)}
      */
     // Keep in sync with system/core/include/system/sound_trigger.h
-    /** Sound trigger service is enabled */
+    /**
+     * Sound trigger service is enabled
+     *
+     * @hide
+     */
     public static final int SERVICE_STATE_ENABLED = 0;
-    /** Sound trigger service is disabled */
+    /**
+     * Sound trigger service is disabled
+     *
+     * @hide
+     */
     public static final int SERVICE_STATE_DISABLED = 1;
 
     /**
@@ -1245,6 +1402,8 @@
      *         - {@link #STATUS_NO_INIT} if the native service cannot be reached
      *         - {@link #STATUS_BAD_VALUE} if modules is null
      *         - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
+     *
+     * @hide
      */
     public static native int listModules(ArrayList <ModuleProperties> modules);
 
@@ -1256,6 +1415,8 @@
      * @param handler the Handler that will receive the callabcks. Can be null if default handler
      *                is OK.
      * @return a valid sound module in case of success or null in case of error.
+     *
+     * @hide
      */
     public static SoundTriggerModule attachModule(int moduleId,
                                                   StatusListener listener,
@@ -1270,6 +1431,8 @@
     /**
      * Interface provided by the client application when attaching to a {@link SoundTriggerModule}
      * to received recognition and error notifications.
+     *
+     * @hide
      */
     public static interface StatusListener {
         /**
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 3ce0283..3a3ddcc 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.net.LinkAddress;
 import android.net.Network;
 import android.net.IpSecConfig;
 import android.net.IpSecUdpEncapResponse;
@@ -48,11 +49,11 @@
 
     void addAddressToTunnelInterface(
             int tunnelResourceId,
-            String localAddr);
+            in LinkAddress localAddr);
 
     void removeAddressFromTunnelInterface(
             int tunnelResourceId,
-            String localAddr);
+            in LinkAddress localAddr);
 
     void deleteTunnelInterface(int resourceId);
 
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index c69a4d4..f4b328e 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -38,6 +38,13 @@
     private static final String TAG = "IpSecAlgorithm";
 
     /**
+     * Null cipher.
+     *
+     * @hide
+     */
+    public static final String CRYPT_NULL = "ecb(cipher_null)";
+
+    /**
      * AES-CBC Encryption/Ciphering Algorithm.
      *
      * <p>Valid lengths for this key are {128, 192, 256}.
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index fbf3056..4e1f834 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -656,10 +656,14 @@
          * tunneled traffic.
          *
          * @param address the local address for traffic inside the tunnel
-         * @throws IOException if the address could not be added
          * @hide
          */
-        public void addAddress(LinkAddress address) throws IOException {
+        public void addAddress(LinkAddress address) {
+            try {
+                mService.addAddressToTunnelInterface(mResourceId, address);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         /**
@@ -668,10 +672,14 @@
          * <p>Remove an address which was previously added to the IpSecTunnelInterface
          *
          * @param address to be removed
-         * @throws IOException if the address could not be removed
          * @hide
          */
-        public void removeAddress(LinkAddress address) throws IOException {
+        public void removeAddress(LinkAddress address) {
+            try {
+                mService.removeAddressFromTunnelInterface(mResourceId, address);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         private IpSecTunnelInterface(@NonNull IIpSecService service,
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index b02d48d..c3f23a1 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -225,7 +225,7 @@
      * Indicates the Secure Element on which the transaction occurred.
      * eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC, etc.
      */
-    public static final String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
+    public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
 
     public static final int STATE_OFF = 1;
     public static final int STATE_TURNING_ON = 2;
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 1681f11..13e4e38 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -65,4 +65,7 @@
 
     // sets the attention light (used by phone app only)
     void setAttentionLight(boolean on, int color);
+
+    // controls whether PowerManager should doze after the screen turns off or not
+    void setDozeAfterScreenOff(boolean on);
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 66fa629..c00100b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1280,6 +1280,19 @@
     }
 
     /**
+     * If true, the doze component is not started until after the screen has been
+     * turned off and the screen off animation has been performed.
+     * @hide
+     */
+    public void setDozeAfterScreenOff(boolean dozeAfterScreenOf) {
+        try {
+            mService.setDozeAfterScreenOff(dozeAfterScreenOf);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the reason the phone was last shutdown. Calling app must have the
      * {@link android.Manifest.permission#DEVICE_POWER} permission to request this information.
      * @return Reason for shutdown as an int, {@link #SHUTDOWN_REASON_UNKNOWN} if the file could
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 79b6ea6..b9aad11 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -83,7 +83,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.MemoryIntArray;
-import android.util.StatsLog;
 import android.view.textservice.TextServicesManager;
 
 import com.android.internal.annotations.GuardedBy;
@@ -784,6 +783,21 @@
             "android.settings.APPLICATION_DETAILS_SETTINGS";
 
     /**
+     * Activity Action: Show the "Open by Default" page in a particular application's details page.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
+     * <p>
+     * Input: The Intent's data URI specifies the application package name
+     * to be shown, with the "package" scheme. That is "package:com.my.app".
+     * <p>
+     * Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_APPLICATION_DETAILS_SETTINGS_OPEN_BY_DEFAULT_PAGE =
+            "android.settings.APPLICATION_DETAILS_SETTINGS_OPEN_BY_DEFAULT_PAGE";
+
+    /**
      * Activity Action: Show list of applications that have been running
      * foreground services (to the user "running in the background").
      * <p>
@@ -1942,11 +1956,7 @@
                     arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
                 }
                 IContentProvider cp = mProviderHolder.getProvider(cr);
-                String prevValue = getStringForUser(cr, name, userHandle);
                 cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
-                String newValue = getStringForUser(cr, name, userHandle);
-                StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
-                        makeDefault, userHandle);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
                 return false;
@@ -9541,6 +9551,12 @@
         public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
                 "ble_scan_low_latency_interval_ms";
 
+        /**
+         * The mode that BLE scanning clients will be moved to when in the background.
+         * @hide
+         */
+        public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
+
        /**
         * Used to save the Wifi_ON state prior to tethering.
         * This state will be checked to restore Wifi after
@@ -11591,6 +11607,24 @@
                 "hidden_api_blacklist_exemptions";
 
         /**
+         * Timeout for a single {@link android.media.soundtrigger.SoundTriggerDetectionService}
+         * operation (in ms).
+         *
+         * @hide
+         */
+        public static final String SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT =
+                "sound_trigger_detection_service_op_timeout";
+
+        /**
+         * Maximum number of {@link android.media.soundtrigger.SoundTriggerDetectionService}
+         * operations per day.
+         *
+         * @hide
+         */
+        public static final String MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY =
+                "max_sound_trigger_detection_service_ops_per_day";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
@@ -12326,6 +12360,16 @@
                 "zram_enabled";
 
         /**
+         * Whether we have enable CPU frequency scaling for this device.
+         * For Wear, default is disable.
+         *
+         * The value is "1" for enable, "0" for disable.
+         * @hide
+         */
+        public static final String CPU_SCALING_ENABLED =
+                "cpu_frequency_scaling_enabled";
+
+        /**
          * Configuration flags for smart replies in notifications.
          * This is encoded as a key=value list, separated by commas. Ex:
          *
diff --git a/core/java/android/security/keystore/recovery/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
index ef5e90c..428eaaa 100644
--- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
@@ -37,25 +37,26 @@
 @SystemApi
 public final class KeyDerivationParams implements Parcelable {
     private final int mAlgorithm;
-    private byte[] mSalt;
+    private final byte[] mSalt;
+    private final int mDifficulty;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
+    @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_SCRYPT})
     public @interface KeyDerivationAlgorithm {
     }
 
     /**
-     * Salted SHA256
+     * Salted SHA256.
      */
     public static final int ALGORITHM_SHA256 = 1;
 
     /**
-     * Argon2ID
+     * SCRYPT.
+     *
      * @hide
      */
-    // TODO: add Argon2ID support.
-    public static final int ALGORITHM_ARGON2ID = 2;
+    public static final int ALGORITHM_SCRYPT = 2;
 
     /**
      * Creates instance of the class to to derive key using salted SHA256 hash.
@@ -65,12 +66,30 @@
     }
 
     /**
+     * Creates instance of the class to to derive key using the password hashing algorithm SCRYPT.
+     *
+     * @hide
+     */
+    public static KeyDerivationParams createScryptParams(@NonNull byte[] salt, int difficulty) {
+        return new KeyDerivationParams(ALGORITHM_SCRYPT, salt, difficulty);
+    }
+
+    /**
      * @hide
      */
     // TODO: Make private once legacy API is removed
     public KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
+        this(algorithm, salt, /*difficulty=*/ 0);
+    }
+
+    /**
+     * @hide
+     */
+    KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt,
+            int difficulty) {
         mAlgorithm = algorithm;
         mSalt = Preconditions.checkNotNull(salt);
+        mDifficulty = difficulty;
     }
 
     /**
@@ -87,6 +106,15 @@
         return mSalt;
     }
 
+    /**
+     * Gets hashing difficulty.
+     *
+     * @hide
+     */
+    public int getDifficulty() {
+        return mDifficulty;
+    }
+
     public static final Parcelable.Creator<KeyDerivationParams> CREATOR =
             new Parcelable.Creator<KeyDerivationParams>() {
         public KeyDerivationParams createFromParcel(Parcel in) {
@@ -102,6 +130,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mAlgorithm);
         out.writeByteArray(mSalt);
+        out.writeInt(mDifficulty);
     }
 
     /**
@@ -110,6 +139,7 @@
     protected KeyDerivationParams(Parcel in) {
         mAlgorithm = in.readInt();
         mSalt = in.createByteArray();
+        mDifficulty = in.readInt();
     }
 
     @Override
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 7523afd..10c1c9e 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -547,10 +547,7 @@
             if (grantAlias == null) {
                 throw new InternalRecoveryServiceException("null grant alias");
             }
-            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
-                    mKeyStore,
-                    grantAlias,
-                    KeyStore.UID_SELF);
+            return getKeyFromGrant(grantAlias);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (UnrecoverableKeyException e) {
@@ -581,10 +578,7 @@
             if (grantAlias == null) {
                 throw new InternalRecoveryServiceException("Null grant alias");
             }
-            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
-                    mKeyStore,
-                    grantAlias,
-                    KeyStore.UID_SELF);
+            return getKeyFromGrant(alias);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (UnrecoverableKeyException e) {
@@ -614,10 +608,7 @@
             if (grantAlias == null) {
                 return null;
             }
-            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
-                    mKeyStore,
-                    grantAlias,
-                    KeyStore.UID_SELF);
+            return getKeyFromGrant(grantAlias);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -626,6 +617,16 @@
     }
 
     /**
+     * Returns the key with the given {@code grantAlias}.
+     */
+    Key getKeyFromGrant(String grantAlias) throws UnrecoverableKeyException {
+        return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+                mKeyStore,
+                grantAlias,
+                KeyStore.UID_SELF);
+    }
+
+    /**
      * Removes a key called {@code alias} from the recoverable key store.
      *
      * @param alias The key alias.
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 137dd89..744bfa3 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -16,17 +16,22 @@
 
 package android.security.keystore.recovery;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import java.security.Key;
 import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
 import java.security.cert.CertPath;
 import java.security.cert.CertificateException;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 /**
@@ -136,6 +141,63 @@
             byte[] recoveryClaim =
                     mRecoveryController.getBinder().startRecoverySessionWithCertPath(
                             mSessionId,
+                            /*rootCertificateAlias=*/ "",  // Use the default root cert
+                            recoveryCertPath,
+                            vaultParams,
+                            vaultChallenge,
+                            secrets);
+            return recoveryClaim;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
+                    || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
+                throw new CertificateException(e.getMessage());
+            }
+            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Starts a recovery session and returns a blob with proof of recovery secret possession.
+     * The method generates a symmetric key for a session, which trusted remote device can use to
+     * return recovery key.
+     *
+     * @param rootCertificateAlias The alias of the root certificate that is already in the Android
+     *     OS. The root certificate will be used for validating {@code verifierCertPath}.
+     * @param verifierCertPath The certificate path used to create the recovery blob on the source
+     *     device. Keystore will verify the certificate path by using the root of trust.
+     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
+     *     Used to limit number of guesses.
+     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
+     *     replay attacks.
+     * @param secrets Secrets provided by user, the method only uses type and secret fields.
+     * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
+     *     encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
+     *     key and parameters necessary to identify the counter with the number of failed recovery
+     *     attempts.
+     * @throws CertificateException if the {@code verifierCertPath} is invalid.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    @NonNull public byte[] start(
+            @NonNull String rootCertificateAlias,
+            @NonNull CertPath verifierCertPath,
+            @NonNull byte[] vaultParams,
+            @NonNull byte[] vaultChallenge,
+            @NonNull List<KeyChainProtectionParams> secrets)
+            throws CertificateException, InternalRecoveryServiceException {
+        // Wrap the CertPath in a Parcelable so it can be passed via Binder calls.
+        RecoveryCertPath recoveryCertPath =
+                RecoveryCertPath.createRecoveryCertPath(verifierCertPath);
+        try {
+            byte[] recoveryClaim =
+                    mRecoveryController.getBinder().startRecoverySessionWithCertPath(
+                            mSessionId,
+                            rootCertificateAlias,
                             recoveryCertPath,
                             vaultParams,
                             vaultChallenge,
@@ -187,6 +249,63 @@
     }
 
     /**
+     * Imports key chain snapshot recovered from a remote vault.
+     *
+     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
+     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
+     *     and session.
+     * @throws SessionExpiredException if {@code session} has since been closed.
+     * @throws DecryptionFailedException if unable to decrypt the snapshot.
+     * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE)
+    public Map<String, Key> recoverKeyChainSnapshot(
+            @NonNull byte[] recoveryKeyBlob,
+            @NonNull List<WrappedApplicationKey> applicationKeys
+    ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException {
+        try {
+            Map<String, String> grantAliases = mRecoveryController
+                    .getBinder()
+                    .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys);
+            return getKeysFromGrants(grantAliases);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) {
+                throw new DecryptionFailedException(e.getMessage());
+            }
+            if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) {
+                throw new SessionExpiredException(e.getMessage());
+            }
+            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */
+    private Map<String, Key> getKeysFromGrants(Map<String, String> grantAliases)
+            throws InternalRecoveryServiceException {
+        ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size());
+        for (String alias : grantAliases.keySet()) {
+            String grantAlias = grantAliases.get(alias);
+            Key key;
+            try {
+                key = mRecoveryController.getKeyFromGrant(grantAlias);
+            } catch (UnrecoverableKeyException e) {
+                throw new InternalRecoveryServiceException(
+                        String.format(
+                                Locale.US,
+                                "Failed to get key '%s' from grant '%s'",
+                                alias,
+                                grantAlias), e);
+            }
+            keysByAlias.put(alias, key);
+        }
+        return keysByAlias;
+    }
+
+    /**
      * An internal session ID, used by the framework to match recovery claims to snapshot responses.
      *
      * @hide
diff --git a/core/java/android/security/backup/TrustedRootCertificates.java b/core/java/android/security/keystore/recovery/TrustedRootCertificates.java
similarity index 85%
rename from core/java/android/security/backup/TrustedRootCertificates.java
rename to core/java/android/security/keystore/recovery/TrustedRootCertificates.java
index ed922ed..4bdde8a 100644
--- a/core/java/android/security/backup/TrustedRootCertificates.java
+++ b/core/java/android/security/keystore/recovery/TrustedRootCertificates.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.security.backup;
+package android.security.keystore.recovery;
 
-import static android.security.backup.X509CertificateParsingUtils.decodeBase64Cert;
+import static android.security.keystore.recovery.X509CertificateParsingUtils.decodeBase64Cert;
 
 import android.util.ArrayMap;
 
@@ -77,10 +77,27 @@
 
     private static final int NUMBER_OF_ROOT_CERTIFICATES = 1;
 
+    private static final ArrayMap<String, X509Certificate> ALL_ROOT_CERTIFICATES =
+            constructRootCertificateMap();
+
     /**
      * Returns all available root certificates, keyed by alias.
      */
     public static Map<String, X509Certificate> listRootCertificates() {
+        return new ArrayMap(ALL_ROOT_CERTIFICATES);
+    }
+
+    /**
+     * Gets a root certificate referenced by the given {@code alias}.
+     *
+     * @param alias the alias of the certificate
+     * @return the certificate referenced by the alias, or null if such a certificate doesn't exist.
+     */
+    public static X509Certificate getRootCertificate(String alias) {
+        return ALL_ROOT_CERTIFICATES.get(alias);
+    }
+
+    private static ArrayMap<String, X509Certificate> constructRootCertificateMap() {
         ArrayMap<String, X509Certificate> certificates =
                 new ArrayMap<>(NUMBER_OF_ROOT_CERTIFICATES);
         certificates.put(
diff --git a/core/java/android/security/backup/X509CertificateParsingUtils.java b/core/java/android/security/keystore/recovery/X509CertificateParsingUtils.java
similarity index 98%
rename from core/java/android/security/backup/X509CertificateParsingUtils.java
rename to core/java/android/security/keystore/recovery/X509CertificateParsingUtils.java
index 30495de..fa72c83 100644
--- a/core/java/android/security/backup/X509CertificateParsingUtils.java
+++ b/core/java/android/security/keystore/recovery/X509CertificateParsingUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.security.backup;
+package android.security.keystore.recovery;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index ea10ae7..3726e66 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1418,6 +1418,7 @@
         private ArrayList<SnoozeCriterion> mSnoozeCriteria;
         private boolean mShowBadge;
         private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
+        private boolean mHidden;
 
         public Ranking() {}
 
@@ -1557,6 +1558,16 @@
         }
 
         /**
+         * Returns whether the app that posted this notification is suspended, so this notification
+         * should be hidden.
+         *
+         * @return true if the notification should be hidden, false otherwise.
+         */
+        public boolean isSuspended() {
+            return mHidden;
+        }
+
+        /**
          * @hide
          */
         @VisibleForTesting
@@ -1565,7 +1576,7 @@
                 CharSequence explanation, String overrideGroupKey,
                 NotificationChannel channel, ArrayList<String> overridePeople,
                 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
-                int userSentiment) {
+                int userSentiment, boolean hidden) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1580,6 +1591,7 @@
             mSnoozeCriteria = snoozeCriteria;
             mShowBadge = showBadge;
             mUserSentiment = userSentiment;
+            mHidden = hidden;
         }
 
         /**
@@ -1628,6 +1640,7 @@
         private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
         private ArrayMap<String, Boolean> mShowBadge;
         private ArrayMap<String, Integer> mUserSentiment;
+        private ArrayMap<String, Boolean> mHidden;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1656,7 +1669,7 @@
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
                     getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
                     getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
-                    getShowBadge(key), getUserSentiment(key));
+                    getShowBadge(key), getUserSentiment(key), getHidden(key));
             return rank >= 0;
         }
 
@@ -1784,6 +1797,16 @@
                     ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue();
         }
 
+        private boolean getHidden(String key) {
+            synchronized (this) {
+                if (mHidden == null) {
+                    buildHiddenLocked();
+                }
+            }
+            Boolean hidden = mHidden.get(key);
+            return hidden == null ? false : hidden.booleanValue();
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1892,6 +1915,15 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildHiddenLocked() {
+            Bundle hidden = mRankingUpdate.getHidden();
+            mHidden = new ArrayMap<>(hidden.size());
+            for (String key : hidden.keySet()) {
+                mHidden.put(key, hidden.getBoolean(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 6d51db0..00c47ec 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -36,12 +36,13 @@
     private final Bundle mSnoozeCriteria;
     private final Bundle mShowBadge;
     private final Bundle mUserSentiment;
+    private final Bundle mHidden;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
             int[] importance, Bundle explanation, Bundle overrideGroupKeys,
             Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
-            Bundle showBadge, Bundle userSentiment) {
+            Bundle showBadge, Bundle userSentiment, Bundle hidden) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -54,6 +55,7 @@
         mSnoozeCriteria = snoozeCriteria;
         mShowBadge = showBadge;
         mUserSentiment = userSentiment;
+        mHidden = hidden;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -70,6 +72,7 @@
         mSnoozeCriteria = in.readBundle();
         mShowBadge = in.readBundle();
         mUserSentiment = in.readBundle();
+        mHidden = in.readBundle();
     }
 
     @Override
@@ -91,6 +94,7 @@
         out.writeBundle(mSnoozeCriteria);
         out.writeBundle(mShowBadge);
         out.writeBundle(mUserSentiment);
+        out.writeBundle(mHidden);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -151,4 +155,8 @@
     public Bundle getUserSentiment() {
         return mUserSentiment;
     }
+
+    public Bundle getHidden() {
+        return mHidden;
+    }
 }
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 40e84b9..61277e2 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -545,7 +545,7 @@
      */
     public final void unlockUserWithToken(long handle, byte[] token, UserHandle user) {
         UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
-        if (um.isUserUnlocked()) {
+        if (um.isUserUnlocked(user)) {
             Slog.i(TAG, "User already unlocked");
             return;
         }
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index aafcf44..980f4704 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -675,7 +675,7 @@
     /**
      * This only works if the MeasuredParagraph is computed with buildForStaticLayout.
      */
-    @IntRange(from = 0) int getMemoryUsage() {
+    public @IntRange(from = 0) int getMemoryUsage() {
         return nGetMemoryUsage(mNativePtr);
     }
 
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index b740193..9458184 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -19,7 +19,6 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.util.IntArray;
 
 import com.android.internal.util.Preconditions;
 
@@ -267,6 +266,22 @@
         }
     };
 
+    /** @hide */
+    public static class ParagraphInfo {
+        public final @IntRange(from = 0) int paragraphEnd;
+        public final @NonNull MeasuredParagraph measured;
+
+        /**
+         * @param paraEnd the end offset of this paragraph
+         * @param measured a measured paragraph
+         */
+        public ParagraphInfo(@IntRange(from = 0) int paraEnd, @NonNull MeasuredParagraph measured) {
+            this.paragraphEnd = paraEnd;
+            this.measured = measured;
+        }
+    };
+
+
     // The original text.
     private final @NonNull SpannedString mText;
 
@@ -278,11 +293,8 @@
 
     private final @NonNull Params mParams;
 
-    // The measured paragraph texts.
-    private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
-
-    // The sorted paragraph end offsets.
-    private final @NonNull int[] mParagraphBreakPoints;
+    // The list of measured paragraph info.
+    private final @NonNull ParagraphInfo[] mParagraphInfo;
 
     /**
      * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph
@@ -293,28 +305,25 @@
      * </p>
      *
      * @param text the text to be measured
-     * @param param parameters that define how text will be precomputed
+     * @param params parameters that define how text will be precomputed
      * @return A {@link PrecomputedText}
      */
-    public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params param) {
-        return createInternal(text, param, 0, text.length(), true /* compute full Layout */);
+    public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) {
+        ParagraphInfo[] paraInfo = createMeasuredParagraphs(
+                text, params, 0, text.length(), true /* computeLayout */);
+        return new PrecomputedText(text, 0, text.length(), params, paraInfo);
     }
 
     /** @hide */
-    public static PrecomputedText createWidthOnly(@NonNull CharSequence text, @NonNull Params param,
-            @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
-        return createInternal(text, param, start, end, false /* compute width only */);
-    }
-
-    private static PrecomputedText createInternal(@NonNull CharSequence text, @NonNull Params param,
+    public static ParagraphInfo[] createMeasuredParagraphs(
+            @NonNull CharSequence text, @NonNull Params params,
             @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) {
-        Preconditions.checkNotNull(text);
-        Preconditions.checkNotNull(param);
-        final boolean needHyphenation = param.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
-                && param.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
+        ArrayList<ParagraphInfo> result = new ArrayList<>();
 
-        final IntArray paragraphEnds = new IntArray();
-        final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+        Preconditions.checkNotNull(text);
+        Preconditions.checkNotNull(params);
+        final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
+                && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
 
         int paraEnd = 0;
         for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
@@ -327,27 +336,22 @@
                 paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
             }
 
-            paragraphEnds.add(paraEnd);
-            measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
-                    param.getTextPaint(), text, paraStart, paraEnd, param.getTextDirection(),
-                    needHyphenation, computeLayout, null /* no recycle */));
+            result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
+                    params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(),
+                    needHyphenation, computeLayout, null /* no recycle */)));
         }
-
-        return new PrecomputedText(text, start, end, param,
-                                measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
-                                paragraphEnds.toArray());
+        return result.toArray(new ParagraphInfo[result.size()]);
     }
 
     // Use PrecomputedText.create instead.
     private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
-            @IntRange(from = 0) int end, @NonNull Params param,
-            @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) {
+            @IntRange(from = 0) int end, @NonNull Params params,
+            @NonNull ParagraphInfo[] paraInfo) {
         mText = new SpannedString(text);
         mStart = start;
         mEnd = end;
-        mParams = param;
-        mMeasuredParagraphs = measuredTexts;
-        mParagraphBreakPoints = paragraphBreakPoints;
+        mParams = params;
+        mParagraphInfo = paraInfo;
     }
 
     /**
@@ -384,7 +388,7 @@
      * Returns the count of paragraphs.
      */
     public @IntRange(from = 0) int getParagraphCount() {
-        return mParagraphBreakPoints.length;
+        return mParagraphInfo.length;
     }
 
     /**
@@ -392,7 +396,7 @@
      */
     public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
         Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
-        return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
+        return paraIndex == 0 ? mStart : getParagraphEnd(paraIndex - 1);
     }
 
     /**
@@ -400,12 +404,17 @@
      */
     public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
         Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
-        return mParagraphBreakPoints[paraIndex];
+        return mParagraphInfo[paraIndex].paragraphEnd;
     }
 
     /** @hide */
     public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
-        return mMeasuredParagraphs[paraIndex];
+        return mParagraphInfo[paraIndex].measured;
+    }
+
+    /** @hide */
+    public @NonNull ParagraphInfo[] getParagraphInfo() {
+        return mParagraphInfo;
     }
 
     /**
@@ -425,13 +434,13 @@
     public int findParaIndex(@IntRange(from = 0) int pos) {
         // TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring
         //       layout support to StaticLayout.
-        for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
-            if (pos < mParagraphBreakPoints[i]) {
+        for (int i = 0; i < mParagraphInfo.length; ++i) {
+            if (pos < mParagraphInfo[i].paragraphEnd) {
                 return i;
             }
         }
         throw new IndexOutOfBoundsException(
-            "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
+            "pos must be less than " + mParagraphInfo[mParagraphInfo.length - 1].paragraphEnd
             + ", gave " + pos);
     }
 
diff --git a/core/java/android/text/SpannableString.java b/core/java/android/text/SpannableString.java
index 56d0946..afb5df8 100644
--- a/core/java/android/text/SpannableString.java
+++ b/core/java/android/text/SpannableString.java
@@ -16,7 +16,6 @@
 
 package android.text;
 
-
 /**
  * This is the class for text whose content is immutable but to which
  * markup objects can be attached and detached.
@@ -26,12 +25,27 @@
 extends SpannableStringInternal
 implements CharSequence, GetChars, Spannable
 {
+    /**
+     * @param source source object to copy from
+     * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
+     * @hide
+     */
+    public SpannableString(CharSequence source, boolean ignoreNoCopySpan) {
+        super(source, 0, source.length(), ignoreNoCopySpan);
+    }
+
+    /**
+     * For the backward compatibility reasons, this constructor copies all spans including {@link
+     * android.text.NoCopySpan}.
+     * @param source source text
+     */
     public SpannableString(CharSequence source) {
-        super(source, 0, source.length());
+        this(source, false /* ignoreNoCopySpan */);  // preserve existing NoCopySpan behavior
     }
 
     private SpannableString(CharSequence source, int start, int end) {
-        super(source, start, end);
+        // preserve existing NoCopySpan behavior
+        super(source, start, end, false /* ignoreNoCopySpan */);
     }
 
     public static SpannableString valueOf(CharSequence source) {
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index 366ec14..5dd1a52 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -26,7 +26,7 @@
 /* package */ abstract class SpannableStringInternal
 {
     /* package */ SpannableStringInternal(CharSequence source,
-                                          int start, int end) {
+                                          int start, int end, boolean ignoreNoCopySpan) {
         if (start == 0 && end == source.length())
             mText = source.toString();
         else
@@ -38,24 +38,37 @@
 
         if (source instanceof Spanned) {
             if (source instanceof SpannableStringInternal) {
-                copySpans((SpannableStringInternal) source, start, end);
+                copySpans((SpannableStringInternal) source, start, end, ignoreNoCopySpan);
             } else {
-                copySpans((Spanned) source, start, end);
+                copySpans((Spanned) source, start, end, ignoreNoCopySpan);
             }
         }
     }
 
     /**
+     * This unused method is left since this is listed in hidden api list.
+     *
+     * Due to backward compatibility reasons, we copy even NoCopySpan by default
+     */
+    /* package */ SpannableStringInternal(CharSequence source, int start, int end) {
+        this(source, start, end, false /* ignoreNoCopySpan */);
+    }
+
+    /**
      * Copies another {@link Spanned} object's spans between [start, end] into this object.
      *
      * @param src Source object to copy from.
      * @param start Start index in the source object.
      * @param end End index in the source object.
+     * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
      */
-    private final void copySpans(Spanned src, int start, int end) {
+    private void copySpans(Spanned src, int start, int end, boolean ignoreNoCopySpan) {
         Object[] spans = src.getSpans(start, end, Object.class);
 
         for (int i = 0; i < spans.length; i++) {
+            if (ignoreNoCopySpan && spans[i] instanceof NoCopySpan) {
+                continue;
+            }
             int st = src.getSpanStart(spans[i]);
             int en = src.getSpanEnd(spans[i]);
             int fl = src.getSpanFlags(spans[i]);
@@ -76,35 +89,48 @@
      * @param src Source object to copy from.
      * @param start Start index in the source object.
      * @param end End index in the source object.
+     * @param ignoreNoCopySpan copy NoCopySpan for backward compatible reasons.
      */
-    private final void copySpans(SpannableStringInternal src, int start, int end) {
-        if (start == 0 && end == src.length()) {
+    private void copySpans(SpannableStringInternal src, int start, int end,
+            boolean ignoreNoCopySpan) {
+        int count = 0;
+        final int[] srcData = src.mSpanData;
+        final Object[] srcSpans = src.mSpans;
+        final int limit = src.mSpanCount;
+        boolean hasNoCopySpan = false;
+
+        for (int i = 0; i < limit; i++) {
+            int spanStart = srcData[i * COLUMNS + START];
+            int spanEnd = srcData[i * COLUMNS + END];
+            if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
+            if (srcSpans[i] instanceof NoCopySpan) {
+                hasNoCopySpan = true;
+                if (ignoreNoCopySpan) {
+                    continue;
+                }
+            }
+            count++;
+        }
+
+        if (count == 0) return;
+
+        if (!hasNoCopySpan && start == 0 && end == src.length()) {
             mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length);
             mSpanData = new int[src.mSpanData.length];
             mSpanCount = src.mSpanCount;
             System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length);
             System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length);
         } else {
-            int count = 0;
-            int[] srcData = src.mSpanData;
-            int limit = src.mSpanCount;
-            for (int i = 0; i < limit; i++) {
-                int spanStart = srcData[i * COLUMNS + START];
-                int spanEnd = srcData[i * COLUMNS + END];
-                if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
-                count++;
-            }
-
-            if (count == 0) return;
-
-            Object[] srcSpans = src.mSpans;
             mSpanCount = count;
             mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount);
             mSpanData = new int[mSpans.length * COLUMNS];
             for (int i = 0, j = 0; i < limit; i++) {
                 int spanStart = srcData[i * COLUMNS + START];
                 int spanEnd = srcData[i * COLUMNS + END];
-                if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
+                if (isOutOfCopyRange(start, end, spanStart, spanEnd)
+                        || (ignoreNoCopySpan && srcSpans[i] instanceof NoCopySpan)) {
+                    continue;
+                }
                 if (spanStart < start) spanStart = start;
                 if (spanEnd > end) spanEnd = end;
 
@@ -494,6 +520,21 @@
         return hash;
     }
 
+    /**
+     * Following two unused methods are left since these are listed in hidden api list.
+     *
+     * Due to backward compatibility reasons, we copy even NoCopySpan by default
+     */
+    private void copySpans(Spanned src, int start, int end) {
+        copySpans(src, start, end, false);
+    }
+
+    private void copySpans(SpannableStringInternal src, int start, int end) {
+        copySpans(src, start, end, false);
+    }
+
+
+
     private String mText;
     private Object[] mSpans;
     private int[] mSpanData;
diff --git a/core/java/android/text/SpannedString.java b/core/java/android/text/SpannedString.java
index afed221..acee3c5 100644
--- a/core/java/android/text/SpannedString.java
+++ b/core/java/android/text/SpannedString.java
@@ -26,12 +26,27 @@
 extends SpannableStringInternal
 implements CharSequence, GetChars, Spanned
 {
+    /**
+     * @param source source object to copy from
+     * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
+     * @hide
+     */
+    public SpannedString(CharSequence source, boolean ignoreNoCopySpan) {
+        super(source, 0, source.length(), ignoreNoCopySpan);
+    }
+
+    /**
+     * For the backward compatibility reasons, this constructor copies all spans including {@link
+     * android.text.NoCopySpan}.
+     * @param source source text
+     */
     public SpannedString(CharSequence source) {
-        super(source, 0, source.length());
+        this(source, false /* ignoreNoCopySpan */);  // preserve existing NoCopySpan behavior
     }
 
     private SpannedString(CharSequence source, int start, int end) {
-        super(source, start, end);
+        // preserve existing NoCopySpan behavior
+        super(source, start, end, false /* ignoreNoCopySpan */);
     }
 
     public CharSequence subSequence(int start, int end) {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 299bde2..0899074 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -651,31 +651,29 @@
                 b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
                 indents, mLeftPaddings, mRightPaddings);
 
-        PrecomputedText measured = null;
-        final Spanned spanned;
+        PrecomputedText.ParagraphInfo[] paragraphInfo = null;
+        final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null;
         if (source instanceof PrecomputedText) {
-            measured = (PrecomputedText) source;
-            if (!measured.canUseMeasuredResult(bufStart, bufEnd, textDir, paint, b.mBreakStrategy,
-                      b.mHyphenationFrequency)) {
+            PrecomputedText precomputed = (PrecomputedText) source;
+            if (precomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint,
+                      b.mBreakStrategy, b.mHyphenationFrequency)) {
                 // Some parameters are different from the ones when measured text is created.
-                measured = null;
+                paragraphInfo = precomputed.getParagraphInfo();
             }
         }
 
-        if (measured == null) {
+        if (paragraphInfo == null) {
             final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir,
                     b.mBreakStrategy, b.mHyphenationFrequency);
-            measured = PrecomputedText.createWidthOnly(source, param, bufStart, bufEnd);
-            spanned = (source instanceof Spanned) ? (Spanned) source : null;
-        } else {
-            final CharSequence original = measured.getText();
-            spanned = (original instanceof Spanned) ? (Spanned) original : null;
+            paragraphInfo = PrecomputedText.createMeasuredParagraphs(source, param, bufStart,
+                    bufEnd, false /* computeLayout */);
         }
 
         try {
-            for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
-                final int paraStart = measured.getParagraphStart(paraIndex);
-                final int paraEnd = measured.getParagraphEnd(paraIndex);
+            for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
+                final int paraStart = paraIndex == 0
+                        ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
+                final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;
 
                 int firstWidthLineCount = 1;
                 int firstWidth = outerWidth;
@@ -741,7 +739,7 @@
                     }
                 }
 
-                final MeasuredParagraph measuredPara = measured.getMeasuredParagraph(paraIndex);
+                final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
                 final char[] chs = measuredPara.getChars();
                 final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
                 final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index c44f42b..46e3169 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -219,7 +219,7 @@
                     && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
                 try {
                     if (value == field.getInt(null)) {
-                        return field.getName().substring(prefix.length());
+                        return constNameWithoutPrefix(prefix, field);
                     }
                 } catch (IllegalAccessException ignored) {
                 }
@@ -236,6 +236,7 @@
      */
     public static String flagsToString(Class<?> clazz, String prefix, int flags) {
         final StringBuilder res = new StringBuilder();
+        boolean flagsWasZero = flags == 0;
 
         for (Field field : clazz.getDeclaredFields()) {
             final int modifiers = field.getModifiers();
@@ -243,9 +244,12 @@
                     && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
                 try {
                     final int value = field.getInt(null);
+                    if (value == 0 && flagsWasZero) {
+                        return constNameWithoutPrefix(prefix, field);
+                    }
                     if ((flags & value) != 0) {
                         flags &= ~value;
-                        res.append(field.getName().substring(prefix.length())).append('|');
+                        res.append(constNameWithoutPrefix(prefix, field)).append('|');
                     }
                 } catch (IllegalAccessException ignored) {
                 }
@@ -258,4 +262,8 @@
         }
         return res.toString();
     }
+
+    private static String constNameWithoutPrefix(String prefix, Field field) {
+        return field.getName().substring(prefix.length());
+    }
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 613e6d8..6486230 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -428,4 +428,14 @@
      * on the next user activity.
      */
     void requestUserActivityNotification();
+
+    /**
+     * Notify WindowManager that it should not override the info in DisplayManager for the specified
+     * display. This can disable letter- or pillar-boxing applied in DisplayManager when the metrics
+     * of the logical display reported from WindowManager do not correspond to the metrics of the
+     * physical display it is based on.
+     *
+     * @param displayId The id of the display.
+     */
+    void dontOverrideDisplayInfo(int displayId);
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b7524fb..7ff4f21 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -566,7 +566,7 @@
      */
     private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
             SurfaceControl parent, int windowType, int ownerUid)
-                    throws OutOfResourcesException {
+                    throws OutOfResourcesException, IllegalArgumentException {
         if (session == null) {
             throw new IllegalArgumentException("session must not be null");
         }
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 779eefb..886f5c8 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -445,9 +445,15 @@
         private void processOrEnqueueTask(SpellCheckerParams scp) {
             ISpellCheckerSession session;
             synchronized (this) {
+                if (scp.mWhat == TASK_CLOSE && (mState == STATE_CLOSED_AFTER_CONNECTION
+                        || mState == STATE_CLOSED_BEFORE_CONNECTION)) {
+                    // It is OK to call SpellCheckerSession#close() multiple times.
+                    // Don't output confusing/misleading warning messages.
+                    return;
+                }
                 if (mState != STATE_WAIT_CONNECTION && mState != STATE_CONNECTED) {
                     Log.e(TAG, "ignoring processOrEnqueueTask due to unexpected mState="
-                            + taskToString(scp.mWhat)
+                            + stateToString(mState)
                             + " scp.mWhat=" + taskToString(scp.mWhat));
                     return;
                 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5178a97..fc94b1f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2451,6 +2451,14 @@
      * Returns the {@link Looper} corresponding to the thread on which WebView calls must be made.
      */
     @NonNull
+    public Looper getWebViewLooper() {
+        return mWebViewThread;
+    }
+
+    /**
+     * Returns the {@link Looper} corresponding to the thread on which WebView calls must be made.
+     */
+    @NonNull
     public Looper getLooper() {
         return mWebViewThread;
     }
diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl
index 1bee692..93730df 100644
--- a/core/java/com/android/internal/app/ISoundTriggerService.aidl
+++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl
@@ -17,8 +17,10 @@
 package com.android.internal.app;
 
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.SoundTrigger;
+import android.os.Bundle;
 import android.os.ParcelUuid;
 
 /**
@@ -44,9 +46,15 @@
     int startRecognitionForIntent(in ParcelUuid soundModelId, in PendingIntent callbackIntent,
          in SoundTrigger.RecognitionConfig config);
 
+
+    int startRecognitionForService(in ParcelUuid soundModelId, in Bundle params,
+         in ComponentName callbackIntent,in SoundTrigger.RecognitionConfig config);
+
+    /** For both ...Intent and ...Service based usage */
     int stopRecognitionForIntent(in ParcelUuid soundModelId);
 
     int unloadSoundModel(in ParcelUuid soundModelId);
 
+    /** For both ...Intent and ...Service based usage */
     boolean isRecognitionActive(in ParcelUuid parcelUuid);
 }
diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java
index 82ac241..d53090b 100644
--- a/core/java/com/android/internal/util/FunctionalUtils.java
+++ b/core/java/com/android/internal/util/FunctionalUtils.java
@@ -17,6 +17,7 @@
 package com.android.internal.util;
 
 import android.os.RemoteException;
+import android.util.ExceptionUtils;
 
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -36,21 +37,45 @@
     }
 
     /**
-     *
+     * Wraps a given {@code action} into one that ignores any {@link RemoteException}s
      */
     public static <T> Consumer<T> ignoreRemoteException(RemoteExceptionIgnoringConsumer<T> action) {
         return action;
     }
 
     /**
+     * Wraps the given {@link ThrowingRunnable} into one that handles any exceptions using the
+     * provided {@code handler}
+     */
+    public static Runnable handleExceptions(ThrowingRunnable r, Consumer<Throwable> handler) {
+        return () -> {
+            try {
+                r.run();
+            } catch (Throwable t) {
+                handler.accept(t);
+            }
+        };
+    }
+
+    /**
      * An equivalent of {@link Runnable} that allows throwing checked exceptions
      *
      * This can be used to specify a lambda argument without forcing all the checked exceptions
      * to be handled within it
      */
     @FunctionalInterface
-    public interface ThrowingRunnable {
+    @SuppressWarnings("FunctionalInterfaceMethodChanged")
+    public interface ThrowingRunnable extends Runnable {
         void runOrThrow() throws Exception;
+
+        @Override
+        default void run() {
+            try {
+                runOrThrow();
+            } catch (Exception ex) {
+                throw ExceptionUtils.propagate(ex);
+            }
+        }
     }
 
     /**
@@ -80,7 +105,7 @@
             try {
                 acceptOrThrow(t);
             } catch (Exception ex) {
-                throw new RuntimeException(ex);
+                throw ExceptionUtils.propagate(ex);
             }
         }
     }
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 25e1589..bec70fd 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -78,10 +78,14 @@
     byte[] startRecoverySession(in String sessionId,
             in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
             in List<KeyChainProtectionParams> secrets);
-    byte[] startRecoverySessionWithCertPath(in String sessionId,
+    byte[] startRecoverySessionWithCertPath(in String sessionId, in String rootCertificateAlias,
             in RecoveryCertPath verifierCertPath, in byte[] vaultParams, in byte[] vaultChallenge,
             in List<KeyChainProtectionParams> secrets);
     Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
             in List<WrappedApplicationKey> applicationKeys);
+    Map/*<String, String>*/ recoverKeyChainSnapshot(
+            in String sessionId,
+            in byte[] recoveryKeyBlob,
+            in List<WrappedApplicationKey> applicationKeys);
     void closeSession(in String sessionId);
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index fde7e96..d4ab426 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -16,6 +16,15 @@
 
 package com.android.internal.widget;
 
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.admin.DevicePolicyManager;
@@ -59,7 +68,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-
 /**
  * Utilities for the lock pattern and its settings.
  */
@@ -585,7 +593,7 @@
             return quality;
         }
 
-        return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+        return PASSWORD_QUALITY_UNSPECIFIED;
     }
 
     /**
@@ -604,13 +612,16 @@
      * Clear any lock pattern or password.
      */
     public void clearLock(String savedCredential, int userHandle) {
-        setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
+        setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle);
 
         try{
             getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential,
-                    DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
-        } catch (RemoteException e) {
-            // well, we tried...
+                    PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to clear lock", e);
+            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
+            return;
         }
 
         if (userHandle == UserHandle.USER_SYSTEM) {
@@ -669,32 +680,34 @@
      * @param userId the user whose pattern is to be saved.
      */
     public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) {
-        try {
-            if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
-                throw new IllegalArgumentException("pattern must not be null and at least "
-                        + MIN_LOCK_PATTERN_SIZE + " dots long.");
-            }
-
-            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
-            getLockSettings().setLockCredential(patternToString(pattern), CREDENTIAL_TYPE_PATTERN,
-                    savedPattern, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
-
-            // Update the device encryption password.
-            if (userId == UserHandle.USER_SYSTEM
-                    && LockPatternUtils.isDeviceEncryptionEnabled()) {
-                if (!shouldEncryptWithCredentials(true)) {
-                    clearEncryptionPassword();
-                } else {
-                    String stringPattern = patternToString(pattern);
-                    updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
-                }
-            }
-
-            reportPatternWasChosen(userId);
-            onAfterChangingPassword(userId);
-        } catch (RemoteException re) {
-            Log.e(TAG, "Couldn't save lock pattern " + re);
+        if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
+            throw new IllegalArgumentException("pattern must not be null and at least "
+                    + MIN_LOCK_PATTERN_SIZE + " dots long.");
         }
+
+        final String stringPattern = patternToString(pattern);
+        final int currentQuality = getKeyguardStoredPasswordQuality(userId);
+        setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId);
+        try {
+            getLockSettings().setLockCredential(stringPattern, CREDENTIAL_TYPE_PATTERN,
+                    savedPattern, PASSWORD_QUALITY_SOMETHING, userId);
+        } catch (Exception e) {
+            Log.e(TAG, "Couldn't save lock pattern", e);
+            setKeyguardStoredPasswordQuality(currentQuality, userId);
+            return;
+        }
+        // Update the device encryption password.
+        if (userId == UserHandle.USER_SYSTEM
+                && LockPatternUtils.isDeviceEncryptionEnabled()) {
+            if (!shouldEncryptWithCredentials(true)) {
+                clearEncryptionPassword();
+            } else {
+                updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
+            }
+        }
+
+        reportPatternWasChosen(userId);
+        onAfterChangingPassword(userId);
     }
 
     private void updateCryptoUserInfo(int userId) {
@@ -796,25 +809,27 @@
      */
     public void saveLockPassword(String password, String savedPassword, int requestedQuality,
             int userHandle) {
-        try {
-            if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
-                throw new IllegalArgumentException("password must not be null and at least "
-                        + "of length " + MIN_LOCK_PASSWORD_SIZE);
-            }
-
-            setLong(PASSWORD_TYPE_KEY,
-                    computePasswordQuality(CREDENTIAL_TYPE_PASSWORD, password, requestedQuality),
-                    userHandle);
-            getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword,
-                    requestedQuality, userHandle);
-
-            updateEncryptionPasswordIfNeeded(password,
-                    PasswordMetrics.computeForPassword(password).quality, userHandle);
-            updatePasswordHistory(password, userHandle);
-        } catch (RemoteException re) {
-            // Cant do much
-            Log.e(TAG, "Unable to save lock password " + re);
+        if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
+            throw new IllegalArgumentException("password must not be null and at least "
+                    + "of length " + MIN_LOCK_PASSWORD_SIZE);
         }
+
+        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
+        setKeyguardStoredPasswordQuality(
+                computePasswordQuality(CREDENTIAL_TYPE_PASSWORD, password, requestedQuality),
+                userHandle);
+        try {
+            getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD,
+                    savedPassword, requestedQuality, userHandle);
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to save lock password", e);
+            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
+            return;
+        }
+
+        updateEncryptionPasswordIfNeeded(password,
+                PasswordMetrics.computeForPassword(password).quality, userHandle);
+        updatePasswordHistory(password, userHandle);
     }
 
     /**
@@ -828,9 +843,8 @@
             if (!shouldEncryptWithCredentials(true)) {
                 clearEncryptionPassword();
             } else {
-                boolean numeric = quality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-                boolean numericComplex = quality
-                        == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+                boolean numeric = quality == PASSWORD_QUALITY_NUMERIC;
+                boolean numericComplex = quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
                 int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
                         : StorageManager.CRYPT_TYPE_PASSWORD;
                 updateEncryptionPassword(type, password);
@@ -894,8 +908,11 @@
      * @return stored password quality
      */
     public int getKeyguardStoredPasswordQuality(int userHandle) {
-        return (int) getLong(PASSWORD_TYPE_KEY,
-                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+        return (int) getLong(PASSWORD_TYPE_KEY, PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+    }
+
+    private void setKeyguardStoredPasswordQuality(int quality, int userHandle) {
+        setLong(PASSWORD_TYPE_KEY, quality, userHandle);
     }
 
     /**
@@ -909,9 +926,9 @@
             int computedQuality = PasswordMetrics.computeForPassword(credential).quality;
             quality = Math.max(requestedQuality, computedQuality);
         } else if (type == CREDENTIAL_TYPE_PATTERN)  {
-            quality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+            quality = PASSWORD_QUALITY_SOMETHING;
         } else /* if (type == CREDENTIAL_TYPE_NONE) */ {
-            quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+            quality = PASSWORD_QUALITY_UNSPECIFIED;
         }
         return quality;
     }
@@ -1125,12 +1142,12 @@
     }
 
     private boolean isLockPasswordEnabled(int mode, int userId) {
-        final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
+        final boolean passwordEnabled = mode == PASSWORD_QUALITY_ALPHABETIC
+                || mode == PASSWORD_QUALITY_NUMERIC
+                || mode == PASSWORD_QUALITY_NUMERIC_COMPLEX
+                || mode == PASSWORD_QUALITY_ALPHANUMERIC
+                || mode == PASSWORD_QUALITY_COMPLEX
+                || mode == PASSWORD_QUALITY_MANAGED;
         return passwordEnabled && savedPasswordExists(userId);
     }
 
@@ -1155,8 +1172,7 @@
     }
 
     private boolean isLockPatternEnabled(int mode, int userId) {
-        return mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
-                && savedPatternExists(userId);
+        return mode == PASSWORD_QUALITY_SOMETHING && savedPatternExists(userId);
     }
 
     /**
@@ -1551,7 +1567,7 @@
                     token, quality, userId)) {
                 return false;
             }
-            setLong(PASSWORD_TYPE_KEY, quality, userId);
+            setKeyguardStoredPasswordQuality(quality, userId);
 
             updateEncryptionPasswordIfNeeded(credential, quality, userId);
             updatePasswordHistory(credential, userId);
@@ -1560,12 +1576,10 @@
                 throw new IllegalArgumentException("password must be emtpy for NONE type");
             }
             if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE,
-                    tokenHandle, token, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
-                    userId)) {
+                    tokenHandle, token, PASSWORD_QUALITY_UNSPECIFIED, userId)) {
                 return false;
             }
-            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
-                    userId);
+            setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId);
 
             if (userId == UserHandle.USER_SYSTEM) {
                 // Set the encryption password to default.
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index d6496cd..7166c75 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -42,7 +42,6 @@
     }
 
     auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
-    auto info = imageDecoder->mCodec->getInfo();
     const SkISize scaledSize = SkISize::Make(width, height);
     SkIRect subset;
     if (jsubset) {
@@ -51,6 +50,35 @@
         subset = SkIRect::MakeWH(width, height);
     }
 
+    auto info = imageDecoder->mCodec->getInfo();
+    bool hasRestoreFrame = false;
+    if (imageDecoder->mCodec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) {
+        if (width < info.width() && height < info.height()) {
+            // WebP will scale its SkBitmap to the scaled size.
+            // FIXME: b/73529447 GIF should do the same.
+            info = info.makeWH(width, height);
+        }
+    } else {
+        const int frameCount = imageDecoder->mCodec->codec()->getFrameCount();
+        for (int i = 0; i < frameCount; ++i) {
+            SkCodec::FrameInfo frameInfo;
+            if (!imageDecoder->mCodec->codec()->getFrameInfo(i, &frameInfo)) {
+                doThrowIOE(env, "Failed to read frame info!");
+                return 0;
+            }
+            if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) {
+                hasRestoreFrame = true;
+                break;
+            }
+        }
+    }
+
+    size_t bytesUsed = info.computeMinByteSize();
+    // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a
+    // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current
+    // frame and the next frame. (The former assumes that the image is animated, and the
+    // latter assumes that it is drawn to a hardware canvas.)
+    bytesUsed *= hasRestoreFrame ? 4 : 3;
     sk_sp<SkPicture> picture;
     if (jpostProcess) {
         SkRect bounds = SkRect::MakeWH(subset.width(), subset.height());
@@ -63,6 +91,7 @@
             return 0;
         }
         picture = recorder.finishRecordingAsPicture();
+        bytesUsed += picture->approximateBytesUsed();
     }
 
 
@@ -74,7 +103,10 @@
         return 0;
     }
 
-    sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(animatedImg));
+    bytesUsed += sizeof(animatedImg.get());
+
+    sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(std::move(animatedImg),
+                                                                    bytesUsed));
     return reinterpret_cast<jlong>(drawable.release());
 }
 
@@ -202,10 +234,9 @@
     }
 }
 
-static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
-    // FIXME: Report the size of the internal SkBitmap etc.
-    return sizeof(drawable);
+    return drawable->byteSize();
 }
 
 static void AnimatedImageDrawable_nMarkInvisible(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp
index d20c7ef..32ac30f 100644
--- a/core/jni/android/graphics/pdf/PdfRenderer.cpp
+++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp
@@ -92,20 +92,7 @@
         renderFlags |= FPDF_PRINTING;
     }
 
-    // PDF's coordinate system origin is left-bottom while in graphics it
-    // is the top-left. So, translate the PDF coordinates to ours.
-    SkMatrix reflectOnX = SkMatrix::MakeScale(1, -1);
-    SkMatrix moveUp = SkMatrix::MakeTrans(0, FPDF_GetPageHeight(page));
-    SkMatrix coordinateChange = SkMatrix::Concat(moveUp, reflectOnX);
-
-    // Apply the transformation
-    SkMatrix matrix;
-    if (transformPtr == 0) {
-        matrix = coordinateChange;
-    } else {
-        matrix = SkMatrix::Concat(*reinterpret_cast<SkMatrix*>(transformPtr), coordinateChange);
-    }
-
+    SkMatrix matrix = *reinterpret_cast<SkMatrix*>(transformPtr);
     SkScalar transformValues[6];
     if (!matrix.asAffine(transformValues)) {
         jniThrowException(env, "java/lang/IllegalArgumentException",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a30b2ad..04cb08f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -116,9 +116,13 @@
     ScopedUtfChars name(env, nameStr);
     sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
     SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
-    sp<SurfaceControl> surface = client->createSurface(
-            String8(name.c_str()), w, h, format, flags, parent, windowType, ownerUid);
-    if (surface == NULL) {
+    sp<SurfaceControl> surface;
+    status_t err = client->createSurfaceChecked(
+            String8(name.c_str()), w, h, format, &surface, flags, parent, windowType, ownerUid);
+    if (err == NAME_NOT_FOUND) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return 0;
+    } else if (err != NO_ERROR) {
         jniThrowException(env, OutOfResourcesException, NULL);
         return 0;
     }
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 4657dc4..6a3aaab 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -118,17 +118,17 @@
 
     // Stack dumps
     optional android.os.BackTraceProto native_traces = 1200 [
-        (section).type = SECTION_TOMBSTONE,
+        (section).type = SECTION_NONE,
         (section).args = "native"
     ];
 
     optional android.os.BackTraceProto hal_traces = 1201 [
-        (section).type = SECTION_TOMBSTONE,
+        (section).type = SECTION_NONE,
         (section).args = "hal"
     ];
 
     optional android.os.BackTraceProto java_traces = 1202 [
-        (section).type = SECTION_TOMBSTONE,
+        (section).type = SECTION_NONE,
         (section).args = "java"
     ];
 
@@ -169,7 +169,7 @@
     ];
 
     optional GZippedFileProto last_kmsg = 2007 [
-        (section).type = SECTION_GZIP,
+        (section).type = SECTION_NONE, // disable until selinux permission is gained
         (section).args = "/sys/fs/pstore/console-ramoops /sys/fs/pstore/console-ramoops-0 /proc/last_kmsg",
         (privacy).dest = DEST_AUTOMATIC
     ];
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 53e6532..4e781d6 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -468,6 +468,8 @@
     optional SettingProto chained_battery_attribution_enabled = 356 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto autofill_compat_allowed_packages = 357 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto hidden_api_blacklist_exemptions = 358 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto sound_trigger_detection_service_op_timeout = 387  [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto max_sound_trigger_detection_service_ops_per_day = 388  [ (android.privacy).dest = DEST_AUTOMATIC ];
     // Subscription to be used for voice call on a multi sim device. The
     // supported values are 0 = SUB1, 1 = SUB2 and etc.
     optional SettingProto multi_sim_voice_call_subscription = 359 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -505,7 +507,7 @@
     optional SettingsProto backup_agent_timeout_parameters = 386;
     // Please insert fields in the same order as in
     // frameworks/base/core/java/android/provider/Settings.java.
-    // Next tag = 387;
+    // Next tag = 389;
 }
 
 message SecureSettingsProto {
diff --git a/core/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto
index f422065..c2fedf5 100644
--- a/core/proto/android/service/graphicsstats.proto
+++ b/core/proto/android/service/graphicsstats.proto
@@ -56,7 +56,7 @@
     // Number of "missed vsync" events.
     optional int32 missed_vsync_count = 3;
 
-    // Number of "high input latency" events.
+    // Number of frames in triple-buffering scenario (high input latency)
     optional int32 high_input_latency_count = 4;
 
     // Number of "slow UI thread" events.
@@ -67,6 +67,9 @@
 
     // Number of "slow draw" events.
     optional int32 slow_draw_count = 7;
+
+    // Number of frames that missed their deadline (aka, visibly janked)
+    optional int32 missed_deadline_count = 8;
 }
 
 message GraphicsStatsHistogramBucketProto {
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 5c40e5f..cccd2fe 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -93,7 +93,8 @@
     message ServiceProto {
         option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-        repeated string name = 1 [ (.android.privacy).dest = DEST_EXPLICIT ];
+        // Package or component name.
+        repeated string name = 1;
         optional int32 user_id = 2;
         optional bool is_primary = 3;
     }
@@ -169,16 +170,16 @@
 message ZenRuleProto {
     option (android.msg_privacy).dest = DEST_EXPLICIT;
 
-    // Required for automatic (unique).
+    // Required for automatic ZenRules (unique).
     optional string id = 1;
-    // Required for automatic.
+    // Required for automatic ZenRules.
     optional string name = 2;
-    // Required for automatic.
+    // Required for automatic ZenRules.
     optional int64 creation_time_ms = 3 [
         (android.privacy).dest = DEST_AUTOMATIC
     ];
     optional bool enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Package name, only used for manual rules.
+    // Package name, only used for manual ZenRules.
     optional string enabler = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
     // User manually disabled this instance.
     optional bool is_snoozing = 6 [
@@ -188,7 +189,7 @@
         (android.privacy).dest = DEST_AUTOMATIC
     ];
 
-    // Required for automatic.
+    // Required for automatic ZenRules.
     optional string condition_id = 8;
     optional ConditionProto condition = 9;
     optional android.content.ComponentNameProto component = 10;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9b11a33..1b4d571 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3754,10 +3754,17 @@
 
     <!-- Must be required by system/priv apps when accessing the sound trigger
          APIs given by {@link SoundTriggerManager}.
-         @hide <p>Not for use by third-party applications.</p> -->
+         @hide
+         @SystemApi -->
     <permission android:name="android.permission.MANAGE_SOUND_TRIGGER"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by system/priv apps implementing sound trigger detection services
+         @hide
+         @SystemApi -->
+    <permission android:name="android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows trusted applications to dispatch managed provisioning message to Managed
          Provisioning app. If requesting app does not have permission, it will be ignored.
          @hide -->
@@ -3879,6 +3886,12 @@
     <permission android:name="android.permission.WATCH_APPOPS"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to directly open the "Open by default" page inside a package's
+         Details screen.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.OPEN_APPLICATION_DETAILS_OPEN_BY_DEFAULT_PAGE"
+                android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/layout/shutdown_dialog.xml b/core/res/res/layout/shutdown_dialog.xml
index 398bfb1..2d214b3 100644
--- a/core/res/res/layout/shutdown_dialog.xml
+++ b/core/res/res/layout/shutdown_dialog.xml
@@ -29,7 +29,7 @@
     <TextView
         android:id="@id/text1"
         android:layout_width="wrap_content"
-        android:layout_height="32dp"
+        android:layout_height="32sp"
         android:text="@string/shutdown_progress"
         android:textDirection="locale"
         android:textSize="24sp"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4a72bf9..75b3bcf 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2151,6 +2151,9 @@
             @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
             -->
             <enum name="shortEdges" value="1" />
+            <!-- Use {@code shortEdges} instead. This is temporarily here to unblock pushing the SDK
+                 until all usages have been migrated to {@code shortEdges} -->
+            <enum name="always" value="1" />
             <!-- The window is never allowed to overlap with the DisplayCutout area.
             <p>
             This should be used with windows that transiently set {@code SYSTEM_UI_FLAG_FULLSCREEN}
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ad4d7dd..ac01c4e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3232,6 +3232,7 @@
   <java-symbol type="id" name="remote_input_progress" />
   <java-symbol type="id" name="remote_input_send" />
   <java-symbol type="id" name="remote_input" />
+  <java-symbol type="dimen" name="notification_content_margin" />
   <java-symbol type="dimen" name="slice_shortcut_size" />
   <java-symbol type="dimen" name="slice_icon_size" />
   <java-symbol type="dimen" name="slice_padding" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 4628aa9..a99e139 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -22,6 +22,7 @@
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
 import android.app.servertransaction.ResumeActivityItem;
+import android.app.servertransaction.StopActivityItem;
 import android.content.Intent;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
@@ -34,6 +35,8 @@
 
 /**
  * Test for verifying {@link android.app.ActivityThread} class.
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:android.app.activity.ActivityThreadTest
  */
 @RunWith(AndroidJUnit4.class)
 @MediumTest
@@ -63,15 +66,23 @@
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
 
+    @Test
+    public void testSleepAndStop() throws Exception {
+        final Activity activity = mActivityTestRule.launchActivity(new Intent());
+        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+
+        appThread.scheduleSleeping(activity.getActivityToken(), true /* sleeping */);
+        appThread.scheduleTransaction(newStopTransaction(activity));
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
     private static ClientTransaction newRelaunchResumeTransaction(Activity activity) {
         final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(null,
-                null, 0, new MergedConfiguration(),
-                false /* preserveWindow */);
+                null, 0, new MergedConfiguration(), false /* preserveWindow */);
         final ResumeActivityItem resumeStateRequest =
                 ResumeActivityItem.obtain(true /* isForward */);
-        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
-        final ClientTransaction transaction =
-                ClientTransaction.obtain(appThread, activity.getActivityToken());
+
+        final ClientTransaction transaction = newTransaction(activity);
         transaction.addCallback(callbackItem);
         transaction.setLifecycleStateRequest(resumeStateRequest);
 
@@ -81,14 +92,28 @@
     private static ClientTransaction newResumeTransaction(Activity activity) {
         final ResumeActivityItem resumeStateRequest =
                 ResumeActivityItem.obtain(true /* isForward */);
-        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
-        final ClientTransaction transaction =
-                ClientTransaction.obtain(appThread, activity.getActivityToken());
+
+        final ClientTransaction transaction = newTransaction(activity);
         transaction.setLifecycleStateRequest(resumeStateRequest);
 
         return transaction;
     }
 
+    private static ClientTransaction newStopTransaction(Activity activity) {
+        final StopActivityItem stopStateRequest =
+                StopActivityItem.obtain(false /* showWindow */, 0 /* configChanges */);
+
+        final ClientTransaction transaction = newTransaction(activity);
+        transaction.setLifecycleStateRequest(stopStateRequest);
+
+        return transaction;
+    }
+
+    private static ClientTransaction newTransaction(Activity activity) {
+        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+        return ClientTransaction.obtain(appThread, activity.getActivityToken());
+    }
+
     // Test activity
     public static class TestActivity extends Activity {
     }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index cc36b96..2036e2f 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -129,6 +129,7 @@
                     Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS,
                     Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS,
                     Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
+                    Settings.Global.BLE_SCAN_BACKGROUND_MODE,
                     Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
                     Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
                     Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX,
@@ -169,6 +170,7 @@
                     Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
                     Settings.Global.CONTACT_METADATA_SYNC_ENABLED,
                     Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
+                    Settings.Global.CPU_SCALING_ENABLED,
                     Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
                     Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
                     Settings.Global.DATABASE_CREATION_BUILDID,
@@ -266,6 +268,7 @@
                     Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX,
                     Settings.Global.LTE_SERVICE_FORCED,
                     Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
+                    Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
                     Settings.Global.MDC_INITIAL_MAX_RETRY,
                     Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
                     Settings.Global.MHL_POWER_CHARGE_ENABLED,
@@ -369,6 +372,7 @@
                     Settings.Global.SMS_SHORT_CODE_RULE,
                     Settings.Global.SMS_SHORT_CODES_UPDATE_CONTENT_URL,
                     Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL,
+                    Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
                     Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
                     Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
                     Settings.Global.STORAGE_BENCHMARK_INTERVAL,
diff --git a/core/tests/coretests/src/android/security/backup/TrustedRootCertificatesTest.java b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
similarity index 90%
rename from core/tests/coretests/src/android/security/backup/TrustedRootCertificatesTest.java
rename to core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
index 0f9cb45..3b4ad38 100644
--- a/core/tests/coretests/src/android/security/backup/TrustedRootCertificatesTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.security.backup;
+package android.security.keystore.recovery;
 
-import static android.security.backup.TrustedRootCertificates.listRootCertificates;
+import static android.security.keystore.recovery.TrustedRootCertificates.listRootCertificates;
 
 import static org.junit.Assert.assertTrue;
 
diff --git a/core/tests/coretests/src/android/security/backup/X509CertificateParsingUtilsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
similarity index 96%
rename from core/tests/coretests/src/android/security/backup/X509CertificateParsingUtilsTest.java
rename to core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
index ad85c25..7f0eb43 100644
--- a/core/tests/coretests/src/android/security/backup/X509CertificateParsingUtilsTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.security.backup;
+package android.security.keystore.recovery;
 
-import static android.security.backup.X509CertificateParsingUtils.decodeBase64Cert;
+import static android.security.keystore.recovery.X509CertificateParsingUtils.decodeBase64Cert;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
diff --git a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
new file mode 100644
index 0000000..c205f96
--- /dev/null
+++ b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.text;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.style.QuoteSpan;
+import android.text.style.UnderlineSpan;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SpannableStringNoCopyTest {
+    @Test
+    public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // By default, copy NoCopySpans
+        final SpannedString copied = new SpannedString(first);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(3, spans.length);
+    }
+
+    @Test
+    public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // Do not copy NoCopySpan if specified so.
+        final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(2, spans.length);
+
+        for (int i = 0; i < spans.length; i++) {
+            assertFalse(spans[i] instanceof NoCopySpan);
+        }
+    }
+
+    @Test
+    public void testCopyConstructor_copyNoCopySpans_OtherSpannableImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // By default, copy NoCopySpans
+        final SpannedString copied = new SpannedString(new CustomSpannable(first));
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(3, spans.length);
+    }
+
+    @Test
+    public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannableImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // Do not copy NoCopySpan if specified so.
+        final SpannedString copied = new SpannedString(
+                new CustomSpannable(first), false /* copyNoCopySpan */);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(2, spans.length);
+
+        for (int i = 0; i < spans.length; i++) {
+            assertFalse(spans[i] instanceof NoCopySpan);
+        }
+    }
+
+    // A custom implementation of Spannable.
+    private static class CustomSpannable implements Spannable {
+        private final @NonNull Spannable mText;
+
+        CustomSpannable(@NonNull Spannable text) {
+            mText = text;
+        }
+
+        @Override
+        public void setSpan(Object what, int start, int end, int flags) {
+            mText.setSpan(what, start, end, flags);
+        }
+
+        @Override
+        public void removeSpan(Object what) {
+            mText.removeSpan(what);
+        }
+
+        @Override
+        public <T> T[] getSpans(int start, int end, Class<T> type) {
+            return mText.getSpans(start, end, type);
+        }
+
+        @Override
+        public int getSpanStart(Object tag) {
+            return mText.getSpanStart(tag);
+        }
+
+        @Override
+        public int getSpanEnd(Object tag) {
+            return mText.getSpanEnd(tag);
+        }
+
+        @Override
+        public int getSpanFlags(Object tag) {
+            return mText.getSpanFlags(tag);
+        }
+
+        @Override
+        public int nextSpanTransition(int start, int limit, Class type) {
+            return mText.nextSpanTransition(start, limit, type);
+        }
+
+        @Override
+        public int length() {
+            return mText.length();
+        }
+
+        @Override
+        public char charAt(int index) {
+            return mText.charAt(index);
+        }
+
+        @Override
+        public CharSequence subSequence(int start, int end) {
+            return mText.subSequence(start, end);
+        }
+
+        @Override
+        public String toString() {
+            return mText.toString();
+        }
+    };
+}
diff --git a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
new file mode 100644
index 0000000..0680924
--- /dev/null
+++ b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.text;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.style.QuoteSpan;
+import android.text.style.UnderlineSpan;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SpannedStringNoCopyTest {
+    @Test
+    public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // By default, copy NoCopySpans
+        final SpannedString copied = new SpannedString(first);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(3, spans.length);
+    }
+
+    @Test
+    public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // Do not copy NoCopySpan if specified so.
+        final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(2, spans.length);
+
+        for (int i = 0; i < spans.length; i++) {
+            assertFalse(spans[i] instanceof NoCopySpan);
+        }
+    }
+
+    @Test
+    public void testCopyConstructor_copyNoCopySpans_OtherSpannedImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // By default, copy NoCopySpans
+        final SpannedString copied = new SpannedString(new CustomSpanned(first));
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(3, spans.length);
+    }
+
+    @Test
+    public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannedImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // Do not copy NoCopySpan if specified so.
+        final SpannedString copied = new SpannedString(
+                new CustomSpanned(first), false /* copyNoCopySpan */);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(2, spans.length);
+
+        for (int i = 0; i < spans.length; i++) {
+            assertFalse(spans[i] instanceof NoCopySpan);
+        }
+    }
+
+    // A custom implementation of Spanned
+    private static class CustomSpanned implements Spanned {
+        private final @NonNull Spanned mText;
+
+        CustomSpanned(@NonNull Spannable text) {
+            mText = text;
+        }
+
+        @Override
+        public <T> T[] getSpans(int start, int end, Class<T> type) {
+            return mText.getSpans(start, end, type);
+        }
+
+        @Override
+        public int getSpanStart(Object tag) {
+            return mText.getSpanStart(tag);
+        }
+
+        @Override
+        public int getSpanEnd(Object tag) {
+            return mText.getSpanEnd(tag);
+        }
+
+        @Override
+        public int getSpanFlags(Object tag) {
+            return mText.getSpanFlags(tag);
+        }
+
+        @Override
+        public int nextSpanTransition(int start, int limit, Class type) {
+            return mText.nextSpanTransition(start, limit, type);
+        }
+
+        @Override
+        public int length() {
+            return mText.length();
+        }
+
+        @Override
+        public char charAt(int index) {
+            return mText.charAt(index);
+        }
+
+        @Override
+        public CharSequence subSequence(int start, int end) {
+            return mText.subSequence(start, end);
+        }
+
+        @Override
+        public String toString() {
+            return mText.toString();
+        }
+    };
+}
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index be0372d..bacddf14 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -25,42 +25,50 @@
   <hidden-api-whitelisted-app package="android.car.input.service" />
   <hidden-api-whitelisted-app package="android.car.usb.handler" />
   <hidden-api-whitelisted-app package="android.ext.services" />
-  <hidden-api-whitelisted-app package="android.ext.shared" />
+  <hidden-api-whitelisted-app package="com.android.apps.tag" />
   <hidden-api-whitelisted-app package="com.android.backupconfirm" />
+  <hidden-api-whitelisted-app package="com.android.basicsmsreceiver" />
   <hidden-api-whitelisted-app package="com.android.bluetooth" />
   <hidden-api-whitelisted-app package="com.android.bluetoothdebug" />
   <hidden-api-whitelisted-app package="com.android.bluetoothmidiservice" />
+  <hidden-api-whitelisted-app package="com.android.bookmarkprovider" />
   <hidden-api-whitelisted-app package="com.android.calllogbackup" />
+  <hidden-api-whitelisted-app package="com.android.camera" />
   <hidden-api-whitelisted-app package="com.android.captiveportallogin" />
   <hidden-api-whitelisted-app package="com.android.car" />
+  <hidden-api-whitelisted-app package="com.android.car.dialer" />
   <hidden-api-whitelisted-app package="com.android.car.hvac" />
   <hidden-api-whitelisted-app package="com.android.car.mapsplaceholder" />
   <hidden-api-whitelisted-app package="com.android.car.media" />
   <hidden-api-whitelisted-app package="com.android.car.media.localmediaplayer" />
+  <hidden-api-whitelisted-app package="com.android.car.messenger" />
+  <hidden-api-whitelisted-app package="com.android.car.overview" />
   <hidden-api-whitelisted-app package="com.android.car.radio" />
   <hidden-api-whitelisted-app package="com.android.car.settings" />
+  <hidden-api-whitelisted-app package="com.android.car.stream" />
   <hidden-api-whitelisted-app package="com.android.car.systemupdater" />
   <hidden-api-whitelisted-app package="com.android.car.trust" />
   <hidden-api-whitelisted-app package="com.android.carrierconfig" />
   <hidden-api-whitelisted-app package="com.android.carrierdefaultapp" />
   <hidden-api-whitelisted-app package="com.android.cellbroadcastreceiver" />
   <hidden-api-whitelisted-app package="com.android.certinstaller" />
+  <hidden-api-whitelisted-app package="com.android.companiondevicemanager" />
   <hidden-api-whitelisted-app package="com.android.customlocale2" />
   <hidden-api-whitelisted-app package="com.android.defcontainer" />
   <hidden-api-whitelisted-app package="com.android.documentsui" />
+  <hidden-api-whitelisted-app package="com.android.dreams.basic" />
   <hidden-api-whitelisted-app package="com.android.egg" />
-  <hidden-api-whitelisted-app package="com.android.email.policy" />
   <hidden-api-whitelisted-app package="com.android.emergency" />
   <hidden-api-whitelisted-app package="com.android.externalstorage" />
   <hidden-api-whitelisted-app package="com.android.fakeoemfeatures" />
   <hidden-api-whitelisted-app package="com.android.gallery" />
   <hidden-api-whitelisted-app package="com.android.hotspot2" />
-  <hidden-api-whitelisted-app package="com.android.inputdevices" />
   <hidden-api-whitelisted-app package="com.android.keychain" />
   <hidden-api-whitelisted-app package="com.android.location.fused" />
   <hidden-api-whitelisted-app package="com.android.managedprovisioning" />
   <hidden-api-whitelisted-app package="com.android.mms.service" />
   <hidden-api-whitelisted-app package="com.android.mtp" />
+  <hidden-api-whitelisted-app package="com.android.musicfx" />
   <hidden-api-whitelisted-app package="com.android.nfc" />
   <hidden-api-whitelisted-app package="com.android.osu" />
   <hidden-api-whitelisted-app package="com.android.packageinstaller" />
@@ -70,12 +78,14 @@
   <hidden-api-whitelisted-app package="com.android.printservice.recommendation" />
   <hidden-api-whitelisted-app package="com.android.printspooler" />
   <hidden-api-whitelisted-app package="com.android.providers.blockednumber" />
+  <hidden-api-whitelisted-app package="com.android.providers.calendar" />
   <hidden-api-whitelisted-app package="com.android.providers.contacts" />
   <hidden-api-whitelisted-app package="com.android.providers.downloads" />
   <hidden-api-whitelisted-app package="com.android.providers.downloads.ui" />
   <hidden-api-whitelisted-app package="com.android.providers.media" />
   <hidden-api-whitelisted-app package="com.android.providers.settings" />
   <hidden-api-whitelisted-app package="com.android.providers.telephony" />
+  <hidden-api-whitelisted-app package="com.android.providers.tv" />
   <hidden-api-whitelisted-app package="com.android.providers.userdictionary" />
   <hidden-api-whitelisted-app package="com.android.provision" />
   <hidden-api-whitelisted-app package="com.android.proxyhandler" />
@@ -87,10 +97,15 @@
   <hidden-api-whitelisted-app package="com.android.settings" />
   <hidden-api-whitelisted-app package="com.android.sharedstoragebackup" />
   <hidden-api-whitelisted-app package="com.android.shell" />
+  <hidden-api-whitelisted-app package="com.android.smspush" />
+  <hidden-api-whitelisted-app package="com.android.spare_parts" />
+  <hidden-api-whitelisted-app package="com.android.statementservice" />
   <hidden-api-whitelisted-app package="com.android.stk" />
+  <hidden-api-whitelisted-app package="com.android.storagemanager" />
   <hidden-api-whitelisted-app package="com.android.support.car.lenspicker" />
   <hidden-api-whitelisted-app package="com.android.systemui" />
-  <hidden-api-whitelisted-app package="com.android.systemui.theme.dark" />
+  <hidden-api-whitelisted-app package="com.android.systemui.plugins" />
+  <hidden-api-whitelisted-app package="com.android.terminal" />
   <hidden-api-whitelisted-app package="com.android.timezone.updater" />
   <hidden-api-whitelisted-app package="com.android.traceur" />
   <hidden-api-whitelisted-app package="com.android.tv.settings" />
@@ -99,5 +114,5 @@
   <hidden-api-whitelisted-app package="com.android.wallpaperbackup" />
   <hidden-api-whitelisted-app package="com.android.wallpapercropper" />
   <hidden-api-whitelisted-app package="com.googlecode.android_scripting" />
+  <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
 </config>
-
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index b455419..c6a6113 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -325,6 +325,7 @@
         <permission name="android.permission.SET_TIME"/>
         <permission name="android.permission.SET_TIME_ZONE"/>
         <permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES"/>
+        <permission name="android.permission.START_TASKS_FROM_RECENTS" />
         <permission name="android.permission.STOP_APP_SWITCHES"/>
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index c0f4920..a47ecf5 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -292,8 +292,7 @@
         mState = new State(nCreate(nativeImageDecoder, decoder, width, height, cropRect),
                 inputStream, afd);
 
-        // FIXME: Use the right size for the native allocation.
-        long nativeSize = 200;
+        final long nativeSize = nNativeByteSize(mState.mNativePtr);
         NativeAllocationRegistry registry = new NativeAllocationRegistry(
                 AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
         registry.registerNativeAllocation(mState, mState.mNativePtr);
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index cf29e43..81a7980 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -38,16 +38,27 @@
 namespace uirenderer {
 
 struct Comparison {
+    JankType type;
+    std::function<int64_t(nsecs_t)> computeThreadshold;
     FrameInfoIndex start;
     FrameInfoIndex end;
 };
 
-static const Comparison COMPARISONS[] = {
-        {FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync},
-        {FrameInfoIndex::OldestInputEvent, FrameInfoIndex::Vsync},
-        {FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},
-        {FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},
-        {FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
+static const std::array<Comparison, 4> COMPARISONS{
+        Comparison{JankType::kMissedVsync, [](nsecs_t) { return 1; }, FrameInfoIndex::IntendedVsync,
+                   FrameInfoIndex::Vsync},
+
+        Comparison{JankType::kSlowUI,
+                   [](nsecs_t frameInterval) { return static_cast<int64_t>(.5 * frameInterval); },
+                   FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},
+
+        Comparison{JankType::kSlowSync,
+                   [](nsecs_t frameInterval) { return static_cast<int64_t>(.2 * frameInterval); },
+                   FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},
+
+        Comparison{JankType::kSlowRT,
+                   [](nsecs_t frameInterval) { return static_cast<int64_t>(.75 * frameInterval); },
+                   FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
 };
 
 // If the event exceeds 10 seconds throw it away, this isn't a jank event
@@ -91,24 +102,10 @@
 
 void JankTracker::setFrameInterval(nsecs_t frameInterval) {
     mFrameInterval = frameInterval;
-    mThresholds[kMissedVsync] = 1;
-    /*
-     * Due to interpolation and sample rate differences between the touch
-     * panel and the display (example, 85hz touch panel driving a 60hz display)
-     * we call high latency 1.5 * frameinterval
-     *
-     * NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel
-     * on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms
-     * Thus this must always be larger than frameInterval, or it will fail
-     */
-    mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval);
 
-    // Note that these do not add up to 1. This is intentional. It's to deal
-    // with variance in values, and should be sort of an upper-bound on what
-    // is reasonable to expect.
-    mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval);
-    mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval);
-    mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval);
+    for (auto& comparison : COMPARISONS) {
+        mThresholds[comparison.type] = comparison.computeThreadshold(frameInterval);
+    }
 }
 
 void JankTracker::finishFrame(const FrameInfo& frame) {
@@ -129,28 +126,48 @@
             totalDuration -= forgiveAmount;
         }
     }
+
     LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration);
     mData->reportFrame(totalDuration);
     (*mGlobalData)->reportFrame(totalDuration);
 
-    // Keep the fast path as fast as possible.
-    if (CC_LIKELY(totalDuration < mFrameInterval)) {
-        return;
-    }
-
     // Only things like Surface.lockHardwareCanvas() are exempt from tracking
-    if (frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS) {
+    if (CC_UNLIKELY(frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS)) {
         return;
     }
 
-    mData->reportJank();
-    (*mGlobalData)->reportJank();
+    if (totalDuration > mFrameInterval) {
+        mData->reportJank();
+        (*mGlobalData)->reportJank();
+    }
 
-    for (int i = 0; i < NUM_BUCKETS; i++) {
-        int64_t delta = frame.duration(COMPARISONS[i].start, COMPARISONS[i].end);
-        if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
-            mData->reportJankType((JankType)i);
-            (*mGlobalData)->reportJankType((JankType)i);
+    bool isTripleBuffered = mSwapDeadline > frame[FrameInfoIndex::IntendedVsync];
+
+    mSwapDeadline = std::max(mSwapDeadline + mFrameInterval,
+                             frame[FrameInfoIndex::IntendedVsync] + mFrameInterval);
+
+    // If we hit the deadline, cool!
+    if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline) {
+        if (isTripleBuffered) {
+            mData->reportJankType(JankType::kHighInputLatency);
+            (*mGlobalData)->reportJankType(JankType::kHighInputLatency);
+        }
+        return;
+    }
+
+    mData->reportJankType(JankType::kMissedDeadline);
+    (*mGlobalData)->reportJankType(JankType::kMissedDeadline);
+
+    // Janked, reset the swap deadline
+    nsecs_t jitterNanos = frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::Vsync];
+    nsecs_t lastFrameOffset = jitterNanos % mFrameInterval;
+    mSwapDeadline = frame[FrameInfoIndex::FrameCompleted] - lastFrameOffset + mFrameInterval;
+
+    for (auto& comparison : COMPARISONS) {
+        int64_t delta = frame.duration(comparison.start, comparison.end);
+        if (delta >= mThresholds[comparison.type] && delta < IGNORE_EXCEEDING) {
+            mData->reportJankType(comparison.type);
+            (*mGlobalData)->reportJankType(comparison.type);
         }
     }
 
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index dc6a7ff..110211e 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -75,6 +75,7 @@
 
     std::array<int64_t, NUM_BUCKETS> mThresholds;
     int64_t mFrameInterval;
+    nsecs_t mSwapDeadline;
     // The amount of time we will erase from the total duration to account
     // for SF vsync offsets with HWC2 blocking dequeueBuffers.
     // (Vsync + mDequeueBlockTolerance) is the point at which we expect
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index b392ecd..f9cf549 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -23,8 +23,7 @@
 
 static const char* JANK_TYPE_NAMES[] = {
         "Missed Vsync",        "High input latency",       "Slow UI thread",
-        "Slow bitmap uploads", "Slow issue draw commands",
-};
+        "Slow bitmap uploads", "Slow issue draw commands", "Frame deadline missed"};
 
 // The bucketing algorithm controls so to speak
 // If a frame is <= to this it goes in bucket 0
diff --git a/libs/hwui/ProfileData.h b/libs/hwui/ProfileData.h
index 1e688ab..564920b 100644
--- a/libs/hwui/ProfileData.h
+++ b/libs/hwui/ProfileData.h
@@ -33,6 +33,7 @@
     kSlowUI,
     kSlowSync,
     kSlowRT,
+    kMissedDeadline,
 
     // must be last
     NUM_BUCKETS,
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 28d0bc4..c529f87 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -26,8 +26,8 @@
 
 namespace android {
 
-AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage)
-        : mSkAnimatedImage(std::move(animatedImage)) {
+AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed)
+        : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) {
     mTimeToShowNextSnapshot = mSkAnimatedImage->currentFrameDuration();
 }
 
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index f4e2ba7..a92b62d 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -45,7 +45,9 @@
  */
 class ANDROID_API AnimatedImageDrawable : public SkDrawable {
 public:
-    AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage);
+    // bytesUsed includes the approximate sizes of the SkAnimatedImage and the SkPictures in the
+    // Snapshots.
+    AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed);
 
     /**
      * This updates the internal time and returns true if the animation needs
@@ -100,11 +102,17 @@
     Snapshot decodeNextFrame();
     Snapshot reset();
 
+    size_t byteSize() const {
+        return sizeof(this) + mBytesUsed;
+    }
+
 protected:
     virtual void onDraw(SkCanvas* canvas) override;
 
 private:
     sk_sp<SkAnimatedImage> mSkAnimatedImage;
+    const size_t mBytesUsed;
+
     bool mRunning = false;
     bool mStarting = false;
 
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index e0303a8..599226b 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -176,6 +176,8 @@
     summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
                                           data->jankTypeCount(kSlowSync));
     summary->set_slow_draw_count(summary->slow_draw_count() + data->jankTypeCount(kSlowRT));
+    summary->set_missed_deadline_count(summary->missed_deadline_count()
+            + data->jankTypeCount(kMissedDeadline));
 
     bool creatingHistogram = false;
     if (proto->histogram_size() == 0) {
@@ -246,6 +248,7 @@
     dprintf(fd, "\nNumber Slow UI thread: %d", summary.slow_ui_thread_count());
     dprintf(fd, "\nNumber Slow bitmap uploads: %d", summary.slow_bitmap_upload_count());
     dprintf(fd, "\nNumber Slow issue draw commands: %d", summary.slow_draw_count());
+    dprintf(fd, "\nNumber Frame deadline missed: %d", summary.missed_deadline_count());
     dprintf(fd, "\nHISTOGRAM:");
     for (const auto& it : proto->histogram()) {
         dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count());
diff --git a/libs/hwui/tests/common/scenes/JankyScene.cpp b/libs/hwui/tests/common/scenes/JankyScene.cpp
new file mode 100644
index 0000000..f5e6b31
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/JankyScene.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#include "TestSceneBase.h"
+
+#include <unistd.h>
+
+class JankyScene;
+
+static TestScene::Registrar _JankyScene(TestScene::Info{
+        "janky",
+        "A scene that intentionally janks just enough to stay in "
+        "triple buffering.",
+        TestScene::simpleCreateScene<JankyScene>});
+
+class JankyScene : public TestScene {
+public:
+    sp<RenderNode> card;
+
+    void createContent(int width, int height, Canvas& canvas) override {
+        card = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) {
+            canvas.drawColor(0xFF0000FF, SkBlendMode::kSrcOver);
+        });
+        canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);  // background
+        canvas.drawRenderNode(card.get());
+    }
+
+    void doFrame(int frameNr) override {
+        int curFrame = frameNr % 150;
+        if (curFrame & 1) {
+            usleep(15000);
+        }
+        // we animate left and top coordinates, which in turn animates width and
+        // height (bottom/right coordinates are fixed)
+        card->mutateStagingProperties().setLeftTop(curFrame, curFrame);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+    }
+};
\ No newline at end of file
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 6abba95..96a0817 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -339,7 +339,8 @@
      * substantially restrict power.
      *
      * <p>In this mode, the GNSS chipset will not, on average, run power hungry operations like RF &
-     * signal searches for more than one second per interval {@link #mInterval}
+     * signal searches for more than one second per interval (specified by
+     * {@link #setInterval(long)}).
      *
      * @param enabled Enable or disable low power mode
      * @return the same object, so that setters can be chained
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index b50aa47..3bfbcc2 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -105,10 +105,10 @@
  * <tr><td>{@link #KEY_HEIGHT}</td><td>Integer</td><td></td></tr>
  * <tr><td>{@link #KEY_COLOR_FORMAT}</td><td>Integer</td><td>set by the user
  *         for encoders, readable in the output format of decoders</b></td></tr>
- * <tr><td>{@link #KEY_GRID_WIDTH}</td><td>Integer</td><td>required if the image has grid</td></tr>
- * <tr><td>{@link #KEY_GRID_HEIGHT}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_TILE_WIDTH}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_TILE_HEIGHT}</td><td>Integer</td><td>required if the image has grid</td></tr>
  * <tr><td>{@link #KEY_GRID_ROWS}</td><td>Integer</td><td>required if the image has grid</td></tr>
- * <tr><td>{@link #KEY_GRID_COLS}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_GRID_COLUMNS}</td><td>Integer</td><td>required if the image has grid</td></tr>
  * </table>
  */
 public final class MediaFormat {
@@ -150,17 +150,17 @@
      * The track's MediaFormat will come with {@link #KEY_WIDTH} and
      * {@link #KEY_HEIGHT} keys, which describes the width and height
      * of the image. If the image doesn't contain grid (i.e. none of
-     * {@link #KEY_GRID_WIDTH}, {@link #KEY_GRID_HEIGHT},
-     * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLS} are present}), the
+     * {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT},
+     * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present}), the
      * track will contain a single sample of coded data for the entire image,
      * and the image width and height should be used to set up the decoder.
      *
      * If the image does come with grid, each sample from the track will
      * contain one tile in the grid, of which the size is described by
-     * {@link #KEY_GRID_WIDTH} and {@link #KEY_GRID_HEIGHT}. This size
+     * {@link #KEY_TILE_WIDTH} and {@link #KEY_TILE_HEIGHT}. This size
      * (instead of {@link #KEY_WIDTH} and {@link #KEY_HEIGHT}) should be
      * used to set up the decoder. The track contains {@link #KEY_GRID_ROWS}
-     * by {@link #KEY_GRID_COLS} samples in row-major, top-row first,
+     * by {@link #KEY_GRID_COLUMNS} samples in row-major, top-row first,
      * left-to-right order. The output image should be reconstructed by
      * first tiling the decoding results of the tiles in the correct order,
      * then trimming (before rotation is applied) on the bottom and right
@@ -275,28 +275,28 @@
     public static final String KEY_FRAME_RATE = "frame-rate";
 
     /**
-     * A key describing the grid width of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC}
-     * track. The associated value is an integer.
+     * A key describing the width (in pixels) of each tile of the content in a
+     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_HEIGHT
+     * @see #KEY_TILE_HEIGHT
      * @see #KEY_GRID_ROWS
-     * @see #KEY_GRID_COLS
+     * @see #KEY_GRID_COLUMNS
      */
-    public static final String KEY_GRID_WIDTH = "grid-width";
+    public static final String KEY_TILE_WIDTH = "tile-width";
 
     /**
-     * A key describing the grid height of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC}
-     * track. The associated value is an integer.
+     * A key describing the height (in pixels) of each tile of the content in a
+     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_WIDTH
+     * @see #KEY_TILE_WIDTH
      * @see #KEY_GRID_ROWS
-     * @see #KEY_GRID_COLS
+     * @see #KEY_GRID_COLUMNS
      */
-    public static final String KEY_GRID_HEIGHT = "grid-height";
+    public static final String KEY_TILE_HEIGHT = "tile-height";
 
     /**
      * A key describing the number of grid rows in the content in a
@@ -304,9 +304,9 @@
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_WIDTH
-     * @see #KEY_GRID_HEIGHT
-     * @see #KEY_GRID_COLS
+     * @see #KEY_TILE_WIDTH
+     * @see #KEY_TILE_HEIGHT
+     * @see #KEY_GRID_COLUMNS
      */
     public static final String KEY_GRID_ROWS = "grid-rows";
 
@@ -316,11 +316,11 @@
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_WIDTH
-     * @see #KEY_GRID_HEIGHT
+     * @see #KEY_TILE_WIDTH
+     * @see #KEY_TILE_HEIGHT
      * @see #KEY_GRID_ROWS
      */
-    public static final String KEY_GRID_COLS = "grid-cols";
+    public static final String KEY_GRID_COLUMNS = "grid-cols";
 
     /**
      * A key describing the raw audio sample encoding/format.
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 1aeed6d..0955dd6 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -427,20 +427,40 @@
      *        a valid frame. The total number of frames available for retrieval can be queried
      *        via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
      * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
-     *        If null, default config will be chosen.
      *
      * @throws IllegalStateException if the container doesn't contain video or image sequences.
      * @throws IllegalArgumentException if the requested frame index does not exist.
      *
      * @return A Bitmap containing the requested video frame, or null if the retrieval fails.
      *
+     * @see #getFrameAtIndex(int)
      * @see #getFramesAtIndex(int, int, BitmapParams)
+     * @see #getFramesAtIndex(int, int)
      */
-    public Bitmap getFrameAtIndex(int frameIndex, @Nullable BitmapParams params) {
+    public Bitmap getFrameAtIndex(int frameIndex, @NonNull BitmapParams params) {
         List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1, params);
-        if (bitmaps == null || bitmaps.size() < 1) {
-            return null;
-        }
+        return bitmaps.get(0);
+    }
+
+    /**
+     * This method is similar to {@link #getFrameAtIndex(int, BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
+     * @param frameIndex 0-based index of the video frame. The frame index must be that of
+     *        a valid frame. The total number of frames available for retrieval can be queried
+     *        via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
+     *
+     * @throws IllegalStateException if the container doesn't contain video or image sequences.
+     * @throws IllegalArgumentException if the requested frame index does not exist.
+     *
+     * @return A Bitmap containing the requested video frame, or null if the retrieval fails.
+     *
+     * @see #getFrameAtIndex(int, BitmapParams)
+     * @see #getFramesAtIndex(int, int, BitmapParams)
+     * @see #getFramesAtIndex(int, int)
+     */
+    public Bitmap getFrameAtIndex(int frameIndex) {
+        List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1);
         return bitmaps.get(0);
     }
 
@@ -461,7 +481,6 @@
      * @param numFrames number of consecutive video frames to retrieve. Must be a positive
      *        value. The stream must contain at least numFrames frames starting at frameIndex.
      * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
-     *        If null, default config will be chosen.
      *
      * @throws IllegalStateException if the container doesn't contain video or image sequences.
      * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the
@@ -471,8 +490,40 @@
      *         array could contain less frames than requested if the retrieval fails.
      *
      * @see #getFrameAtIndex(int, BitmapParams)
+     * @see #getFrameAtIndex(int)
+     * @see #getFramesAtIndex(int, int)
      */
-    public List<Bitmap> getFramesAtIndex(
+    public @NonNull List<Bitmap> getFramesAtIndex(
+            int frameIndex, int numFrames, @NonNull BitmapParams params) {
+        return getFramesAtIndexInternal(frameIndex, numFrames, params);
+    }
+
+    /**
+     * This method is similar to {@link #getFramesAtIndex(int, int, BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
+     * @param frameIndex 0-based index of the first video frame to retrieve. The frame index
+     *        must be that of a valid frame. The total number of frames available for retrieval
+     *        can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
+     * @param numFrames number of consecutive video frames to retrieve. Must be a positive
+     *        value. The stream must contain at least numFrames frames starting at frameIndex.
+     *
+     * @throws IllegalStateException if the container doesn't contain video or image sequences.
+     * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the
+     *         stream doesn't contain at least numFrames starting at frameIndex.
+
+     * @return An list of Bitmaps containing the requested video frames. The returned
+     *         array could contain less frames than requested if the retrieval fails.
+     *
+     * @see #getFrameAtIndex(int, BitmapParams)
+     * @see #getFrameAtIndex(int)
+     * @see #getFramesAtIndex(int, int, BitmapParams)
+     */
+    public @NonNull List<Bitmap> getFramesAtIndex(int frameIndex, int numFrames) {
+        return getFramesAtIndexInternal(frameIndex, numFrames, null);
+    }
+
+    private @NonNull List<Bitmap> getFramesAtIndexInternal(
             int frameIndex, int numFrames, @Nullable BitmapParams params) {
         if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) {
             throw new IllegalStateException("Does not contail video or image sequences");
@@ -487,7 +538,8 @@
         }
         return _getFrameAtIndex(frameIndex, numFrames, params);
     }
-    private native List<Bitmap> _getFrameAtIndex(
+
+    private native @NonNull List<Bitmap> _getFrameAtIndex(
             int frameIndex, int numFrames, @Nullable BitmapParams params);
 
     /**
@@ -498,29 +550,39 @@
      * used to create the bitmap from the {@code BitmapParams} argument, for instance
      * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}.
      *
-     * @param imageIndex 0-based index of the image, with negative value indicating
-     *        the primary image.
+     * @param imageIndex 0-based index of the image.
      * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
-     *        If null, default config will be chosen.
      *
      * @throws IllegalStateException if the container doesn't contain still images.
      * @throws IllegalArgumentException if the requested image does not exist.
      *
      * @return the requested still image, or null if the image cannot be retrieved.
      *
+     * @see #getImageAtIndex(int)
      * @see #getPrimaryImage(BitmapParams)
+     * @see #getPrimaryImage()
      */
-    public Bitmap getImageAtIndex(int imageIndex, @Nullable BitmapParams params) {
-        if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) {
-            throw new IllegalStateException("Does not contail still images");
-        }
+    public Bitmap getImageAtIndex(int imageIndex, @NonNull BitmapParams params) {
+        return getImageAtIndexInternal(imageIndex, params);
+    }
 
-        String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT);
-        if (imageIndex >= Integer.parseInt(imageCount)) {
-            throw new IllegalArgumentException("Invalid image index: " + imageCount);
-        }
-
-        return _getImageAtIndex(imageIndex, params);
+    /**
+     * This method is similar to {@link #getImageAtIndex(int, BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
+     * @param imageIndex 0-based index of the image.
+     *
+     * @throws IllegalStateException if the container doesn't contain still images.
+     * @throws IllegalArgumentException if the requested image does not exist.
+     *
+     * @return the requested still image, or null if the image cannot be retrieved.
+     *
+     * @see #getImageAtIndex(int, BitmapParams)
+     * @see #getPrimaryImage(BitmapParams)
+     * @see #getPrimaryImage()
+     */
+    public Bitmap getImageAtIndex(int imageIndex) {
+        return getImageAtIndexInternal(imageIndex, null);
     }
 
     /**
@@ -532,16 +594,46 @@
      * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}.
      *
      * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
-     *        If null, default config will be chosen.
      *
      * @return the primary image, or null if it cannot be retrieved.
      *
      * @throws IllegalStateException if the container doesn't contain still images.
      *
      * @see #getImageAtIndex(int, BitmapParams)
+     * @see #getImageAtIndex(int)
+     * @see #getPrimaryImage()
      */
-    public Bitmap getPrimaryImage(@Nullable BitmapParams params) {
-        return getImageAtIndex(-1, params);
+    public Bitmap getPrimaryImage(@NonNull BitmapParams params) {
+        return getImageAtIndexInternal(-1, params);
+    }
+
+    /**
+     * This method is similar to {@link #getPrimaryImage(BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
+     * @return the primary image, or null if it cannot be retrieved.
+     *
+     * @throws IllegalStateException if the container doesn't contain still images.
+     *
+     * @see #getImageAtIndex(int, BitmapParams)
+     * @see #getImageAtIndex(int)
+     * @see #getPrimaryImage(BitmapParams)
+     */
+    public Bitmap getPrimaryImage() {
+        return getImageAtIndexInternal(-1, null);
+    }
+
+    private Bitmap getImageAtIndexInternal(int imageIndex, @Nullable BitmapParams params) {
+        if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) {
+            throw new IllegalStateException("Does not contail still images");
+        }
+
+        String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT);
+        if (imageIndex >= Integer.parseInt(imageCount)) {
+            throw new IllegalArgumentException("Invalid image index: " + imageCount);
+        }
+
+        return _getImageAtIndex(imageIndex, params);
     }
 
     private native Bitmap _getImageAtIndex(int imageIndex, @Nullable BitmapParams params);
@@ -788,7 +880,8 @@
     public static final int METADATA_KEY_IMAGE_HEIGHT    = 30;
     /**
      * If the media contains still images, this key retrieves the rotation
-     * of the primary image.
+     * angle (in degrees clockwise) of the primary image. The image rotation
+     * angle must be one of 0, 90, 180, or 270 degrees.
      */
     public static final int METADATA_KEY_IMAGE_ROTATION  = 31;
     /**
diff --git a/media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl b/media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl
new file mode 100644
index 0000000..ef4bb01
--- /dev/null
+++ b/media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.soundtrigger;
+
+import android.media.soundtrigger.ISoundTriggerDetectionServiceClient;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.os.Bundle;
+import android.os.ParcelUuid;
+
+/**
+ * AIDL for the SoundTriggerDetectionService to run detection operations when triggered.
+ *
+ * {@hide}
+ */
+oneway interface ISoundTriggerDetectionService {
+    void setClient(in ParcelUuid uuid, in Bundle params, in ISoundTriggerDetectionServiceClient client);
+    void removeClient(in ParcelUuid uuid);
+    void onGenericRecognitionEvent(in ParcelUuid uuid, int opId, in SoundTrigger.GenericRecognitionEvent event);
+    void onError(in ParcelUuid uuid, int opId, int status);
+    void onStopOperation(in ParcelUuid uuid, int opId);
+}
diff --git a/wifi/java/android/net/wifi/ScanSettings.aidl b/media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl
similarity index 61%
rename from wifi/java/android/net/wifi/ScanSettings.aidl
rename to media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl
index ebd2a39..230735a 100644
--- a/wifi/java/android/net/wifi/ScanSettings.aidl
+++ b/media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,6 +14,13 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.media.soundtrigger;
 
-parcelable ScanSettings;
+/**
+ * AIDL for the callbacks from a ISoundTriggerDetectionService.
+ *
+ * {@hide}
+ */
+oneway interface ISoundTriggerDetectionServiceClient {
+    void onOpFinished(int opId);
+}
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetectionService.java b/media/java/android/media/soundtrigger/SoundTriggerDetectionService.java
new file mode 100644
index 0000000..7381d97
--- /dev/null
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetectionService.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.soundtrigger;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.UUID;
+
+/**
+ * A service that allows interaction with the actual sound trigger detection on the system.
+ *
+ * <p> Sound trigger detection refers to detectors that match generic sound patterns that are
+ * not voice-based. The voice-based recognition models should utilize the {@link
+ * android.service.voice.VoiceInteractionService} instead. Access to this class needs to be
+ * protected by the {@value android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE}
+ * permission granted only to the system.
+ *
+ * <p>This service has to be explicitly started by an app, the system does not scan for and start
+ * these services.
+ *
+ * <p>If an operation ({@link #onGenericRecognitionEvent}, {@link #onError},
+ * {@link #onRecognitionPaused}, {@link #onRecognitionResumed}) is triggered the service is
+ * considered as running in the foreground. Once the operation is processed the service should call
+ * {@link #operationFinished(UUID, int)}. If this does not happen in
+ * {@link SoundTriggerManager#getDetectionServiceOperationsTimeout()} milliseconds
+ * {@link #onStopOperation(UUID, Bundle, int)} is called and the service is unbound.
+ *
+ * <p>The total amount of operations per day might be limited.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class SoundTriggerDetectionService extends Service {
+    private static final String LOG_TAG = SoundTriggerDetectionService.class.getSimpleName();
+
+    private static final boolean DEBUG = false;
+
+    private final Object mLock = new Object();
+
+    /**
+     * Client indexed by model uuid. This is needed for the {@link #operationFinished(UUID, int)}
+     * callbacks.
+     */
+    @GuardedBy("mLock")
+    private final ArrayMap<UUID, ISoundTriggerDetectionServiceClient> mClients =
+            new ArrayMap<>();
+
+    private Handler mHandler;
+
+    /**
+     * @hide
+     */
+    @Override
+    protected final void attachBaseContext(Context base) {
+        super.attachBaseContext(base);
+        mHandler = new Handler(base.getMainLooper());
+    }
+
+    private void setClient(@NonNull UUID uuid, @Nullable Bundle params,
+            @NonNull ISoundTriggerDetectionServiceClient client) {
+        if (DEBUG) Log.i(LOG_TAG, uuid + ": handle setClient");
+
+        synchronized (mLock) {
+            mClients.put(uuid, client);
+        }
+        onConnected(uuid, params);
+    }
+
+    private void removeClient(@NonNull UUID uuid, @Nullable Bundle params) {
+        if (DEBUG) Log.i(LOG_TAG, uuid + ": handle removeClient");
+
+        synchronized (mLock) {
+            mClients.remove(uuid);
+        }
+        onDisconnected(uuid, params);
+    }
+
+    /**
+     * The system has connected to this service for the recognition registered for the model
+     * {@code uuid}.
+     *
+     * <p> This is called before any operations are delivered.
+     *
+     * @param uuid   The {@code uuid} of the model the recognitions is registered for
+     * @param params The {@code params} passed when the recognition was started
+     */
+    @MainThread
+    public void onConnected(@NonNull UUID uuid, @Nullable Bundle params) {
+        /* do nothing */
+    }
+
+    /**
+     * The system has disconnected from this service for the recognition registered for the model
+     * {@code uuid}.
+     *
+     * <p>Once this is called {@link #operationFinished} cannot be called anymore for
+     * {@code uuid}.
+     *
+     * <p> {@link #onConnected(UUID, Bundle)} is called before any further operations are delivered.
+     *
+     * @param uuid   The {@code uuid} of the model the recognitions is registered for
+     * @param params The {@code params} passed when the recognition was started
+     */
+    @MainThread
+    public void onDisconnected(@NonNull UUID uuid, @Nullable Bundle params) {
+        /* do nothing */
+    }
+
+    /**
+     * A new generic sound trigger event has been detected.
+     *
+     * @param uuid   The {@code uuid} of the model the recognition is registered for
+     * @param params The {@code params} passed when the recognition was started
+     * @param opId The id of this operation. Once the operation is done, this service needs to call
+     *             {@link #operationFinished(UUID, int)}
+     * @param event The event that has been detected
+     */
+    @MainThread
+    public void onGenericRecognitionEvent(@NonNull UUID uuid, @Nullable Bundle params, int opId,
+            @NonNull SoundTrigger.RecognitionEvent event) {
+        operationFinished(uuid, opId);
+    }
+
+    /**
+     * A error has been detected.
+     *
+     * @param uuid   The {@code uuid} of the model the recognition is registered for
+     * @param params The {@code params} passed when the recognition was started
+     * @param opId The id of this operation. Once the operation is done, this service needs to call
+     *             {@link #operationFinished(UUID, int)}
+     * @param status The error code detected
+     */
+    @MainThread
+    public void onError(@NonNull UUID uuid, @Nullable Bundle params, int opId, int status) {
+        operationFinished(uuid, opId);
+    }
+
+    /**
+     * An operation took too long and should be stopped.
+     *
+     * @param uuid   The {@code uuid} of the model the recognition is registered for
+     * @param params The {@code params} passed when the recognition was started
+     * @param opId The id of the operation that took too long
+     */
+    @MainThread
+    public abstract void onStopOperation(@NonNull UUID uuid, @Nullable Bundle params, int opId);
+
+    /**
+     * Tell that the system that an operation has been fully processed.
+     *
+     * @param uuid The {@code uuid} of the model the recognition is registered for
+     * @param opId The id of the operation that is processed
+     */
+    public final void operationFinished(@Nullable UUID uuid, int opId) {
+        try {
+            ISoundTriggerDetectionServiceClient client;
+            synchronized (mLock) {
+                client = mClients.get(uuid);
+
+                if (client == null) {
+                    throw new IllegalStateException("operationFinished called, but no client for "
+                            + uuid + ". Was this called after onDisconnected?");
+                }
+            }
+            client.onOpFinished(opId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IBinder onBind(Intent intent) {
+        return new ISoundTriggerDetectionService.Stub() {
+            private final Object mBinderLock = new Object();
+
+            /** Cached params bundles indexed by the model uuid */
+            @GuardedBy("mBinderLock")
+            public final ArrayMap<UUID, Bundle> mParams = new ArrayMap<>();
+
+            @Override
+            public void setClient(ParcelUuid puuid, Bundle params,
+                    ISoundTriggerDetectionServiceClient client) {
+                UUID uuid = puuid.getUuid();
+                synchronized (mBinderLock) {
+                    mParams.put(uuid, params);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + ": setClient(" + params + ")");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::setClient,
+                        SoundTriggerDetectionService.this, uuid, params, client));
+            }
+
+            @Override
+            public void removeClient(ParcelUuid puuid) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.remove(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + ": removeClient");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::removeClient,
+                        SoundTriggerDetectionService.this, uuid, params));
+            }
+
+            @Override
+            public void onGenericRecognitionEvent(ParcelUuid puuid, int opId,
+                    SoundTrigger.GenericRecognitionEvent event) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.get(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onGenericRecognitionEvent");
+                mHandler.sendMessage(
+                        obtainMessage(SoundTriggerDetectionService::onGenericRecognitionEvent,
+                                SoundTriggerDetectionService.this, uuid, params, opId, event));
+            }
+
+            @Override
+            public void onError(ParcelUuid puuid, int opId, int status) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.get(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onError(" + status + ")");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::onError,
+                        SoundTriggerDetectionService.this, uuid, params, opId, status));
+            }
+
+            @Override
+            public void onStopOperation(ParcelUuid puuid, int opId) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.get(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onStopOperation");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::onStopOperation,
+                        SoundTriggerDetectionService.this, uuid, params, opId));
+            }
+        };
+    }
+
+    @CallSuper
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mClients.clear();
+
+        return false;
+    }
+}
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 92ffae0..c9ec752 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -15,26 +15,31 @@
  */
 
 package android.media.soundtrigger;
+
 import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
 
-import android.app.PendingIntent;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.hardware.soundtrigger.SoundTrigger;
-import android.hardware.soundtrigger.SoundTrigger.SoundModel;
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.hardware.soundtrigger.SoundTrigger.SoundModel;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
+import android.provider.Settings;
 import android.util.Slog;
 
 import com.android.internal.app.ISoundTriggerService;
+import com.android.internal.util.Preconditions;
 
 import java.util.HashMap;
 import java.util.UUID;
@@ -276,6 +281,40 @@
     }
 
     /**
+     * Starts recognition for the given model id. All events from the model will be sent to the
+     * service.
+     *
+     * <p>This only supports generic sound trigger events. For keyphrase events, please use
+     * {@link android.service.voice.VoiceInteractionService}.
+     *
+     * @param soundModelId Id of the sound model
+     * @param params Opaque data sent to each service call of the service as the {@code params}
+     *               argument
+     * @param detectionService The component name of the service that should receive the events.
+     *                         Needs to subclass {@link SoundTriggerDetectionService}
+     * @param config Configures the recognition
+     *
+     * @return {@link SoundTrigger#STATUS_OK} if the recognition could be started, error code
+     *         otherwise
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
+    public int startRecognition(@NonNull UUID soundModelId, @Nullable Bundle params,
+        @NonNull ComponentName detectionService, @NonNull RecognitionConfig config) {
+        Preconditions.checkNotNull(soundModelId);
+        Preconditions.checkNotNull(detectionService);
+        Preconditions.checkNotNull(config);
+
+        try {
+            return mSoundTriggerService.startRecognitionForService(new ParcelUuid(soundModelId),
+                params, detectionService, config);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Stops the given model's recognition.
      * @hide
      */
@@ -324,4 +363,19 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get the amount of time (in milliseconds) an operation of the
+     * {@link ISoundTriggerDetectionService} is allowed to ask.
+     *
+     * @return The amount of time an sound trigger detection service operation is allowed to last
+     */
+    public int getDetectionServiceOperationsTimeout() {
+        try {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT);
+        } catch (Settings.SettingNotFoundException e) {
+            return Integer.MAX_VALUE;
+        }
+    }
 }
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 3a67142..4f6763e 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -449,13 +449,14 @@
     std::vector<sp<IMemory> > frames;
     status_t err = retriever->getFrameAtIndex(&frames, frameIndex, numFrames, colorFormat);
     if (err != OK || frames.size() == 0) {
-        ALOGE("failed to get frames from retriever, err=%d, size=%zu",
-                err, frames.size());
+        jniThrowException(env,
+                "java/lang/IllegalStateException", "No frames from retriever");
         return NULL;
     }
     jobject arrayList = env->NewObject(fields.arrayListClazz, fields.arrayListInit);
     if (arrayList == NULL) {
-        ALOGE("can't create bitmap array list object");
+        jniThrowException(env,
+                "java/lang/IllegalStateException", "Can't create bitmap array");
         return NULL;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 350b648..8473c06 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.UserHandle;
@@ -50,6 +51,19 @@
         return getEnabledServicesFromSettings(context, UserHandle.myUserId());
     }
 
+    public static boolean hasServiceCrashed(String packageName, String serviceName,
+            List<AccessibilityServiceInfo> enabledServiceInfos) {
+        for (int i = 0; i < enabledServiceInfos.size(); i++) {
+            AccessibilityServiceInfo accessibilityServiceInfo = enabledServiceInfos.get(i);
+            final ServiceInfo serviceInfo = enabledServiceInfos.get(i).getResolveInfo().serviceInfo;
+            if (TextUtils.equals(serviceInfo.packageName, packageName)
+                    && TextUtils.equals(serviceInfo.name, serviceName)) {
+                return accessibilityServiceInfo.crashed;
+            }
+        }
+        return false;
+    }
+
     /**
      * @return the set of enabled accessibility services for {@param userId}. If there are no
      * services, it returns the unmodifiable {@link Collections#emptySet()}.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index f6ec6a8..ec25d2d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -416,6 +416,29 @@
         }
     }
 
+    /**
+     * Set this device as active device
+     * @return true if at least one profile on this device is set to active, false otherwise
+     */
+    public boolean setActive() {
+        boolean result = false;
+        A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+        if (a2dpProfile != null && isConnectedProfile(a2dpProfile)) {
+            if (a2dpProfile.setActiveDevice(getDevice())) {
+                Log.i(TAG, "OnPreferenceClickListener: A2DP active device=" + this);
+                result = true;
+            }
+        }
+        HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+        if ((headsetProfile != null) && isConnectedProfile(headsetProfile)) {
+            if (headsetProfile.setActiveDevice(getDevice())) {
+                Log.i(TAG, "OnPreferenceClickListener: Headset active device=" + this);
+                result = true;
+            }
+        }
+        return result;
+    }
+
     void refreshName() {
         fetchName();
         dispatchAttributesChanged();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 085e112..8f80527 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -176,33 +176,34 @@
     @Deprecated
     public WifiTracker(Context context, WifiListener wifiListener,
             boolean includeSaved, boolean includeScans) {
-        this(context, new WifiListenerExecutor(wifiListener),
+        this(context, wifiListener,
                 context.getSystemService(WifiManager.class),
                 context.getSystemService(ConnectivityManager.class),
                 context.getSystemService(NetworkScoreManager.class),
                 newIntentFilter());
     }
 
-    // TODO(Sghuman): Clean up includeSaved and includeScans from all constructors and linked
+    // TODO(sghuman): Clean up includeSaved and includeScans from all constructors and linked
     // calling apps once IC window is complete
     public WifiTracker(Context context, WifiListener wifiListener,
             @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
-        this(context, new WifiListenerExecutor(wifiListener),
+        this(context, wifiListener,
                 context.getSystemService(WifiManager.class),
                 context.getSystemService(ConnectivityManager.class),
                 context.getSystemService(NetworkScoreManager.class),
                 newIntentFilter());
+
         lifecycle.addObserver(this);
     }
 
     @VisibleForTesting
-    WifiTracker(Context context, WifiListenerExecutor wifiListenerExecutor,
+    WifiTracker(Context context, WifiListener wifiListener,
             WifiManager wifiManager, ConnectivityManager connectivityManager,
             NetworkScoreManager networkScoreManager,
             IntentFilter filter) {
         mContext = context;
         mWifiManager = wifiManager;
-        mListener = wifiListenerExecutor;
+        mListener = new WifiListenerExecutor(wifiListener);
         mConnectivityManager = connectivityManager;
 
         // check if verbose logging developer option has been turned on or off
@@ -853,8 +854,7 @@
      *
      * <p>Also logs all callbacks invocations when verbose logging is enabled.
      */
-    @VisibleForTesting
-    public static class WifiListenerExecutor implements WifiListener {
+    @VisibleForTesting class WifiListenerExecutor implements WifiListener {
 
         private final WifiListener mDelegatee;
 
@@ -864,27 +864,29 @@
 
         @Override
         public void onWifiStateChanged(int state) {
-            if (isVerboseLoggingEnabled()) {
-                Log.i(TAG,
-                        String.format("Invoking onWifiStateChanged callback with state %d", state));
-            }
-            ThreadUtils.postOnMainThread(() -> mDelegatee.onWifiStateChanged(state));
+            runAndLog(() -> mDelegatee.onWifiStateChanged(state),
+                    String.format("Invoking onWifiStateChanged callback with state %d", state));
         }
 
         @Override
         public void onConnectedChanged() {
-            if (isVerboseLoggingEnabled()) {
-                Log.i(TAG, "Invoking onConnectedChanged callback");
-            }
-            ThreadUtils.postOnMainThread(() -> mDelegatee.onConnectedChanged());
+            runAndLog(mDelegatee::onConnectedChanged, "Invoking onConnectedChanged callback");
         }
 
         @Override
         public void onAccessPointsChanged() {
-            if (isVerboseLoggingEnabled()) {
-                Log.i(TAG, "Invoking onAccessPointsChanged callback");
-            }
-            ThreadUtils.postOnMainThread(() -> mDelegatee.onAccessPointsChanged());
+            runAndLog(mDelegatee::onAccessPointsChanged, "Invoking onAccessPointsChanged callback");
+        }
+
+        private void runAndLog(Runnable r, String verboseLog) {
+            ThreadUtils.postOnMainThread(() -> {
+                if (mRegistered) {
+                    if (isVerboseLoggingEnabled()) {
+                        Log.i(TAG, verboseLog);
+                    }
+                    r.run();
+                }
+            });
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index fd48eea..61efabc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -76,7 +76,8 @@
      * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"]
      * For instance [-40,5/-30,2]
      */
-    private static String getVisibilityStatus(AccessPoint accessPoint) {
+    @VisibleForTesting
+    static String getVisibilityStatus(AccessPoint accessPoint) {
         final WifiInfo info = accessPoint.getInfo();
         StringBuilder visibility = new StringBuilder();
         StringBuilder scans24GHz = new StringBuilder();
@@ -110,6 +111,9 @@
         // TODO: sort list by RSSI or age
         long nowMs = SystemClock.elapsedRealtime();
         for (ScanResult result : accessPoint.getScanResults()) {
+            if (result == null) {
+                continue;
+            }
             if (result.frequency >= AccessPoint.LOWER_FREQ_5GHZ
                     && result.frequency <= AccessPoint.HIGHER_FREQ_5GHZ) {
                 // Strictly speaking: [4915, 5825]
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 7fb4dc5..517db78 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -58,6 +58,8 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import com.android.settingslib.utils.ThreadUtils;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -135,7 +137,7 @@
     @Mock private RssiCurve mockBadgeCurve1;
     @Mock private RssiCurve mockBadgeCurve2;
     @Mock private WifiManager mockWifiManager;
-    @Mock private WifiTracker.WifiListenerExecutor mockWifiListenerExecutor;
+    @Mock private WifiTracker.WifiListener mockWifiListener;
 
     private final List<NetworkKey> mRequestedKeys = new ArrayList<>();
 
@@ -205,7 +207,7 @@
                       mAccessPointsChangedLatch.countDown();
                     }
                     return null;
-                }).when(mockWifiListenerExecutor).onAccessPointsChanged();
+                }).when(mockWifiListener).onAccessPointsChanged();
 
         // Turn on Scoring UI features
         mOriginalScoringUiSettingValue = Settings.Global.getInt(
@@ -271,7 +273,7 @@
     private WifiTracker createMockedWifiTracker() {
         final WifiTracker wifiTracker = new WifiTracker(
                 mContext,
-                mockWifiListenerExecutor,
+                mockWifiListener,
                 mockWifiManager,
                 mockConnectivityManager,
                 mockNetworkScoreManager,
@@ -690,7 +692,7 @@
         verify(mockConnectivityManager).getNetworkInfo(any(Network.class));
 
         // mStaleAccessPoints is true
-        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
+        verify(mockWifiListener, never()).onAccessPointsChanged();
         assertThat(tracker.getAccessPoints().size()).isEqualTo(2);
         assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue();
     }
@@ -719,7 +721,7 @@
         verify(mockConnectivityManager).getNetworkInfo(any(Network.class));
 
         // mStaleAccessPoints is true
-        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
+        verify(mockWifiListener, never()).onAccessPointsChanged();
 
         assertThat(tracker.getAccessPoints()).hasSize(1);
         assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue();
@@ -762,6 +764,41 @@
     }
 
     @Test
+    public void stopTrackingShouldPreventCallbacksFromOngoingWork() throws Exception {
+        WifiTracker tracker = createMockedWifiTracker();
+        startTracking(tracker);
+
+        final CountDownLatch ready = new CountDownLatch(1);
+        final CountDownLatch latch = new CountDownLatch(1);
+        final CountDownLatch lock = new CountDownLatch(1);
+        tracker.mWorkHandler.post(() -> {
+            try {
+                ready.countDown();
+                lock.await();
+
+                tracker.mReceiver.onReceive(
+                        mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
+
+                latch.countDown();
+            } catch (InterruptedException e) {
+                fail("Interrupted Exception while awaiting lock release: " + e);
+            }
+        });
+
+        ready.await(); // Make sure we have entered the first message handler
+        tracker.onStop();
+        lock.countDown();
+        assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+
+        // Wait for main thread
+        final CountDownLatch latch2 = new CountDownLatch(1);
+        ThreadUtils.postOnMainThread(latch2::countDown);
+        latch2.await();
+
+        verify(mockWifiListener, never()).onWifiStateChanged(anyInt());
+    }
+
+    @Test
     public void stopTrackingShouldSetStaleBitWhichPreventsCallbacksUntilNextScanResult()
             throws Exception {
         WifiTracker tracker = createMockedWifiTracker();
@@ -778,7 +815,7 @@
                 mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION));
 
 
-        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
+        verify(mockWifiListener, never()).onAccessPointsChanged();
 
         sendScanResults(tracker); // verifies onAccessPointsChanged is invoked
     }
@@ -795,7 +832,7 @@
         tracker.mReceiver.onReceive(
                 mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION));
 
-        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
+        verify(mockWifiListener, never()).onAccessPointsChanged();
 
         sendScanResults(tracker); // verifies onAccessPointsChanged is invoked
     }
@@ -816,7 +853,7 @@
     @Test
     public void onConnectedChangedCallback_shouldNotBeInvokedWhenNoStateChange() throws Exception {
         WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
-        verify(mockWifiListenerExecutor, times(1)).onConnectedChanged();
+        verify(mockWifiListener, times(1)).onConnectedChanged();
 
         NetworkInfo networkInfo = new NetworkInfo(
                 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
@@ -826,13 +863,13 @@
         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
         tracker.mReceiver.onReceive(mContext, intent);
 
-        verify(mockWifiListenerExecutor, times(1)).onConnectedChanged();
+        verify(mockWifiListener, times(1)).onConnectedChanged();
     }
 
     @Test
     public void onConnectedChangedCallback_shouldBeInvokedWhenStateChanges() throws Exception {
         WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
-        verify(mockWifiListenerExecutor, times(1)).onConnectedChanged();
+        verify(mockWifiListener, times(1)).onConnectedChanged();
 
         NetworkInfo networkInfo = new NetworkInfo(
                 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
@@ -844,7 +881,7 @@
         tracker.mReceiver.onReceive(mContext, intent);
 
         assertThat(tracker.isConnected()).isFalse();
-        verify(mockWifiListenerExecutor, times(2)).onConnectedChanged();
+        verify(mockWifiListener, times(2)).onConnectedChanged();
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 0775727..632b014 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -294,4 +294,24 @@
         // Verify new alias is returned on getName
         assertThat(cachedBluetoothDevice.getName()).isEqualTo(DEVICE_ALIAS_NEW);
     }
+
+    @Test
+    public void testSetActive() {
+        when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+        when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile);
+        when(mA2dpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
+        when(mHfpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
+
+        assertThat(mCachedDevice.setActive()).isFalse();
+
+        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.setActive()).isTrue();
+
+        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.setActive()).isTrue();
+
+        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.setActive()).isFalse();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index ea8ecba..91d9f91 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -18,6 +18,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -31,6 +32,7 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.text.format.DateUtils;
+import android.util.ArraySet;
 
 import com.android.settingslib.R;
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
@@ -43,6 +45,7 @@
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
+import java.util.Set;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class WifiUtilsTest {
@@ -56,6 +59,8 @@
     private RssiCurve mockBadgeCurve;
     @Mock
     private WifiNetworkScoreCache mockWifiNetworkScoreCache;
+    @Mock
+    private AccessPoint mAccessPoint;
 
     @Before
     public void setUp() {
@@ -84,6 +89,15 @@
         assertThat(summary.contains(mContext.getString(R.string.speed_label_very_fast))).isTrue();
     }
 
+    @Test
+    public void testGetVisibilityStatus_nullResultDoesNotCrash() {
+        doReturn(null).when(mAccessPoint).getInfo();
+        Set<ScanResult> set = new ArraySet<>();
+        set.add(null);
+        doReturn(set).when(mAccessPoint).getScanResults();
+        WifiUtils.getVisibilityStatus(mAccessPoint);
+    }
+
     private static ArrayList<ScanResult> buildScanResultCache() {
         ArrayList<ScanResult> scanResults = new ArrayList<>();
         for (int i = 0; i < 5; i++) {
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index ecda53a..8b78366 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -82,6 +82,10 @@
     <string name="def_wireless_charging_started_sound" translatable="false">/system/media/audio/ui/WirelessChargingStarted.ogg</string>
     <string name="def_charging_started_sound" translatable="false">/system/media/audio/ui/ChargingStarted.ogg</string>
 
+    <!-- sound trigger detection service default values -->
+    <integer name="def_max_sound_trigger_detection_service_ops_per_day" translatable="false">200</integer>
+    <integer name="def_sound_trigger_detection_service_op_timeout" translatable="false">15000</integer>
+
     <bool name="def_lockscreen_disabled">false</bool>
     <bool name="def_device_provisioned">false</bool>
     <integer name="def_dock_audio_media_enabled">1</integer>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f895a98..34aae49 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1164,6 +1164,12 @@
                 Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
                 GlobalSettingsProto.HIDDEN_API_BLACKLIST_EXEMPTIONS);
         dumpSetting(s, p,
+                Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
+                GlobalSettingsProto.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT);
+        dumpSetting(s, p,
+                Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
+                GlobalSettingsProto.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY);
+        dumpSetting(s, p,
                 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
                 GlobalSettingsProto.MULTI_SIM_VOICE_CALL_SUBSCRIPTION);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5102cbd..f215c12 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2938,7 +2938,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 159;
+            private static final int SETTINGS_VERSION = 161;
 
             private final int mUserId;
 
@@ -3646,6 +3646,57 @@
                     currentVersion = 159;
                 }
 
+                if (currentVersion == 159) {
+                    // Version 160: Hiding notifications from the lockscreen is only available as
+                    // primary user option, profiles can only make them redacted. If a profile was
+                    // configured to not show lockscreen notifications, ensure that at the very
+                    // least these will be come hidden.
+                    if (mUserManager.isManagedProfile(userId)) {
+                        final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                        Setting showNotifications = secureSettings.getSettingLocked(
+                            Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+                        // The default value is "1", check if user has turned it off.
+                        if ("0".equals(showNotifications.getValue())) {
+                            secureSettings.insertSettingLocked(
+                                Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, "0",
+                                null /* tag */, false /* makeDefault */,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                        // The setting is no longer valid for managed profiles, it should be
+                        // treated as if it was set to "1".
+                        secureSettings.deleteSettingLocked(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+                    }
+                    currentVersion = 160;
+                }
+
+                if (currentVersion == 160) {
+                    // Version 161: Set the default value for
+                    // MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY and
+                    // SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT
+                    final SettingsState globalSettings = getGlobalSettingsLocked();
+
+                    String oldValue = globalSettings.getSettingLocked(
+                            Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY).getValue();
+                    if (TextUtils.equals(null, oldValue)) {
+                        globalSettings.insertSettingLocked(
+                                Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
+                                Integer.toString(getContext().getResources().getInteger(
+                                        R.integer.def_max_sound_trigger_detection_service_ops_per_day)),
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    oldValue = globalSettings.getSettingLocked(
+                            Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT).getValue();
+                    if (TextUtils.equals(null, oldValue)) {
+                        globalSettings.insertSettingLocked(
+                                Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
+                                Integer.toString(getContext().getResources().getInteger(
+                                        R.integer.def_sound_trigger_detection_service_op_timeout)),
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 161;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 6d341c5..449946d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -42,6 +42,7 @@
 import android.util.Base64;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
@@ -386,6 +387,9 @@
             mSettings.put(name, newState);
         }
 
+        StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newState.value, oldValue, tag,
+            makeDefault, getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED);
+
         addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
 
         updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
@@ -410,6 +414,10 @@
 
         Setting oldState = mSettings.remove(name);
 
+        StatsLog.write(StatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "",
+            oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
+            StatsLog.SETTING_CHANGED__REASON__DELETED);
+
         updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
                 null, oldState.defaultValue, null);
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 589ae2a..b49f1ac 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -121,6 +121,7 @@
     <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+    <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
     <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
diff --git a/packages/SystemUI/res/drawable/heads_up_scrim.xml b/packages/SystemUI/res/drawable/heads_up_scrim.xml
deleted file mode 100644
index 59000fc..0000000
--- a/packages/SystemUI/res/drawable/heads_up_scrim.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <gradient
-            android:type="linear"
-            android:angle="-90"
-            android:startColor="#55000000"
-            android:endColor="#00000000" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml b/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml
index 3345f61..6a7f18b 100644
--- a/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml
+++ b/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml
@@ -20,6 +20,6 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:pathData="M20.000000,5.000000L4.000000,5.000000C2.900000,5.000000 2.000000,5.900000 2.000000,7.000000l0.000000,10.000000c0.000000,1.100000 0.900000,2.000000 2.000000,2.000000l16.000000,0.000000c1.100000,0.000000 2.000000,-0.900000 2.000000,-2.000000L22.000000,7.000000C22.000000,5.900000 21.100000,5.000000 20.000000,5.000000zM11.000000,8.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000L11.000000,8.000000zM11.000000,11.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000L11.000000,11.000000zM8.000000,8.000000l2.000000,0.000000l0.000000,2.000000L8.000000,10.000000L8.000000,8.000000zM8.000000,11.000000l2.000000,0.000000l0.000000,2.000000L8.000000,13.000000L8.000000,11.000000zM7.000000,13.000000L5.000000,13.000000l0.000000,-2.000000l2.000000,0.000000L7.000000,13.000000zM7.000000,10.000000L5.000000,10.000000L5.000000,8.000000l2.000000,0.000000L7.000000,10.000000zM16.000000,17.000000L8.000000,17.000000l0.000000,-2.000000l8.000000,0.000000L16.000000,17.000000zM16.000000,13.000000l-2.000000,0.000000l0.000000,-2.000000l2.000000,0.000000L16.000000,13.000000zM16.000000,10.000000l-2.000000,0.000000L14.000000,8.000000l2.000000,0.000000L16.000000,10.000000zM19.000000,13.000000l-2.000000,0.000000l0.000000,-2.000000l2.000000,0.000000L19.000000,13.000000zM19.000000,10.000000l-2.000000,0.000000L17.000000,8.000000l2.000000,0.000000L19.000000,10.000000z"
+        android:pathData="M21,4H3C1.9,4 1,4.9 1,6v13c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V6C23,4.9 22.1,4 21,4zM21,19H3V6h18V19zM9,8h2v2H9V8zM5,8h2v2H5V8zM8,16h8v1H8V16zM13,8h2v2h-2V8zM9,12h2v2H9V12zM5,12h2v2H5V12zM13,12h2v2h-2V12zM17,8h2v2h-2V8zM17,12h2v2h-2V12z"
         android:fillColor="?attr/singleToneColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_lockdown.xml b/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
index b517fc8..65b9813 100644
--- a/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
+++ b/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
@@ -20,6 +20,7 @@
         android:viewportHeight="24.0">
 
     <path
-        android:fillColor="#757575"
+        android:fillColor="#000000"
+        android:alpha="0.87"
         android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml
new file mode 100644
index 0000000..bacb5c1
--- /dev/null
+++ b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<com.android.systemui.statusbar.HeadsUpStatusBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:visibility="invisible"
+    android:id="@+id/heads_up_status_bar_view"
+    android:alpha="0"
+>
+    <!-- This is a space just used as a layout and it's not actually displaying anything. We're
+         repositioning the statusbar icon to the position where this is laid out when showing this
+         view. -->
+    <Space
+        android:id="@+id/icon_placeholder"
+        android:layout_width="@dimen/status_bar_icon_drawing_size"
+        android:layout_height="@dimen/status_bar_icon_drawing_size"
+        android:layout_gravity="center_vertical"
+    />
+    <TextView
+        android:id="@+id/text"
+        android:textAppearance="@style/TextAppearance.HeadsUpStatusBarText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:fadingEdge="horizontal"
+        android:textAlignment="viewStart"
+        android:paddingStart="6dp"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+    />
+
+</com.android.systemui.statusbar.HeadsUpStatusBarView>
diff --git a/packages/SystemUI/res/layout/notification_icon_area.xml b/packages/SystemUI/res/layout/notification_icon_area.xml
index 6732e6c..fa696cc 100644
--- a/packages/SystemUI/res/layout/notification_icon_area.xml
+++ b/packages/SystemUI/res/layout/notification_icon_area.xml
@@ -18,12 +18,14 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/notification_icon_area_inner"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" >
+    android:layout_height="match_parent"
+    android:clipChildren="false">
     <com.android.systemui.statusbar.phone.NotificationIconContainer
         android:id="@+id/notificationIcons"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_alignParentStart="true"
         android:gravity="center_vertical"
-        android:orientation="horizontal"/>
+        android:orientation="horizontal"
+        android:clipChildren="false"/>
 </com.android.keyguard.AlphaOptimizedLinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml
index 734c877..26cf792 100644
--- a/packages/SystemUI/res/layout/rounded_corners.xml
+++ b/packages/SystemUI/res/layout/rounded_corners.xml
@@ -14,7 +14,7 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 -->
-<FrameLayout
+<com.android.systemui.RegionInterceptingFrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
@@ -32,4 +32,4 @@
         android:tint="#ff000000"
         android:layout_gravity="right|bottom"
         android:src="@drawable/rounded" />
-</FrameLayout>
+</com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 8c0b9ba..9d336e2 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -3,16 +3,16 @@
 **
 ** Copyright 2006, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** 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 
+**     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 
+** 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.
 */
 -->
@@ -34,7 +34,7 @@
         android:id="@+id/notification_lights_out"
         android:layout_width="@dimen/status_bar_icon_size"
         android:layout_height="match_parent"
-        android:paddingStart="6dip"
+        android:paddingStart="@dimen/status_bar_padding_start"
         android:paddingBottom="2dip"
         android:src="@drawable/ic_sysbar_lights_out_dot_small"
         android:scaleType="center"
@@ -44,7 +44,7 @@
     <LinearLayout android:id="@+id/status_bar_contents"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingStart="6dp"
+        android:paddingStart="@dimen/status_bar_padding_start"
         android:paddingEnd="8dp"
         android:orientation="horizontal"
         >
@@ -53,33 +53,41 @@
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:layout="@layout/operator_name" />
-
-        <LinearLayout
+        <FrameLayout
             android:layout_height="match_parent"
             android:layout_width="0dp"
-            android:layout_weight="1"
-        >
-            <com.android.systemui.statusbar.policy.Clock
-                android:id="@+id/clock"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-                android:singleLine="true"
-                android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
-                android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
-                android:gravity="center_vertical|start"
-            />
+            android:layout_weight="1">
 
-            <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
-                 PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
-            <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
-                android:id="@+id/notification_icon_area"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:orientation="horizontal" />
+            <include layout="@layout/heads_up_status_bar_layout" />
 
-        </LinearLayout>
+            <LinearLayout
+                android:layout_height="match_parent"
+                android:layout_width="match_parent"
+                android:clipChildren="false"
+            >
+                <com.android.systemui.statusbar.policy.Clock
+                    android:id="@+id/clock"
+                    android:layout_width="wrap_content"
+                    android:layout_height="match_parent"
+                    android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+                    android:singleLine="true"
+                    android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+                    android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+                    android:gravity="center_vertical|start"
+                />
+
+                <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
+                     PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
+                <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+                    android:id="@+id/notification_icon_area"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"
+                    android:orientation="horizontal"
+                    android:clipChildren="false"/>
+
+            </LinearLayout>
+        </FrameLayout>
 
         <!-- Space should cover the notch (if it exists) and let other views lay out around it -->
         <android.widget.Space
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index ef442e5..75403b9 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -51,14 +51,6 @@
         sysui:ignoreRightInset="true"
         />
 
-    <com.android.systemui.statusbar.AlphaOptimizedView
-        android:id="@+id/heads_up_scrim"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/heads_up_scrim_height"
-        android:background="@drawable/heads_up_scrim"
-        sysui:ignoreRightInset="true"
-        android:importantForAccessibility="no"/>
-
     <FrameLayout
         android:id="@+id/status_bar_container"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index cb3c282..d65e42b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -110,6 +110,12 @@
     <!-- Side padding on the lockscreen on the side of notifications -->
     <dimen name="notification_side_paddings">4dp</dimen>
 
+    <!-- padding between the heads up and the statusbar -->
+    <dimen name="heads_up_status_bar_padding">8dp</dimen>
+
+    <!-- heads up elevation that is added if the view is pinned -->
+    <dimen name="heads_up_pinned_elevation">16dp</dimen>
+
     <!-- Height of a messaging notifications with actions at least. Not that this is an upper bound
          and the notification won't use this much, but is measured with wrap_content -->
     <dimen name="notification_messaging_actions_min_height">196dp</dimen>
@@ -451,7 +457,6 @@
     <dimen name="keyguard_clock_notifications_margin">30dp</dimen>
     <!-- Minimum margin between clock and status bar -->
     <dimen name="keyguard_clock_top_margin">36dp</dimen>
-    <dimen name="heads_up_scrim_height">250dp</dimen>
 
     <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bf3fa29..3c8a695 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -365,9 +365,6 @@
     <!-- Content description of the data connection type GPRS. [CHAR LIMIT=NONE] -->
     <string name="data_connection_gprs">GPRS</string>
 
-    <!-- Content description of the data connection type 1x. [CHAR LIMIT=NONE] -->
-    <string name="data_connection_1x">1 X</string>
-
     <!-- Content description of the data connection type HSPA and its variants. [CHAR LIMIT=NONE] -->
     <string name="data_connection_hspa">HSPA</string>
 
@@ -1848,11 +1845,14 @@
     <string name="right_icon">Right icon</string>
 
     <!-- Label for area where tiles can be dragged out of [CHAR LIMIT=60] -->
-    <string name="drag_to_add_tiles">Drag to add tiles</string>
+    <string name="drag_to_add_tiles">Hold and drag to add tiles</string>
 
     <!-- Label for area where tiles can be dragged in to [CHAR LIMIT=60] -->
     <string name="drag_to_remove_tiles">Drag here to remove</string>
 
+    <!-- Label to indicate to users that additional tiles cannot be removed. [CHAR LIMIT=60] -->
+    <string name="drag_to_remove_disabled">You need at least 6 tiles</string>
+
     <!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] -->
     <string name="qs_edit">Edit</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1470dfa..20d8834 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -490,6 +490,10 @@
         <item name="android:paddingEnd">8dp</item>
     </style>
 
+    <style name="TextAppearance.HeadsUpStatusBarText"
+           parent="@*android:style/TextAppearance.Material.Notification.Info">
+    </style>
+
     <style name="edit_theme" parent="qs_base">
         <item name="android:colorBackground">?android:attr/colorSecondary</item>
     </style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 940c9ef..5fa6c79 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -29,6 +29,8 @@
 
     private IRecentsAnimationController mAnimationController;
 
+    public RecentsAnimationControllerCompat() { }
+
     public RecentsAnimationControllerCompat(IRecentsAnimationController animationController) {
         mAnimationController = animationController;
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9d5fb52..d24675c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1726,6 +1726,7 @@
         callback.onPhoneStateChanged(mPhoneState);
         callback.onRefreshCarrierInfo();
         callback.onClockVisibilityChanged();
+        callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
         for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
             final SimData state = data.getValue();
             callback.onSimStateChanged(state.subId, state.slotId, state.simState);
diff --git a/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
new file mode 100644
index 0000000..646f69e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.content.Context;
+import android.graphics.Region;
+import android.graphics.Region.Op;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.InternalInsetsInfo;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.widget.FrameLayout;
+
+/**
+ * Frame layout that will intercept the touches of children if they want to
+ */
+public class RegionInterceptingFrameLayout extends FrameLayout {
+    public RegionInterceptingFrameLayout(Context context) {
+        super(context);
+    }
+
+    public RegionInterceptingFrameLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public RegionInterceptingFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public RegionInterceptingFrameLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
+    }
+
+    private final OnComputeInternalInsetsListener mInsetsListener = internalInsetsInfo -> {
+        internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+        internalInsetsInfo.touchableRegion.setEmpty();
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            if (!(child instanceof RegionInterceptableView)) {
+                continue;
+            }
+            RegionInterceptableView riv = (RegionInterceptableView) child;
+            if (!riv.shouldInterceptTouch()) {
+                continue;
+            }
+            Region unionRegion = riv.getInterceptRegion();
+            if (unionRegion == null) {
+                continue;
+            }
+
+            internalInsetsInfo.touchableRegion.op(riv.getInterceptRegion(), Op.UNION);
+        }
+    };
+
+    public interface RegionInterceptableView {
+        default public boolean shouldInterceptTouch() {
+            return false;
+        }
+
+        public Region getInterceptRegion();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 9a20c81..a0fa69e 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -49,6 +49,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.qs.QS;
@@ -232,8 +233,7 @@
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 LayoutParams.WRAP_CONTENT,
                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                         | WindowManager.LayoutParams.FLAG_SLIPPERY
@@ -317,7 +317,8 @@
         }
     }
 
-    public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener {
+    public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener,
+            RegionInterceptableView {
 
         private final DisplayInfo mInfo = new DisplayInfo();
         private final Paint mPaint = new Paint();
@@ -468,5 +469,19 @@
                 }
             }
         }
+
+        @Override
+        public boolean shouldInterceptTouch() {
+            return mInfo.displayCutout != null && getVisibility() == VISIBLE;
+        }
+
+        @Override
+        public Region getInterceptRegion() {
+            if (mInfo.displayCutout == null) {
+                return null;
+            }
+
+            return mInfo.displayCutout.getBounds();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 91edfda..391843c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -100,10 +100,10 @@
     }
 
     public ScrimController createScrimController(LightBarController lightBarController,
-            ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
-            LockscreenWallpaper lockscreenWallpaper, Consumer<Integer> scrimVisibleListener,
-            DozeParameters dozeParameters, AlarmManager alarmManager) {
-        return new ScrimController(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
+            ScrimView scrimBehind, ScrimView scrimInFront, LockscreenWallpaper lockscreenWallpaper,
+            Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
+            AlarmManager alarmManager) {
+        return new ScrimController(lightBarController, scrimBehind, scrimInFront,
                 scrimVisibleListener, dozeParameters, alarmManager);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 092f3d2..5bf62f6 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -22,8 +22,10 @@
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.os.Handler;
+import android.os.PowerManager;
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIApplication;
@@ -46,7 +48,7 @@
 
         DozeHost host = getHost(dozeService);
         AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context);
-        DozeParameters params = new DozeParameters(context);
+        DozeParameters params = DozeParameters.getInstance(context);
         Handler handler = new Handler();
         WakeLock wakeLock = new DelayedWakeLock(handler,
                 WakeLock.createPartial(context, "Doze"));
@@ -64,9 +66,9 @@
                 createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
                         handler, wakeLock, machine),
                 createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params),
-                new DozeScreenState(wrappedService, handler, params),
+                new DozeScreenState(wrappedService, handler, params, wakeLock),
                 createDozeScreenBrightness(context, wrappedService, sensorManager, host, handler),
-                new DozeWallpaperState(context)
+                new DozeWallpaperState(context, params)
         });
 
         return machine;
@@ -92,7 +94,8 @@
     private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
             DozeMachine machine, Handler handler, AlarmManager alarmManager,
             DozeParameters params) {
-        return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params);
+        return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params,
+                KeyguardUpdateMonitor.getInstance(context));
     }
 
     public static DozeHost getHost(DozeService service) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 6ff8e3d..152b9fc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -92,17 +92,17 @@
             switch (this) {
                 case UNINITIALIZED:
                 case INITIALIZED:
-                case DOZE:
+                case DOZE_REQUEST_PULSE:
+                    return parameters.shouldControlScreenOff() ? Display.STATE_ON
+                            : Display.STATE_OFF;
                 case DOZE_AOD_PAUSED:
+                case DOZE:
                     return Display.STATE_OFF;
                 case DOZE_PULSING:
                     return Display.STATE_ON;
                 case DOZE_AOD:
                 case DOZE_AOD_PAUSING:
                     return Display.STATE_DOZE_SUSPEND;
-                case DOZE_REQUEST_PULSE:
-                    return parameters.getDisplayNeedsBlanking() ? Display.STATE_OFF
-                            : Display.STATE_ON;
                 default:
                     return Display.STATE_UNKNOWN;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 7d14564..f72bff5d6 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -21,6 +21,7 @@
 import android.view.Display;
 
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.wakelock.WakeLock;
 
 /**
  * Controls the screen when dozing.
@@ -30,18 +31,27 @@
     private static final boolean DEBUG = DozeService.DEBUG;
     private static final String TAG = "DozeScreenState";
 
+    /**
+     * Delay entering low power mode when animating to make sure that we'll have
+     * time to move all elements into their final positions while still at 60 fps.
+     */
+    private static final int ENTER_DOZE_DELAY = 3000;
+
     private final DozeMachine.Service mDozeService;
     private final Handler mHandler;
     private final Runnable mApplyPendingScreenState = this::applyPendingScreenState;
     private final DozeParameters mParameters;
 
     private int mPendingScreenState = Display.STATE_UNKNOWN;
+    private boolean mWakeLockHeld;
+    private WakeLock mWakeLock;
 
     public DozeScreenState(DozeMachine.Service service, Handler handler,
-            DozeParameters parameters) {
+            DozeParameters parameters, WakeLock wakeLock) {
         mDozeService = service;
         mHandler = handler;
         mParameters = parameters;
+        mWakeLock = wakeLock;
     }
 
     @Override
@@ -69,12 +79,33 @@
             // that the screen turns on again before the navigation bar is hidden. To work around
             // that, wait for a traversal to happen before applying the initial screen state.
             mPendingScreenState = screenState;
-            if (!messagePending) {
-                mHandler.post(mApplyPendingScreenState);
+
+            // Delay screen state transitions even longer while animations are running.
+            boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD
+                    && mParameters.shouldControlScreenOff();
+
+            if (!mWakeLockHeld && shouldDelayTransition) {
+                mWakeLockHeld = true;
+                mWakeLock.acquire();
             }
-            return;
+
+            if (!messagePending) {
+                if (DEBUG) {
+                    Log.d(TAG, "Display state changed to " + screenState + " delayed by "
+                            + (shouldDelayTransition ? ENTER_DOZE_DELAY : 1));
+                }
+
+                if (shouldDelayTransition) {
+                    mHandler.postDelayed(mApplyPendingScreenState, ENTER_DOZE_DELAY);
+                } else {
+                    mHandler.post(mApplyPendingScreenState);
+                }
+            } else if (DEBUG) {
+                Log.d(TAG, "Pending display state change to " + screenState);
+            }
+        } else {
+            applyScreenState(screenState);
         }
-        applyScreenState(screenState);
     }
 
     private void applyPendingScreenState() {
@@ -87,6 +118,10 @@
             if (DEBUG) Log.d(TAG, "setDozeScreenState(" + screenState + ")");
             mDozeService.setDozeScreenState(screenState);
             mPendingScreenState = Display.STATE_UNKNOWN;
+            if (mWakeLockHeld) {
+                mWakeLockHeld = false;
+                mWakeLock.release();
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 75f1b50..778e630 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -25,6 +25,9 @@
 import android.text.format.Formatter;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.AlarmTimeout;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -44,22 +47,46 @@
     private final WakeLock mWakeLock;
     private final DozeMachine mMachine;
     private final AlarmTimeout mTimeTicker;
-    private final boolean mCanAnimateWakeup;
+    private final boolean mCanAnimateTransition;
+    private final DozeParameters mDozeParameters;
+
+    private boolean mKeyguardShowing;
+    private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
+            new KeyguardUpdateMonitorCallback() {
+
+                @Override
+                public void onKeyguardVisibilityChanged(boolean showing) {
+                    mKeyguardShowing = showing;
+                    updateAnimateScreenOff();
+                }
+            };
 
     private long mLastTimeTickElapsed = 0;
 
     public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine,
             WakeLock wakeLock, DozeHost host, Handler handler,
-            DozeParameters params) {
+            DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor) {
         mContext = context;
         mMachine = machine;
         mWakeLock = wakeLock;
         mHost = host;
         mHandler = handler;
-        mCanAnimateWakeup = !params.getDisplayNeedsBlanking();
-
+        mCanAnimateTransition = !params.getDisplayNeedsBlanking();
+        mDozeParameters = params;
         mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
-        mHost.setAnimateScreenOff(params.getCanControlScreenOffAnimation());
+        keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
+    }
+
+    /**
+     * Decide if we're taking over the screen-off animation
+     * when the device was configured to skip doze after screen off.
+     */
+    private void updateAnimateScreenOff() {
+        if (mCanAnimateTransition) {
+            final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing;
+            mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
+            mHost.setAnimateScreenOff(controlScreenOff);
+        }
     }
 
     private void pulseWhileDozing(int reason) {
@@ -118,7 +145,7 @@
                 // Keep current state.
                 break;
             default:
-                mHost.setAnimateWakeup(mCanAnimateWakeup);
+                mHost.setAnimateWakeup(mCanAnimateTransition && mDozeParameters.getAlwaysOn());
                 break;
         }
     }
@@ -170,4 +197,9 @@
 
         scheduleTimeTick();
     }
+
+    @VisibleForTesting
+    KeyguardUpdateMonitorCallback getKeyguardCallback() {
+        return mKeyguardVisibilityCallback;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 5156272..9d110fb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -26,7 +26,6 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 
 import java.io.PrintWriter;
 
@@ -43,10 +42,10 @@
     private boolean mIsAmbientMode;
     private final DozeParameters mDozeParameters;
 
-    public DozeWallpaperState(Context context) {
+    public DozeWallpaperState(Context context, DozeParameters dozeParameters) {
         this(IWallpaperManager.Stub.asInterface(
                 ServiceManager.getService(Context.WALLPAPER_SERVICE)),
-                new DozeParameters(context), KeyguardUpdateMonitor.getInstance(context));
+                dozeParameters, KeyguardUpdateMonitor.getInstance(context));
     }
 
     @VisibleForTesting
@@ -80,7 +79,7 @@
 
         final boolean animated;
         if (isAmbientMode) {
-            animated = mDozeParameters.getCanControlScreenOffAnimation() && !mKeyguardVisible;
+            animated = mDozeParameters.shouldControlScreenOff();
         } else {
             animated = !mDozeParameters.getDisplayNeedsBlanking();
         }
@@ -88,8 +87,10 @@
         if (isAmbientMode != mIsAmbientMode) {
             mIsAmbientMode = isAmbientMode;
             try {
-                Log.i(TAG, "AoD wallpaper state changed to: " + mIsAmbientMode
-                        + ", animated: " + animated);
+                if (DEBUG) {
+                    Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode
+                            + ", animated: " + animated);
+                }
                 mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, animated);
             } catch (RemoteException e) {
                 // Cannot notify wallpaper manager service, but it's fine, let's just skip it.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 951c0ea..59c7f23 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -41,21 +41,33 @@
     }
 
     public void dispatchStartedWakingUp() {
+        if (getWakefulness() == WAKEFULNESS_WAKING) {
+            return;
+        }
         setWakefulness(WAKEFULNESS_WAKING);
         dispatch(Observer::onStartedWakingUp);
     }
 
     public void dispatchFinishedWakingUp() {
+        if (getWakefulness() == WAKEFULNESS_AWAKE) {
+            return;
+        }
         setWakefulness(WAKEFULNESS_AWAKE);
         dispatch(Observer::onFinishedWakingUp);
     }
 
     public void dispatchStartedGoingToSleep() {
+        if (getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP) {
+            return;
+        }
         setWakefulness(WAKEFULNESS_GOING_TO_SLEEP);
         dispatch(Observer::onStartedGoingToSleep);
     }
 
     public void dispatchFinishedGoingToSleep() {
+        if (getWakefulness() == WAKEFULNESS_ASLEEP) {
+            return;
+        }
         setWakefulness(WAKEFULNESS_ASLEEP);
         dispatch(Observer::onFinishedGoingToSleep);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index bdc5e7d..3ba5fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -42,19 +42,19 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.systemui.R;
-import com.android.systemui.qs.tileimpl.QSIconViewImpl;
+import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.customize.TileAdapter.Holder;
 import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
 import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
 import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.tileimpl.QSIconViewImpl;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener {
-
+    private static final int MIN_NUM_TILES = 6;
     private static final long DRAG_LENGTH = 100;
     private static final float DRAG_SCALE = 1.2f;
     public static final long MOVE_DURATION = 150;
@@ -219,9 +219,15 @@
             return;
         }
         if (holder.getItemViewType() == TYPE_EDIT) {
-            ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(
-                    mCurrentDrag != null ? R.string.drag_to_remove_tiles
-                    : R.string.drag_to_add_tiles);
+            final int titleResId;
+            if (mCurrentDrag == null) {
+                titleResId = R.string.drag_to_add_tiles;
+            } else if (!canRemoveTiles() && mCurrentDrag.getAdapterPosition() < mEditIndex) {
+                titleResId = R.string.drag_to_remove_disabled;
+            } else {
+                titleResId = R.string.drag_to_remove_tiles;
+            }
+            ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleResId);
             return;
         }
         if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) {
@@ -286,7 +292,7 @@
                         if (mAccessibilityMoving) {
                             selectPosition(position, v);
                         } else {
-                            if (position < mEditIndex) {
+                            if (position < mEditIndex && canRemoveTiles()) {
                                 showAccessibilityDialog(position, v);
                             } else {
                                 startAccessibleDrag(position);
@@ -297,6 +303,10 @@
             }
         }
     }
+    
+    private boolean canRemoveTiles() {
+        return mCurrentSpecs.size() > MIN_NUM_TILES;
+    }
 
     private void selectPosition(int position, View v) {
         // Remove the placeholder.
@@ -507,7 +517,7 @@
                 break;
             }
         }
-    };
+    }
 
     private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
 
@@ -551,6 +561,9 @@
         @Override
         public boolean canDropOver(RecyclerView recyclerView, ViewHolder current,
                 ViewHolder target) {
+            if (!canRemoveTiles() && current.getAdapterPosition() < mEditIndex) {
+                return target.getAdapterPosition() < mEditIndex;
+            }
             return target.getAdapterPosition() <= mEditIndex + 1;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index cb9453b..b7a5d31 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -28,6 +28,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 
@@ -55,6 +56,7 @@
         mLp.token = new Binder();
         mLp.setTitle(WINDOW_TITLE);
         mLp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 0876507..8c28af5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -26,6 +26,7 @@
 import android.graphics.Color;
 import android.graphics.RectF;
 import android.util.AttributeSet;
+import android.util.MathUtils;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewAnimationUtils;
@@ -178,6 +179,10 @@
     private boolean mNeedsDimming;
     private int mDimmedAlpha;
     private boolean mBlockNextTouch;
+    private boolean mIsHeadsUpAnimation;
+    private int mHeadsUpAddStartLocation;
+    private float mHeadsUpLocation;
+    private boolean mIsAppearing;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -204,6 +209,18 @@
                 makeInactive(true /* animate */);
             }
         }, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
+        initDimens();
+    }
+
+    private void initDimens() {
+        mHeadsUpAddStartLocation = getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin_start);
+    }
+
+    @Override
+    public void onDensityOrFontScaleChanged() {
+        super.onDensityOrFontScaleChanged();
+        initDimens();
     }
 
     @Override
@@ -745,27 +762,34 @@
     }
 
     @Override
-    public void performRemoveAnimation(long duration, float translationDirection,
-            Runnable onFinishedRunnable) {
+    public void performRemoveAnimation(long duration, long delay,
+            float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+            Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
         enableAppearDrawing(true);
+        mIsHeadsUpAnimation = isHeadsUpAnimation;
+        mHeadsUpLocation = endLocation;
         if (mDrawingAppearAnimation) {
             startAppearAnimation(false /* isAppearing */, translationDirection,
-                    0, duration, onFinishedRunnable);
+                    delay, duration, onFinishedRunnable, animationListener);
         } else if (onFinishedRunnable != null) {
             onFinishedRunnable.run();
         }
     }
 
     @Override
-    public void performAddAnimation(long delay, long duration) {
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
         enableAppearDrawing(true);
+        mIsHeadsUpAnimation = isHeadsUpAppear;
+        mHeadsUpLocation = mHeadsUpAddStartLocation;
         if (mDrawingAppearAnimation) {
-            startAppearAnimation(true /* isAppearing */, -1.0f, delay, duration, null);
+            startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
+                    duration, null, null);
         }
     }
 
     private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
-            long duration, final Runnable onFinishedRunnable) {
+            long duration, final Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener) {
         cancelAppearAnimation();
         mAnimationTranslationY = translationDirection * getActualHeight();
         if (mAppearAnimationFraction == -1.0f) {
@@ -778,6 +802,7 @@
                 mAppearAnimationTranslation = 0;
             }
         }
+        mIsAppearing = isAppearing;
 
         float targetValue;
         if (isAppearing) {
@@ -803,6 +828,9 @@
                 invalidate();
             }
         });
+        if (animationListener != null) {
+            mAppearAnimator.addListener(animationListener);
+        }
         if (delay > 0) {
             // we need to apply the initial state already to avoid drawn frames in the wrong state
             updateAppearAnimationAlpha();
@@ -862,9 +890,21 @@
                 / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
         widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
         widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
-        float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
-                widthFraction);
-        float right = getWidth() - left;
+        float startWidthFraction = HORIZONTAL_COLLAPSED_REST_PARTIAL;
+        if (mIsHeadsUpAnimation && !mIsAppearing) {
+            startWidthFraction = 0;
+        }
+        float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction)
+                        * getWidth();
+        float left;
+        float right;
+        if (mIsHeadsUpAnimation) {
+            left = MathUtils.lerp(mHeadsUpLocation, 0, 1.0f - widthFraction);
+            right = left + width;
+        } else {
+            left = getWidth() * 0.5f - width / 2.0f;
+            right = getWidth() - left;
+        }
 
         // handle top animation
         float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
@@ -1046,6 +1086,14 @@
         return calculateBgColor(false /* withTint */, false /* withOverride */);
     }
 
+    public boolean isPinned() {
+        return false;
+    }
+
+    public boolean isHeadsUpAnimatingAway() {
+        return false;
+    }
+
     public interface OnActivatedListener {
         void onActivated(ActivatableNotificationView view);
         void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index e8f0925..4728a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -28,11 +28,17 @@
     public static final long ANIMATION_DURATION_LENGTH = 210;
 
     public static void fadeOut(final View view, final Runnable endRunnable) {
+        fadeOut(view, ANIMATION_DURATION_LENGTH, 0, endRunnable);
+    }
+
+    public static void fadeOut(final View view, long duration, int delay,
+            final Runnable endRunnable) {
         view.animate().cancel();
         view.animate()
                 .alpha(0f)
-                .setDuration(ANIMATION_DURATION_LENGTH)
+                .setDuration(duration)
                 .setInterpolator(Interpolators.ALPHA_OUT)
+                .setStartDelay(delay)
                 .withEndAction(new Runnable() {
                     @Override
                     public void run() {
@@ -93,6 +99,10 @@
     }
 
     public static void fadeIn(final View view) {
+        fadeIn(view, ANIMATION_DURATION_LENGTH, 0);
+    }
+
+    public static void fadeIn(final View view, long duration, int delay) {
         view.animate().cancel();
         if (view.getVisibility() == View.INVISIBLE) {
             view.setAlpha(0.0f);
@@ -100,7 +110,8 @@
         }
         view.animate()
                 .alpha(1f)
-                .setDuration(ANIMATION_DURATION_LENGTH)
+                .setDuration(duration)
+                .setStartDelay(delay)
                 .setInterpolator(Interpolators.ALPHA_IN)
                 .withEndAction(null);
         if (view.hasOverlappingRendering()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index e5c5dcd..9bd8080 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -139,6 +139,7 @@
     private boolean mSensitiveHiddenInGeneral;
     private boolean mShowingPublicInitialized;
     private boolean mHideSensitiveForIntrinsicHeight;
+    private float mHeaderVisibleAmount = 1.0f;
 
     /**
      * Is this notification expanded by the system. The expansion state can be overridden by the
@@ -156,8 +157,6 @@
     private NotificationContentView mPublicLayout;
     private NotificationContentView mPrivateLayout;
     private NotificationContentView[] mLayouts;
-    private int mMaxExpandHeight;
-    private int mHeadsUpHeight;
     private int mNotificationColor;
     private ExpansionLogger mLogger;
     private String mLoggingKey;
@@ -534,6 +533,30 @@
         addChildNotification(row, -1);
     }
 
+    /**
+     * Set the how much the header should be visible. A value of 0 will make the header fully gone
+     * and a value of 1 will make the notification look just like normal.
+     * This is being used for heads up notifications, when they are pinned to the top of the screen
+     * and the header content is extracted to the statusbar.
+     *
+     * @param headerVisibleAmount the amount the header should be visible.
+     */
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        if (mHeaderVisibleAmount != headerVisibleAmount) {
+            mHeaderVisibleAmount = headerVisibleAmount;
+            mPrivateLayout.setHeaderVisibleAmount(headerVisibleAmount);
+            if (mChildrenContainer != null) {
+                mChildrenContainer.setHeaderVisibleAmount(headerVisibleAmount);
+            }
+            notifyHeightChanged(false /* needsAnimation */);
+        }
+    }
+
+    @Override
+    public float getHeaderVisibleAmount() {
+        return mHeaderVisibleAmount;
+    }
+
     @Override
     public void setHeadsUpIsVisible() {
         super.setHeadsUpIsVisible();
@@ -722,6 +745,7 @@
         }
     }
 
+    @Override
     public boolean isPinned() {
         return mIsPinned;
     }
@@ -741,11 +765,11 @@
             return mChildrenContainer.getIntrinsicHeight();
         }
         if(mExpandedWhenPinned) {
-            return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
+            return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
         } else if (atLeastMinHeight) {
-            return Math.max(getCollapsedHeight(), mHeadsUpHeight);
+            return Math.max(getCollapsedHeight(), getHeadsUpHeight());
         } else {
-            return mHeadsUpHeight;
+            return getHeadsUpHeight();
         }
     }
 
@@ -1034,11 +1058,12 @@
         }
     }
 
-    public void setDismissed(boolean dismissed, boolean fromAccessibility) {
-        mDismissed = dismissed;
+    public void setDismissed(boolean fromAccessibility) {
+        mDismissed = true;
         mGroupParentWhenDismissed = mNotificationParent;
         mRefocusOnDismiss = fromAccessibility;
         mChildAfterViewWhenDismissed = null;
+        mEntry.icon.setDismissed();
         if (isChildInGroup()) {
             List<ExpandableNotificationRow> notificationChildren =
                     mNotificationParent.getNotificationChildren();
@@ -1101,6 +1126,7 @@
      * @return if the view was just heads upped and is now animating away. During such a time the
      * layout needs to be kept consistent
      */
+    @Override
     public boolean isHeadsUpAnimatingAway() {
         return mHeadsupDisappearRunning;
     }
@@ -1121,7 +1147,7 @@
                 groupSummary.performDismiss(fromAccessibility);
             }
         }
-        setDismissed(true, fromAccessibility);
+        setDismissed(fromAccessibility);
         if (isClearable()) {
             if (mOnDismissRunnable != null) {
                 mOnDismissRunnable.run();
@@ -1921,9 +1947,9 @@
             if (isPinned() || mHeadsupDisappearRunning) {
                 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
             } else if (isExpanded()) {
-                return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
+                return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
             } else {
-                return Math.max(getCollapsedHeight(), mHeadsUpHeight);
+                return Math.max(getCollapsedHeight(), getHeadsUpHeight());
             }
         } else if (isExpanded()) {
             return getMaxExpandHeight();
@@ -1997,8 +2023,11 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        int intrinsicBefore = getIntrinsicHeight();
         super.onLayout(changed, left, top, right, bottom);
-        updateMaxHeights();
+        if (intrinsicBefore != getIntrinsicHeight()) {
+            notifyHeightChanged(true  /* needsAnimation */);
+        }
         if (mMenuRow.getMenuView() != null) {
             mMenuRow.onHeightUpdate();
         }
@@ -2022,15 +2051,6 @@
         }
     }
 
-    public void updateMaxHeights() {
-        int intrinsicBefore = getIntrinsicHeight();
-        mMaxExpandHeight = mPrivateLayout.getExpandHeight();
-        mHeadsUpHeight = mPrivateLayout.getHeadsUpHeight();
-        if (intrinsicBefore != getIntrinsicHeight()) {
-            notifyHeightChanged(true  /* needsAnimation */);
-        }
-    }
-
     @Override
     public void notifyHeightChanged(boolean needsAnimation) {
         super.notifyHeightChanged(needsAnimation);
@@ -2173,7 +2193,12 @@
     }
 
     public int getMaxExpandHeight() {
-        return mMaxExpandHeight;
+        return mPrivateLayout.getExpandHeight();
+    }
+
+
+    private int getHeadsUpHeight() {
+        return mPrivateLayout.getHeadsUpHeight();
     }
 
     public boolean areGutsExposed() {
@@ -2273,7 +2298,7 @@
         } else if (mIsSummaryWithChildren && !isGroupExpanded() && !shouldShowPublic()) {
             return mChildrenContainer.getMinHeight();
         } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
-            return mHeadsUpHeight;
+            return getHeadsUpHeight();
         }
         NotificationContentView showingLayout = getShowingLayout();
         return showingLayout.getMinHeight();
@@ -2319,10 +2344,6 @@
         }
     }
 
-    public boolean isMaxExpandHeightInitialized() {
-        return mMaxExpandHeight != 0;
-    }
-
     public NotificationContentView getShowingLayout() {
         return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index 8bc2201..67268c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -276,12 +276,18 @@
         setClipToOutline(mAlwaysRoundBothCorners);
     }
 
-    public void setTopRoundness(float topRoundness, boolean animate) {
+    /**
+     * Set the topRoundness of this view.
+     * @return Whether the roundness was changed.
+     */
+    public boolean setTopRoundness(float topRoundness, boolean animate) {
         if (mTopRoundness != topRoundness) {
             mTopRoundness = topRoundness;
             PropertyAnimator.setProperty(this, TOP_ROUNDNESS, topRoundness,
                     ROUNDNESS_PROPERTIES, animate);
+            return true;
         }
+        return false;
     }
 
     protected void applyRoundness() {
@@ -305,12 +311,18 @@
         return mCurrentBottomRoundness * mOutlineRadius;
     }
 
-    public void setBottomRoundness(float bottomRoundness, boolean animate) {
+    /**
+     * Set the bottom roundness of this view.
+     * @return Whether the roundness was changed.
+     */
+    public boolean setBottomRoundness(float bottomRoundness, boolean animate) {
         if (mBottomRoundness != bottomRoundness) {
             mBottomRoundness = bottomRoundness;
             PropertyAnimator.setProperty(this, BOTTOM_ROUNDNESS, bottomRoundness,
                     ROUNDNESS_PROPERTIES, animate);
+            return true;
         }
+        return false;
     }
 
     protected void setBackgroundTop(int backgroundTop) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 204adc8..df6a977 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.graphics.Paint;
 import android.graphics.Rect;
@@ -296,19 +297,24 @@
 
     /**
      * Perform a remove animation on this view.
-     *
      * @param duration The duration of the remove animation.
+     * @param delay The delay of the animation
      * @param translationDirection The direction value from [-1 ... 1] indicating in which the
-     *                             animation should be performed. A value of -1 means that The
-     *                             remove animation should be performed upwards,
-     *                             such that the  child appears to be going away to the top. 1
-     *                             Should mean the opposite.
+ *                             animation should be performed. A value of -1 means that The
+ *                             remove animation should be performed upwards,
+ *                             such that the  child appears to be going away to the top. 1
+ *                             Should mean the opposite.
+     * @param isHeadsUpAnimation Is this a headsUp animation.
+     * @param endLocation The location where the horizonal heads up disappear animation should end.
      * @param onFinishedRunnable A runnable which should be run when the animation is finished.
+     * @param animationListener An animation listener to add to the animation.
      */
-    public abstract void performRemoveAnimation(long duration, float translationDirection,
-            Runnable onFinishedRunnable);
+    public abstract void performRemoveAnimation(long duration,
+            long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+            Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener);
 
-    public abstract void performAddAnimation(long delay, long duration);
+    public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear);
 
     /**
      * Set the notification appearance to be below the speed bump.
@@ -390,6 +396,10 @@
         }
     }
 
+    public float getHeaderVisibleAmount() {
+        return 1.0f;
+    }
+
     protected boolean shouldClipToActualHeight() {
         return true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
new file mode 100644
index 0000000..5e03fbf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -0,0 +1,193 @@
+/*
+ * 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.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+
+/**
+ * The view in the statusBar that contains part of the heads-up information
+ */
+public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
+    private int mAbsoluteStartPadding;
+    private int mEndMargin;
+    private View mIconPlaceholder;
+    private TextView mTextView;
+    private NotificationData.Entry mShowingEntry;
+    private Rect mLayoutedIconRect = new Rect();
+    private int[] mTmpPosition = new int[2];
+    private boolean mFirstLayout = true;
+    private boolean mPublicMode;
+    private int mMaxWidth;
+    private View mRootView;
+    private int mLeftInset;
+    private Rect mIconDrawingRect = new Rect();
+    private Runnable mOnDrawingRectChangedListener;
+
+    public HeadsUpStatusBarView(Context context) {
+        this(context, null);
+    }
+
+    public HeadsUpStatusBarView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        Resources res = getResources();
+        mAbsoluteStartPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings)
+            + res.getDimensionPixelSize(
+                    com.android.internal.R.dimen.notification_content_margin_start);
+        mEndMargin = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin_end);
+        setPaddingRelative(mAbsoluteStartPadding, 0, mEndMargin, 0);
+        updateMaxWidth();
+    }
+
+    private void updateMaxWidth() {
+        int maxWidth = getResources().getDimensionPixelSize(R.dimen.qs_panel_width);
+        if (maxWidth != mMaxWidth) {
+            // maxWidth doesn't work with fill_parent, let's manually make it at most as big as the
+            // notification panel
+            mMaxWidth = maxWidth;
+            requestLayout();
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mMaxWidth > 0) {
+            int newSize = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth);
+            widthMeasureSpec = MeasureSpec.makeMeasureSpec(newSize,
+                    MeasureSpec.getMode(widthMeasureSpec));
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateMaxWidth();
+    }
+
+    @VisibleForTesting
+    public HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView) {
+        this(context);
+        mIconPlaceholder = iconPlaceholder;
+        mTextView = textView;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mIconPlaceholder = findViewById(R.id.icon_placeholder);
+        mTextView = findViewById(R.id.text);
+    }
+
+    public void setEntry(NotificationData.Entry entry) {
+        if (entry != null) {
+            mShowingEntry = entry;
+            CharSequence text = entry.headsUpStatusBarText;
+            if (mPublicMode) {
+                text = entry.headsUpStatusBarTextPublic;
+            }
+            mTextView.setText(text);
+        } else {
+            mShowingEntry = null;
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        mIconPlaceholder.getLocationOnScreen(mTmpPosition);
+        int left = (int) (mTmpPosition[0] - getTranslationX());
+        int top = mTmpPosition[1];
+        int right = left + mIconPlaceholder.getWidth();
+        int bottom = top + mIconPlaceholder.getHeight();
+        mLayoutedIconRect.set(left, top, right, bottom);
+        updateDrawingRect();
+        int targetPadding = mAbsoluteStartPadding + mLeftInset;
+        if (left != targetPadding) {
+            int newPadding = targetPadding - left + getPaddingStart();
+            setPaddingRelative(newPadding, 0, mEndMargin, 0);
+        }
+        if (mFirstLayout) {
+            // we need to do the padding calculation in the first frame, so the layout specified
+            // our visibility to be INVISIBLE in the beginning. let's correct that and set it
+            // to GONE.
+            setVisibility(GONE);
+            mFirstLayout = false;
+        }
+    }
+
+    @Override
+    public void setTranslationX(float translationX) {
+        super.setTranslationX(translationX);
+        updateDrawingRect();
+    }
+
+    private void updateDrawingRect() {
+        float oldLeft = mIconDrawingRect.left;
+        mIconDrawingRect.set(mLayoutedIconRect);
+        mIconDrawingRect.offset((int) getTranslationX(), 0);
+        if (oldLeft != mIconDrawingRect.left && mOnDrawingRectChangedListener != null) {
+            mOnDrawingRectChangedListener.run();
+        }
+    }
+
+    @Override
+    protected boolean fitSystemWindows(Rect insets) {
+        mLeftInset = insets.left;
+        return super.fitSystemWindows(insets);
+    }
+
+    public NotificationData.Entry getShowingEntry() {
+        return mShowingEntry;
+    }
+
+    public Rect getIconDrawingRect() {
+        return mIconDrawingRect;
+    }
+
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
+    }
+
+    public void setPublicMode(boolean publicMode) {
+        mPublicMode = publicMode;
+    }
+
+    public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
+        mOnDrawingRectChangedListener = onDrawingRectChangedListener;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 73c8795..f64b1bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -602,14 +602,15 @@
                     && (mIsHeadsUp || mHeadsUpAnimatingAway)
                     && !mContainingNotification.isOnKeyguard();
             if (transitioningBetweenHunAndExpanded || pinned) {
-                return Math.min(mHeadsUpChild.getHeight(), mExpandedChild.getHeight());
+                return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
+                        getViewHeight(VISIBLE_TYPE_EXPANDED));
             }
         }
 
         // Size change of the expanded version
         if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
                 && mExpandedChild != null) {
-            return Math.min(mContentHeightAtAnimationStart, mExpandedChild.getHeight());
+            return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
         }
 
         int hint;
@@ -619,16 +620,17 @@
                 VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
             hint = mAmbientSingleLineChild.getHeight();
         } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
-            hint = mHeadsUpChild.getHeight();
+            hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
         } else if (mExpandedChild != null) {
-            hint = mExpandedChild.getHeight();
+            hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
         } else {
-            hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.notification_action_list_height);
+            hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
+                    + mContext.getResources().getDimensionPixelSize(
+                            com.android.internal.R.dimen.notification_action_list_height);
         }
 
         if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
-            hint = Math.min(hint, mExpandedChild.getHeight());
+            hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
         }
         return hint;
     }
@@ -694,8 +696,8 @@
     }
 
     private float calculateTransformationAmount() {
-        int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
-        int endHeight = getViewForVisibleType(mVisibleType).getHeight();
+        int startHeight = getViewHeight(mTransformationStartVisibleType);
+        int endHeight = getViewHeight(mVisibleType);
         int progress = Math.abs(mContentHeight - startHeight);
         int totalDistance = Math.abs(endHeight - startHeight);
         if (totalDistance == 0) {
@@ -717,11 +719,23 @@
         if (mContainingNotification.isShowingAmbient()) {
             return getShowingAmbientView().getHeight();
         } else if (mExpandedChild != null) {
-            return mExpandedChild.getHeight() + getExtraRemoteInputHeight(mExpandedRemoteInput);
+            return getViewHeight(VISIBLE_TYPE_EXPANDED)
+                    + getExtraRemoteInputHeight(mExpandedRemoteInput);
         } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
-            return mHeadsUpChild.getHeight() + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+            return getViewHeight(VISIBLE_TYPE_HEADSUP)
+                    + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
         }
-        return mContractedChild.getHeight();
+        return getViewHeight(VISIBLE_TYPE_CONTRACTED);
+    }
+
+    private int getViewHeight(int visibleType) {
+        View view = getViewForVisibleType(visibleType);
+        int height = view.getHeight();
+        NotificationViewWrapper viewWrapper = getWrapperForView(view);
+        if (viewWrapper != null) {
+            height += viewWrapper.getHeaderTranslation();
+        }
+        return height;
     }
 
     public int getMinHeight() {
@@ -732,7 +746,7 @@
         if (mContainingNotification.isShowingAmbient()) {
             return getShowingAmbientView().getHeight();
         } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
-            return mContractedChild.getHeight();
+            return getViewHeight(VISIBLE_TYPE_CONTRACTED);
         } else {
             return mSingleLineView.getHeight();
         }
@@ -1046,7 +1060,7 @@
 
     private int getVisualTypeForHeight(float viewHeight) {
         boolean noExpandedChild = mExpandedChild == null;
-        if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
+        if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
             return VISIBLE_TYPE_EXPANDED;
         }
         if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
@@ -1055,13 +1069,13 @@
 
         if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
                 && !mContainingNotification.isOnKeyguard()) {
-            if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
+            if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
                 return VISIBLE_TYPE_HEADSUP;
             } else {
                 return VISIBLE_TYPE_EXPANDED;
             }
         } else {
-            if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
+            if (noExpandedChild || (viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
                     && (!mIsChildInGroup || isGroupExpanded()
                             || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
                 return VISIBLE_TYPE_CONTRACTED;
@@ -1616,19 +1630,19 @@
     }
 
     public int getExpandHeight() {
-        View expandedChild = mExpandedChild;
-        if (expandedChild == null) {
-            expandedChild = mContractedChild;
+        int viewType = VISIBLE_TYPE_EXPANDED;
+        if (mExpandedChild == null) {
+            viewType = VISIBLE_TYPE_CONTRACTED;
         }
-        return expandedChild.getHeight() + getExtraRemoteInputHeight(mExpandedRemoteInput);
+        return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
     }
 
     public int getHeadsUpHeight() {
-        View headsUpChild = mHeadsUpChild;
-        if (headsUpChild == null) {
-            headsUpChild = mContractedChild;
+        int viewType = VISIBLE_TYPE_HEADSUP;
+        if (mHeadsUpChild == null) {
+            viewType = VISIBLE_TYPE_CONTRACTED;
         }
-        return headsUpChild.getHeight()+ getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+        return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
     }
 
     public void setRemoteInputVisible(boolean remoteInputVisible) {
@@ -1641,4 +1655,16 @@
         clipChildren = clipChildren && !mRemoteInputVisible;
         super.setClipChildren(clipChildren);
     }
+
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        if (mContractedWrapper != null) {
+            mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+        }
+        if (mHeadsUpWrapper != null) {
+            mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+        }
+        if (mExpandedWrapper != null) {
+            mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 22a186f..775faee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -107,6 +107,8 @@
         public CharSequence remoteInputTextWhenReset;
         public long lastRemoteInputSent = NOT_LAUNCHED_YET;
         public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
+        public CharSequence headsUpStatusBarText;
+        public CharSequence headsUpStatusBarTextPublic;
 
         public Entry(StatusBarNotification n) {
             this.key = n.getKey();
@@ -529,6 +531,14 @@
         return null;
     }
 
+    public boolean shouldHide(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.isSuspended();
+        }
+        return false;
+    }
+
     private void updateRankingAndSort(RankingMap ranking) {
         if (ranking != null) {
             mRankingMap = ranking;
@@ -618,6 +628,10 @@
             return true;
         }
 
+        if (shouldHide(sbn.getKey())) {
+            return true;
+        }
+
         if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
                 && mGroupManager.isChildInGroupWithSummary(sbn)) {
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index cc584e7e..ccabb79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -389,7 +389,7 @@
      * "public" (secure & locked) mode?
      */
     private boolean userAllowsNotificationsInPublic(int userHandle) {
-        if (userHandle == UserHandle.USER_ALL) {
+        if (isCurrentProfile(userHandle)) {
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 4f09133..aecf5fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -177,6 +177,9 @@
             mShelfState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
                     getFullyClosedTranslation());
             mShelfState.zTranslation = ambientState.getBaseZHeight();
+            if (mAmbientState.isDark()) {
+                mShelfState.yTranslation = mAmbientState.getDarkTopPadding();
+            }
             float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
                     / (getIntrinsicHeight() * 2);
             openedAmount = Math.min(1.0f, openedAmount);
@@ -252,7 +255,8 @@
             }
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
             float notificationClipEnd;
-            boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight;
+            boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight
+                    || row.isPinned();
             boolean isLastChild = child == lastChild;
             float rowTranslationY = row.getTranslationY();
             if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) {
@@ -343,7 +347,7 @@
         float maxTop = row.getTranslationY();
         StatusBarIconView icon = row.getEntry().expandedIcon;
         float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
-        if (shelfIconPosition < maxTop) {
+        if (shelfIconPosition < maxTop && !mAmbientState.isDark()) {
             int top = (int) (maxTop - shelfIconPosition);
             Rect clipRect = new Rect(0, top, icon.getWidth(), Math.max(top, icon.getHeight()));
             icon.setClipBounds(clipRect);
@@ -354,7 +358,7 @@
 
     private void updateContinuousClipping(final ExpandableNotificationRow row) {
         StatusBarIconView icon = row.getEntry().expandedIcon;
-        boolean needsContinuousClipping = ViewState.isAnimatingY(icon);
+        boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDark();
         boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
         if (needsContinuousClipping && !isContinuousClipping) {
             ViewTreeObserver.OnPreDrawListener predrawListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
index 0a7ee51..badc40d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
@@ -112,14 +113,16 @@
     }
 
     @Override
-    public void performRemoveAnimation(long duration, float translationDirection,
-            Runnable onFinishedRunnable) {
+    public void performRemoveAnimation(long duration, long delay,
+            float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+            Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener) {
         // TODO: Use duration
         performVisibilityAnimation(false);
     }
 
     @Override
-    public void performAddAnimation(long delay, long duration) {
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
         // TODO: use delay and duration
         performVisibilityAnimation(true);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 6cfd42f..5bb85e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -27,7 +27,6 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.Rect;
@@ -43,7 +42,6 @@
 import android.util.Log;
 import android.util.Property;
 import android.util.TypedValue;
-import android.view.View;
 import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Interpolator;
@@ -144,6 +142,8 @@
     private ColorMatrixColorFilter mMatrixColorFilter;
     private boolean mIsInShelf;
     private Runnable mLayoutRunnable;
+    private boolean mDismissed;
+    private Runnable mOnDismissListener;
 
     public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
         this(context, slot, sbn, false);
@@ -668,6 +668,19 @@
     }
 
     public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) {
+        setVisibleState(visibleState, animate, endRunnable, 0);
+    }
+
+    /**
+     * Set the visibleState of this view.
+     *
+     * @param visibleState The new state.
+     * @param animate Should we animate?
+     * @param endRunnable The runnable to run at the end.
+     * @param duration The duration of an animation or 0 if the default should be taken.
+     */
+    public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable,
+            long duration) {
         boolean runnableAdded = false;
         if (visibleState != mVisibleState) {
             mVisibleState = visibleState;
@@ -689,7 +702,8 @@
                     mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT,
                             currentAmount, targetAmount);
                     mIconAppearAnimator.setInterpolator(interpolator);
-                    mIconAppearAnimator.setDuration(ANIMATION_DURATION_FAST);
+                    mIconAppearAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
+                            : duration);
                     mIconAppearAnimator.addListener(new AnimatorListenerAdapter() {
                         @Override
                         public void onAnimationEnd(Animator animation) {
@@ -711,8 +725,9 @@
                 if (targetAmount != currentAmount) {
                     mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
                             currentAmount, targetAmount);
-                    mDotAnimator.setInterpolator(interpolator);
-                    mDotAnimator.setDuration(ANIMATION_DURATION_FAST);
+                    mDotAnimator.setInterpolator(interpolator);;
+                    mDotAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
+                            : duration);
                     final boolean runRunnable = !runnableAdded;
                     mDotAnimator.addListener(new AnimatorListenerAdapter() {
                         @Override
@@ -837,6 +852,21 @@
         mLayoutRunnable = runnable;
     }
 
+    public void setDismissed() {
+        mDismissed = true;
+        if (mOnDismissListener != null) {
+            mOnDismissListener.run();
+        }
+    }
+
+    public boolean isDismissed() {
+        return mDismissed;
+    }
+
+    public void setOnDismissListener(Runnable onDismissListener) {
+        mOnDismissListener = onDismissListener;
+    }
+
     public interface OnVisibilityChangedListener {
         void onVisibilityChanged(int newVisibility);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index dfcd5e6..251e04b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -52,6 +52,7 @@
 
     protected final ViewInvertHelper mInvertHelper;
     protected final ViewTransformationHelper mTransformationHelper;
+    private final int mTranslationForHeader;
 
     protected int mColor;
     private ImageView mIcon;
@@ -63,6 +64,7 @@
     private boolean mIsLowPriority;
     private boolean mTransformLowPriorityTitle;
     private boolean mShowExpandButtonAtEnd;
+    protected float mHeaderTranslation;
 
     protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
         super(ctx, view, row);
@@ -99,6 +101,10 @@
         resolveHeaderViews();
         updateInvertHelper();
         addAppOpsOnClickListener(row);
+        mTranslationForHeader = ctx.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin)
+                - ctx.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.notification_content_margin_top);
     }
 
     @Override
@@ -243,6 +249,19 @@
     }
 
     @Override
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        super.setHeaderVisibleAmount(headerVisibleAmount);
+        mNotificationHeader.setAlpha(headerVisibleAmount);
+        mHeaderTranslation = (1.0f - headerVisibleAmount) * mTranslationForHeader;
+        mView.setTranslationY(mHeaderTranslation);
+    }
+
+    @Override
+    public int getHeaderTranslation() {
+        return (int) mHeaderTranslation;
+    }
+
+    @Override
     public NotificationHeaderView getNotificationHeader() {
         return mNotificationHeader;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
index f967118..f5110a2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -179,6 +179,9 @@
                     : builder.makeAmbientNotification();
         }
         result.packageContext = packageContext;
+        result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
+        result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
+                true /* showingPublic */);
         return result;
     }
 
@@ -456,6 +459,8 @@
                 }
                 entry.cachedAmbientContentView = result.newAmbientView;
             }
+            entry.headsUpStatusBarText = result.headsUpStatusBarText;
+            entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
             if (endListener != null) {
                 endListener.onAsyncInflationFinished(row.getEntry());
             }
@@ -665,6 +670,8 @@
         private View inflatedExpandedView;
         private View inflatedAmbientView;
         private View inflatedPublicView;
+        private CharSequence headsUpStatusBarText;
+        private CharSequence headsUpStatusBarTextPublic;
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index d463eae..28beb21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -278,7 +278,10 @@
         if (mActionsContainer != null) {
             // We should never push the actions higher than they are in the headsup view.
             int constrainedContentHeight = Math.max(mContentHeight, mMinHeightHint);
-            mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight());
+
+            // We also need to compensate for any header translation, since we're always at the end.
+            mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()
+                    - getHeaderTranslation());
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 17eb4c1..873f088 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -133,6 +133,10 @@
         return null;
     }
 
+    public int getHeaderTranslation() {
+        return 0;
+    }
+
     @Override
     public TransformState getCurrentState(int fadingView) {
         return null;
@@ -198,4 +202,7 @@
     public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
         return false;
     }
+
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index fb3adf4..07b79a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.os.PowerManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -24,6 +25,7 @@
 import android.util.MathUtils;
 import android.util.SparseBooleanArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -36,19 +38,35 @@
     private static final int MAX_DURATION = 60 * 1000;
     public static final String DOZE_SENSORS_WAKE_UP_FULLY = "doze_sensors_wake_up_fully";
 
+    private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;
+    private static DozeParameters sInstance;
+
     private final Context mContext;
     private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+    private final PowerManager mPowerManager;
 
-    private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;
     private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
 
     private boolean mDozeAlwaysOn;
+    private boolean mControlScreenOffAnimation;
 
-    public DozeParameters(Context context) {
+    public static DozeParameters getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new DozeParameters(context);
+        }
+        return sInstance;
+    }
+
+    @VisibleForTesting
+    protected DozeParameters(Context context) {
         mContext = context;
         mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
         mAlwaysOnPolicy = new AlwaysOnDisplayPolicy(context);
 
+        mControlScreenOffAnimation = !getDisplayNeedsBlanking();
+        mPowerManager = mContext.getSystemService(PowerManager.class);
+        mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
+
         Dependency.get(TunerService.class).addTunable(this, Settings.Secure.DOZE_ALWAYS_ON,
                 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
     }
@@ -165,15 +183,21 @@
                 com.android.internal.R.bool.config_displayBlanksAfterDoze);
     }
 
-    /**
-     * Whether we can implement our own screen off animation or if we need
-     * to rely on DisplayPowerManager to dim the display.
-     *
-     * @return {@code true} if SystemUI can control the screen off animation.
-     */
-    public boolean getCanControlScreenOffAnimation() {
-        return !mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_dozeAfterScreenOff);
+    public boolean shouldControlScreenOff() {
+        return mControlScreenOffAnimation;
+    }
+
+    public void setControlScreenOffAnimation(boolean controlScreenOffAnimation) {
+        if (mControlScreenOffAnimation == controlScreenOffAnimation) {
+            return;
+        }
+        mControlScreenOffAnimation = controlScreenOffAnimation;
+        getPowerManager().setDozeAfterScreenOff(!controlScreenOffAnimation);
+    }
+
+    @VisibleForTesting
+    protected PowerManager getPowerManager() {
+        return mPowerManager;
     }
 
     private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 1011383..afd64f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -78,9 +78,10 @@
         }
     };
 
-    public DozeScrimController(ScrimController scrimController, Context context) {
+    public DozeScrimController(ScrimController scrimController, Context context,
+            DozeParameters dozeParameters) {
         mScrimController = scrimController;
-        mDozeParameters = new DozeParameters(context);
+        mDozeParameters = dozeParameters;
     }
 
     public void setDozing(boolean dozing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
new file mode 100644
index 0000000..c576801
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -0,0 +1,225 @@
+/*
+ * 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.phone;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+/**
+ * Controls the appearance of heads up notifications in the icon area and the header itself.
+ */
+class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
+        DarkIconDispatcher.DarkReceiver {
+    public static final int CONTENT_FADE_DURATION = 110;
+    public static final int CONTENT_FADE_DELAY = 100;
+    private final NotificationIconAreaController mNotificationIconAreaController;
+    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final NotificationStackScrollLayout mStackScroller;
+    private final HeadsUpStatusBarView mHeadsUpStatusBarView;
+    private final View mClockView;
+    private final DarkIconDispatcher mDarkIconDispatcher;
+    private float mExpandedHeight;
+    private boolean mIsExpanded;
+    private float mExpandFraction;
+    private ExpandableNotificationRow mTrackedChild;
+    private boolean mShown;
+
+    public HeadsUpAppearanceController(
+            NotificationIconAreaController notificationIconAreaController,
+            HeadsUpManagerPhone headsUpManager,
+            View statusbarView) {
+        this(notificationIconAreaController, headsUpManager,
+                statusbarView.findViewById(R.id.heads_up_status_bar_view),
+                statusbarView.findViewById(R.id.notification_stack_scroller),
+                statusbarView.findViewById(R.id.notification_panel),
+                statusbarView.findViewById(R.id.clock));
+    }
+
+    @VisibleForTesting
+    public HeadsUpAppearanceController(
+            NotificationIconAreaController notificationIconAreaController,
+            HeadsUpManagerPhone headsUpManager,
+            HeadsUpStatusBarView headsUpStatusBarView,
+            NotificationStackScrollLayout stackScroller,
+            NotificationPanelView panelView,
+            View clockView) {
+        mNotificationIconAreaController = notificationIconAreaController;
+        mHeadsUpManager = headsUpManager;
+        mHeadsUpManager.addListener(this);
+        mHeadsUpStatusBarView = headsUpStatusBarView;
+        headsUpStatusBarView.setOnDrawingRectChangedListener(
+                () -> updateIsolatedIconLocation(true /* requireUpdate */));
+        mStackScroller = stackScroller;
+        panelView.addTrackingHeadsUpListener(this::setTrackingHeadsUp);
+        panelView.setVerticalTranslationListener(this::updatePanelTranslation);
+        panelView.setHeadsUpAppearanceController(this);
+        mStackScroller.addOnExpandedHeightListener(this::setExpandedHeight);
+        mStackScroller.addOnLayoutChangeListener(
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
+                        -> updatePanelTranslation());
+        mClockView = clockView;
+        mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
+        mDarkIconDispatcher.addDarkReceiver(this);
+    }
+
+    private void updateIsolatedIconLocation(boolean requireStateUpdate) {
+        mNotificationIconAreaController.setIsolatedIconLocation(
+                mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate);
+    }
+
+    @Override
+    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+        updateTopEntry();
+        updateHeader(headsUp.getEntry());
+    }
+
+    public void updatePanelTranslation() {
+        float newTranslation = mStackScroller.getLeft() + mStackScroller.getTranslationX();
+        mHeadsUpStatusBarView.setTranslationX(newTranslation);
+    }
+
+    private void updateTopEntry() {
+        NotificationData.Entry newEntry = null;
+        if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
+            newEntry = mHeadsUpManager.getTopEntry();
+        }
+        NotificationData.Entry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
+        mHeadsUpStatusBarView.setEntry(newEntry);
+        if (newEntry != previousEntry) {
+            boolean animateIsolation = false;
+            if (newEntry == null) {
+                // no heads up anymore, lets start the disappear animation
+
+                setShown(false);
+                animateIsolation = !mIsExpanded;
+            } else if (previousEntry == null) {
+                // We now have a headsUp and didn't have one before. Let's start the disappear
+                // animation
+                setShown(true);
+                animateIsolation = !mIsExpanded;
+            }
+            updateIsolatedIconLocation(false /* requireUpdate */);
+            mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
+                    : newEntry.icon, animateIsolation);
+        }
+    }
+
+    private void setShown(boolean isShown) {
+        if (mShown != isShown) {
+            mShown = isShown;
+            if (isShown) {
+                mHeadsUpStatusBarView.setVisibility(View.VISIBLE);
+                CrossFadeHelper.fadeIn(mHeadsUpStatusBarView, CONTENT_FADE_DURATION /* duration */,
+                        CONTENT_FADE_DELAY /* delay */);
+                CrossFadeHelper.fadeOut(mClockView, CONTENT_FADE_DURATION/* duration */,
+                        0 /* delay */, () -> mClockView.setVisibility(View.INVISIBLE));
+            } else {
+                CrossFadeHelper.fadeIn(mClockView, CONTENT_FADE_DURATION /* duration */,
+                        CONTENT_FADE_DELAY /* delay */);
+                CrossFadeHelper.fadeOut(mHeadsUpStatusBarView, CONTENT_FADE_DURATION/* duration */,
+                        0 /* delay */, () -> mHeadsUpStatusBarView.setVisibility(View.GONE));
+
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public boolean isShown() {
+        return mShown;
+    }
+
+    /**
+     * Should the headsup status bar view be visible right now? This may be different from isShown,
+     * since the headsUp manager might not have notified us yet of the state change.
+     *
+     * @return if the heads up status bar view should be shown
+     */
+    public boolean shouldBeVisible() {
+        return !mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp();
+    }
+
+    @Override
+    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+        updateTopEntry();
+        updateHeader(headsUp.getEntry());
+    }
+
+    public void setExpandedHeight(float expandedHeight, float appearFraction) {
+        boolean changedHeight = expandedHeight != mExpandedHeight;
+        mExpandedHeight = expandedHeight;
+        mExpandFraction = appearFraction;
+        boolean isExpanded = expandedHeight > 0;
+        if (changedHeight) {
+            updateHeadsUpHeaders();
+        }
+        if (isExpanded != mIsExpanded) {
+            mIsExpanded = isExpanded;
+            updateTopEntry();
+        }
+    }
+
+    /**
+     * Set a headsUp to be tracked, meaning that it is currently being pulled down after being
+     * in a pinned state on the top. The expand animation is different in that case and we need
+     * to update the header constantly afterwards.
+     *
+     * @param trackedChild the tracked headsUp or null if it's not tracking anymore.
+     */
+    public void setTrackingHeadsUp(ExpandableNotificationRow trackedChild) {
+        ExpandableNotificationRow previousTracked = mTrackedChild;
+        mTrackedChild = trackedChild;
+        if (previousTracked != null) {
+            updateHeader(previousTracked.getEntry());
+        }
+    }
+
+    private void updateHeadsUpHeaders() {
+        mHeadsUpManager.getAllEntries().forEach(entry -> {
+            updateHeader(entry);
+        });
+    }
+
+    private void updateHeader(NotificationData.Entry entry) {
+        ExpandableNotificationRow row = entry.row;
+        float headerVisibleAmount = 1.0f;
+        if (row.isPinned() || row == mTrackedChild) {
+            headerVisibleAmount = mExpandFraction;
+        }
+        row.setHeaderVisibleAmount(headerVisibleAmount);
+    }
+
+    @Override
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint);
+    }
+
+    public void setPublicMode(boolean publicMode) {
+        mHeadsUpStatusBarView.setPublicMode(publicMode);
+        updateTopEntry();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index d2cdc27..fa0a774 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -29,6 +29,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
+import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
@@ -52,12 +53,13 @@
     private static final boolean DEBUG = false;
 
     private final View mStatusBarWindowView;
-    private int mStatusBarHeight;
     private final NotificationGroupManager mGroupManager;
     private final StatusBar mBar;
     private final VisualStabilityManager mVisualStabilityManager;
-
     private boolean mReleaseOnExpandFinish;
+
+    private int mStatusBarHeight;
+    private int mHeadsUpInset;
     private boolean mTrackingHeadsUp;
     private HashSet<String> mSwipedOutKeys = new HashSet<>();
     private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
@@ -101,9 +103,7 @@
         mBar = bar;
         mVisualStabilityManager = visualStabilityManager;
 
-        Resources resources = mContext.getResources();
-        mStatusBarHeight = resources.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
+        initResources();
 
         addListener(new OnHeadsUpChangedListener() {
             @Override
@@ -114,6 +114,20 @@
         });
     }
 
+    private void initResources() {
+        Resources resources = mContext.getResources();
+        mStatusBarHeight = resources.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+        mHeadsUpInset = mStatusBarHeight + resources.getDimensionPixelSize(
+                R.dimen.heads_up_status_bar_padding);
+    }
+
+    @Override
+    public void onDensityOrFontScaleChanged() {
+        super.onDensityOrFontScaleChanged();
+        initResources();
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     //  Public methods:
 
@@ -283,10 +297,10 @@
             topEntry.getLocationOnScreen(mTmpTwoArray);
             int minX = mTmpTwoArray[0];
             int maxX = mTmpTwoArray[0] + topEntry.getWidth();
-            int maxY = topEntry.getIntrinsicHeight();
+            int height = topEntry.getIntrinsicHeight();
 
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            info.touchableRegion.set(minX, 0, maxX, maxY);
+            info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
         } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
             info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 2bfdefe..903b813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -23,7 +23,6 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 /**
@@ -102,10 +101,11 @@
                     mCollapseSnoozes = h < 0;
                     mInitialTouchX = x;
                     mInitialTouchY = y;
-                    int expandedHeight = mPickedChild.getActualHeight();
-                    mPanel.setPanelScrimMinFraction((float) expandedHeight
+                    int startHeight = (int) (mPickedChild.getActualHeight()
+                                                + mPickedChild.getTranslationY());
+                    mPanel.setPanelScrimMinFraction((float) startHeight
                             / mPanel.getMaxPanelHeight());
-                    mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight);
+                    mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
                     mPanel.startExpandingFromPeek();
                     // This call needs to be after the expansion start otherwise we will get a
                     // flicker of one frame as it's not expanded yet.
@@ -135,7 +135,7 @@
     private void setTrackingHeadsUp(boolean tracking) {
         mTrackingHeadsUp = tracking;
         mHeadsUpManager.setTrackingHeadsUp(tracking);
-        mPanel.setTrackingHeadsUp(tracking);
+        mPanel.setTrackedHeadsUp(tracking ? mPickedChild : null);
     }
 
     public void notifyFling(boolean collapse) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index b493d7a..a39800d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -464,7 +464,10 @@
     private void onRotationSuggestionsDisabled() {
         // Immediately hide the rotate button and clear any planned removal
         setRotateSuggestionButtonState(false, true);
-        getView().removeCallbacks(mRemoveRotationProposal);
+
+        // This method can be called before view setup is done, ensure getView isn't null
+        final View v = getView();
+        if (v != null) v.removeCallbacks(mRemoveRotationProposal);
     }
 
     private void showAndLogRotationSuggestion() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e1aed7a..9063dea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -12,9 +12,11 @@
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.NotificationColorUtil;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationEntryManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -31,6 +33,8 @@
  */
 public class NotificationIconAreaController implements DarkReceiver {
     private final NotificationColorUtil mNotificationColorUtil;
+    private final NotificationEntryManager mEntryManager;
+    private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
 
     private int mIconSize;
     private int mIconHPadding;
@@ -48,6 +52,7 @@
         mStatusBar = statusBar;
         mNotificationColorUtil = NotificationColorUtil.getInstance(context);
         mContext = context;
+        mEntryManager = Dependency.get(NotificationEntryManager.class);
 
         initializeNotificationAreaViews(context);
     }
@@ -129,8 +134,8 @@
     }
 
     protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
-            NotificationData notificationData, boolean showAmbient) {
-        if (notificationData.isAmbient(entry.key) && !showAmbient) {
+            boolean showAmbient, boolean hideDismissed) {
+        if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
             return false;
         }
         if (!StatusBar.isTopLevelChild(entry)) {
@@ -139,9 +144,13 @@
         if (entry.row.getVisibility() == View.GONE) {
             return false;
         }
+        if (entry.row.isDismissed() && hideDismissed) {
+            return false;
+        }
 
         // showAmbient == show in shade but not shelf
-        if (!showAmbient && notificationData.shouldSuppressStatusBar(entry.key)) {
+        if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(
+                entry.key)) {
             return false;
         }
 
@@ -151,28 +160,30 @@
     /**
      * Updates the notifications with the given list of notifications to display.
      */
-    public void updateNotificationIcons(NotificationData notificationData) {
+    public void updateNotificationIcons() {
 
-        updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons,
-                false /* showAmbient */);
-        updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons,
-                NotificationShelf.SHOW_AMBIENT_ICONS);
+        updateStatusBarIcons();
+        updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
+                NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */);
 
         applyNotificationIconsTint();
     }
 
+    private void updateStatusBarIcons() {
+        updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
+                false /* showAmbient */, true /* hideDismissed */);
+    }
+
     /**
      * Updates the notification icons for a host layout. This will ensure that the notification
      * host layout will have the same icons like the ones in here.
-     *
-     * @param notificationData the notification data to look up which notifications are relevant
      * @param function A function to look up an icon view based on an entry
      * @param hostLayout which layout should be updated
      * @param showAmbient should ambient notification icons be shown
+     * @param hideDismissed should dismissed icons be hidden
      */
-    private void updateIconsForLayout(NotificationData notificationData,
-            Function<NotificationData.Entry, StatusBarIconView> function,
-            NotificationIconContainer hostLayout, boolean showAmbient) {
+    private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
+            NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                 mNotificationScrollLayout.getChildCount());
 
@@ -181,7 +192,7 @@
             View view = mNotificationScrollLayout.getChildAt(i);
             if (view instanceof ExpandableNotificationRow) {
                 NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
-                if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) {
+                if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed)) {
                     toShow.add(function.apply(ent));
                 }
             }
@@ -243,10 +254,13 @@
 
         final FrameLayout.LayoutParams params = generateIconLayoutParams();
         for (int i = 0; i < toShow.size(); i++) {
-            View v = toShow.get(i);
+            StatusBarIconView v = toShow.get(i);
             // The view might still be transiently added if it was just removed and added again
             hostLayout.removeTransientView(v);
             if (v.getParent() == null) {
+                if (hideDismissed) {
+                    v.setOnDismissListener(mUpdateStatusBarIcons);
+                }
                 hostLayout.addView(v, i, params);
             }
         }
@@ -296,4 +310,12 @@
         mNotificationIcons.setDark(dark, false, 0);
         mShelfIcons.setDark(dark, false, 0);
     }
+
+    public void showIconIsolated(StatusBarIconView icon, boolean animated) {
+        mNotificationIcons.showIconIsolated(icon, animated);
+    }
+
+    public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) {
+        mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index b29ac90..5517434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -16,17 +16,17 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DURATION;
+import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY;
+
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
 import android.support.v4.util.ArrayMap;
-import android.support.v4.util.ArraySet;
 import android.util.AttributeSet;
 import android.view.View;
 
@@ -100,6 +100,33 @@
         }
     }.setDuration(200).setDelay(50);
 
+    /**
+     * The animation property used for all icons that were not isolated, when the isolation ends.
+     * This just fades the alpha and doesn't affect the movement and has a delay.
+     */
+    private static final AnimationProperties UNISOLATION_PROPERTY_OTHERS
+            = new AnimationProperties() {
+        private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
+
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    }.setDuration(CONTENT_FADE_DURATION);
+
+    /**
+     * The animation property used for the icon when its isolation ends.
+     * This animates the translation back to the right position.
+     */
+    private static final AnimationProperties UNISOLATION_PROPERTY = new AnimationProperties() {
+        private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
+
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    }.setDuration(CONTENT_FADE_DURATION);
+
     public static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5;
     public static final int MAX_STATIC_ICONS = 4;
     private static final int MAX_DOTS = 3;
@@ -127,6 +154,10 @@
     private float mVisualOverflowStart;
     // Keep track of overflow in range [0, 3]
     private int mNumDots;
+    private StatusBarIconView mIsolatedIcon;
+    private Rect mIsolatedIconLocation;
+    private int[] mAbsolutePosition = new int[2];
+    private View mIsolatedIconForAnimation;
 
 
     public NotificationIconContainer(Context context, AttributeSet attrs) {
@@ -196,13 +227,18 @@
                 mIconSize = child.getWidth();
             }
         }
+        getLocationOnScreen(mAbsolutePosition);
         if (mIsStaticLayout) {
-            resetViewStates();
-            calculateIconTranslations();
-            applyIconStates();
+            updateState();
         }
     }
 
+    private void updateState() {
+        resetViewStates();
+        calculateIconTranslations();
+        applyIconStates();
+    }
+
     public void applyIconStates() {
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
@@ -214,6 +250,7 @@
         mAddAnimationStartIndex = -1;
         mCannedAnimationStartIndex = -1;
         mDisallowNextAnimation = false;
+        mIsolatedIconForAnimation = null;
     }
 
     @Override
@@ -281,8 +318,10 @@
                 mIconStates.remove(child);
                 if (!isReplacingIcon) {
                     addTransientView(icon, 0);
+                    boolean isIsolatedIcon = child == mIsolatedIcon;
                     icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */,
-                            () -> removeTransientView(icon));
+                            () -> removeTransientView(icon),
+                            isIsolatedIcon ? CONTENT_FADE_DURATION : 0);
                 }
             }
         }
@@ -306,7 +345,7 @@
             View view = getChildAt(i);
             ViewState iconState = mIconStates.get(view);
             iconState.initFrom(view);
-            iconState.alpha = 1.0f;
+            iconState.alpha = mIsolatedIcon == null || view == mIsolatedIcon ? 1.0f : 0.0f;
             iconState.hidden = false;
         }
     }
@@ -402,6 +441,16 @@
                 iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth();
             }
         }
+        if (mIsolatedIcon != null) {
+            IconState iconState = mIconStates.get(mIsolatedIcon);
+            if (iconState != null) {
+                // Most of the time the icon isn't yet added when this is called but only happening
+                // later
+                iconState.xTranslation = mIsolatedIconLocation.left - mAbsolutePosition[0]
+                        - (1 - mIsolatedIcon.getIconScale()) * mIsolatedIcon.getWidth() / 2.0f;
+                iconState.visibleState = StatusBarIconView.STATE_ICON;
+            }
+        }
     }
 
     private float getLayoutEnd() {
@@ -573,6 +622,21 @@
         mReplacingIcons = replacingIcons;
     }
 
+    public void showIconIsolated(StatusBarIconView icon, boolean animated) {
+        if (animated) {
+            mIsolatedIconForAnimation = icon != null ? icon : mIsolatedIcon;
+        }
+        mIsolatedIcon = icon;
+        updateState();
+    }
+
+    public void setIsolatedIconLocation(Rect isolatedIconLocation, boolean requireUpdate) {
+        mIsolatedIconLocation = isolatedIconLocation;
+        if (requireUpdate) {
+            updateState();
+        }
+    }
+
     public class IconState extends ViewState {
         public static final int NO_VALUE = NotificationIconContainer.NO_VALUE;
         public float iconAppearAmount = 1.0f;
@@ -646,6 +710,18 @@
                         animationProperties.setDuration(CANNED_ANIMATION_DURATION);
                         animate = true;
                     }
+                    if (mIsolatedIconForAnimation != null) {
+                        if (view == mIsolatedIconForAnimation) {
+                            animationProperties = UNISOLATION_PROPERTY;
+                            animationProperties.setDelay(
+                                    mIsolatedIcon != null ? CONTENT_FADE_DELAY : 0);
+                        } else {
+                            animationProperties = UNISOLATION_PROPERTY_OTHERS;
+                            animationProperties.setDelay(
+                                    mIsolatedIcon == null ? CONTENT_FADE_DELAY : 0);
+                        }
+                        animate = true;
+                    }
                 }
                 icon.setVisibleState(visibleState, animationsAllowed);
                 icon.setIconColor(iconColor, needsCannedAnimation && animationsAllowed);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 2711d7a..6852df6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -74,7 +74,9 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 
 public class NotificationPanelView extends PanelView implements
         ExpandableView.OnHeightChangedListener,
@@ -243,6 +245,10 @@
     private float mExpandOffset;
     private boolean mHideIconsDuringNotificationLaunch = true;
     private int mStackScrollerMeasuringPass;
+    private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners
+            = new ArrayList<>();
+    private Runnable mVerticalTranslationListener;
+    private HeadsUpAppearanceController mHeadsUpAppearanceController;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -269,6 +275,7 @@
         mNotificationStackScroller.setOnHeightChangedListener(this);
         mNotificationStackScroller.setOverscrollTopChangedListener(this);
         mNotificationStackScroller.setOnEmptySpaceClickListener(this);
+        addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp);
         mKeyguardBottomArea = findViewById(R.id.keyguard_bottom_area);
         mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
         mLastOrientation = getResources().getConfiguration().orientation;
@@ -1139,6 +1146,14 @@
         @Override
         public void run() {
             mKeyguardStatusViewAnimating = false;
+            mKeyguardStatusView.setVisibility(View.INVISIBLE);
+        }
+    };
+
+    private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mKeyguardStatusViewAnimating = false;
             mKeyguardStatusView.setVisibility(View.GONE);
         }
     };
@@ -1227,16 +1242,17 @@
 
     private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway,
             boolean goingToFullShade) {
+        mKeyguardStatusView.animate().cancel();
+        mKeyguardStatusViewAnimating = false;
         if ((!keyguardFadingAway && mStatusBarState == StatusBarState.KEYGUARD
                 && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) {
-            mKeyguardStatusView.animate().cancel();
             mKeyguardStatusViewAnimating = true;
             mKeyguardStatusView.animate()
                     .alpha(0f)
                     .setStartDelay(0)
                     .setDuration(160)
                     .setInterpolator(Interpolators.ALPHA_OUT)
-                    .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable);
+                    .withEndAction(mAnimateKeyguardStatusViewGoneEndRunnable);
             if (keyguardFadingAway) {
                 mKeyguardStatusView.animate()
                         .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
@@ -1245,7 +1261,6 @@
             }
         } else if (mStatusBarState == StatusBarState.SHADE_LOCKED
                 && statusBarState == StatusBarState.KEYGUARD) {
-            mKeyguardStatusView.animate().cancel();
             mKeyguardStatusView.setVisibility(View.VISIBLE);
             mKeyguardStatusViewAnimating = true;
             mKeyguardStatusView.setAlpha(0f);
@@ -1256,13 +1271,21 @@
                     .setInterpolator(Interpolators.ALPHA_IN)
                     .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
         } else if (statusBarState == StatusBarState.KEYGUARD) {
-            mKeyguardStatusView.animate().cancel();
-            mKeyguardStatusViewAnimating = false;
-            mKeyguardStatusView.setVisibility(View.VISIBLE);
-            mKeyguardStatusView.setAlpha(1f);
+            if (keyguardFadingAway) {
+                mKeyguardStatusViewAnimating = true;
+                mKeyguardStatusView.animate()
+                        .alpha(0)
+                        .translationYBy(-getHeight() * 0.05f)
+                        .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+                        .setDuration(125)
+                        .setStartDelay(0)
+                        .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
+                        .start();
+            } else {
+                mKeyguardStatusView.setVisibility(View.VISIBLE);
+                mKeyguardStatusView.setAlpha(1f);
+            }
         } else {
-            mKeyguardStatusView.animate().cancel();
-            mKeyguardStatusViewAnimating = false;
             mKeyguardStatusView.setVisibility(View.GONE);
             mKeyguardStatusView.setAlpha(1f);
         }
@@ -1780,11 +1803,19 @@
         mQsExpandImmediate = false;
         mTwoFingerQsExpandPossible = false;
         mIsExpansionFromHeadsUp = false;
-        mNotificationStackScroller.setTrackingHeadsUp(false);
+        notifyListenersTrackingHeadsUp(null);
         mExpandingFromHeadsUp = false;
         setPanelScrimMinFraction(0.0f);
     }
 
+    private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
+        for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
+            Consumer<ExpandableNotificationRow> listener
+                    = mTrackingHeadsUpListeners.get(i);
+            listener.accept(pickedChild);
+        }
+    }
+
     private void setListening(boolean listening) {
         mKeyguardStatusBar.setListening(listening);
         if (mQs == null) return;
@@ -2197,14 +2228,6 @@
         return (1 - t) * start + t * end;
     }
 
-    public void setDozing(boolean dozing, boolean animate) {
-        if (dozing == mDozing) return;
-        mDozing = dozing;
-        if (mStatusBarState == StatusBarState.KEYGUARD) {
-            updateDozingVisibilities(animate);
-        }
-    }
-
     private void updateDozingVisibilities(boolean animate) {
         if (mDozing) {
             mKeyguardStatusBar.setVisibility(View.INVISIBLE);
@@ -2351,9 +2374,9 @@
                 this);
     }
 
-    public void setTrackingHeadsUp(boolean tracking) {
-        if (tracking) {
-            mNotificationStackScroller.setTrackingHeadsUp(true);
+    public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
+        if (pickedChild != null) {
+            notifyListenersTrackingHeadsUp(pickedChild);
             mExpandingFromHeadsUp = true;
         }
         // otherwise we update the state when the expansion is finished
@@ -2400,6 +2423,9 @@
     protected void setVerticalPanelTranslation(float translation) {
         mNotificationStackScroller.setTranslationX(translation);
         mQsFrame.setTranslationX(translation);
+        if (mVerticalTranslationListener != null) {
+            mVerticalTranslationListener.run();
+        }
     }
 
     protected void updateExpandedHeight(float expandedHeight) {
@@ -2555,6 +2581,10 @@
         if (mLaunchingNotification) {
             return mHideIconsDuringNotificationLaunch;
         }
+        if (mHeadsUpAppearanceController != null
+                && mHeadsUpAppearanceController.shouldBeVisible()) {
+            return false;
+        }
         return !isFullWidth() || !mShowIconsWhenExpanded;
     }
 
@@ -2600,11 +2630,16 @@
         }
     }
 
-    public void setDark(boolean dark, boolean animate) {
-        float darkAmount = dark ? 1 : 0;
-        if (mDarkAmount == darkAmount) {
-            return;
+    public void setDozing(boolean dozing, boolean animate) {
+        if (dozing == mDozing) return;
+        mDozing = dozing;
+
+        if (mStatusBarState == StatusBarState.KEYGUARD
+                || mStatusBarState == StatusBarState.SHADE_LOCKED) {
+            updateDozingVisibilities(animate);
         }
+
+        final float darkAmount = dozing ? 1 : 0;
         if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
             if (animate && mDarkAmountTarget == darkAmount) {
                 return;
@@ -2696,4 +2731,26 @@
             }
         }
     }
+
+    public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+        mTrackingHeadsUpListeners.add(listener);
+    }
+
+    public void setVerticalTranslationListener(Runnable verticalTranslationListener) {
+        mVerticalTranslationListener = verticalTranslationListener;
+    }
+
+    public void setHeadsUpAppearanceController(
+            HeadsUpAppearanceController headsUpAppearanceController) {
+        mHeadsUpAppearanceController = headsUpAppearanceController;
+    }
+
+    /**
+     * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
+     * security view of the bouncer.
+     */
+    public void onBouncerPreHideAnimation() {
+        setKeyguardStatusViewVisibility(mStatusBarState, true /* keyguardFadingAway */,
+                false /* goingToFullShade */);
+    }
 }
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 ae55ae8e..cfc0cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -49,7 +49,6 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.stack.ViewState;
 import com.android.systemui.util.AlarmTimeout;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
@@ -63,8 +62,8 @@
  * Controls both the scrim behind the notifications and in front of the notifications (when a
  * security method gets shown).
  */
-public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
-        OnHeadsUpChangedListener, OnColorsChangedListener, Dumpable {
+public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener,
+        Dumpable {
 
     private static final String TAG = "ScrimController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -106,7 +105,6 @@
     private final Context mContext;
     protected final ScrimView mScrimBehind;
     protected final ScrimView mScrimInFront;
-    private final View mHeadsUpScrim;
     private final LightBarController mLightBarController;
     private final UnlockMethodCache mUnlockMethodCache;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -140,9 +138,6 @@
     private int mCurrentInFrontTint;
     private int mCurrentBehindTint;
     private boolean mWallpaperVisibilityTimedOut;
-    private int mPinnedHeadsUpCount;
-    private float mTopHeadsUpDragAmount;
-    private View mDraggedHeadsUpView;
     private int mScrimsVisibility;
     private final Consumer<Integer> mScrimVisibleListener;
     private boolean mBlankScreen;
@@ -158,13 +153,13 @@
 
     private final WakeLock mWakeLock;
     private boolean mWakeLockHeld;
+    private boolean mKeyguardOccluded;
 
     public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
-            ScrimView scrimInFront, View headsUpScrim, Consumer<Integer> scrimVisibleListener,
+            ScrimView scrimInFront, Consumer<Integer> scrimVisibleListener,
             DozeParameters dozeParameters, AlarmManager alarmManager) {
         mScrimBehind = scrimBehind;
         mScrimInFront = scrimInFront;
-        mHeadsUpScrim = headsUpScrim;
         mScrimVisibleListener = scrimVisibleListener;
         mContext = scrimBehind.getContext();
         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
@@ -195,7 +190,6 @@
         }
         mState = ScrimState.UNINITIALIZED;
 
-        updateHeadsUpScrim(false);
         updateScrims();
     }
 
@@ -268,12 +262,13 @@
 
         // AOD wallpapers should fade away after a while
         if (mWallpaperSupportsAmbientMode && mDozeParameters.getAlwaysOn()
-                && (mState == ScrimState.AOD || mState == ScrimState.PULSING)) {
+                && mState == ScrimState.AOD) {
             if (!mWallpaperVisibilityTimedOut) {
                 mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
                         AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
             }
-        } else {
+        // Do not re-schedule timeout when pulsing, let's save some extra battery.
+        } else if (mState != ScrimState.PULSING) {
             mTimeTicker.cancel();
             mWallpaperVisibilityTimedOut = false;
         }
@@ -283,10 +278,11 @@
             // with too many things at this case, in order to not skip the initial frames.
             mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
             mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
-        } else if (!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD) {
-            // Execute first frame immediately when display was completely off.
-            // Scheduling a frame isn't enough because the system may aggressively enter doze,
-            // delaying callbacks or never triggering them until the power button is pressed.
+        } else if (!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD
+                || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
+            // Scheduling a frame isn't enough when:
+            //  • Leaving doze and we need to modify scrim color immediately
+            //  • ColorFade will not kick-in and scrim cannot wait for pre-draw.
             onPreDraw();
         } else {
             scheduleUpdate();
@@ -317,7 +313,7 @@
 
     @VisibleForTesting
     protected void onHideWallpaperTimeout() {
-        if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
+        if (mState != ScrimState.AOD) {
             return;
         }
 
@@ -361,10 +357,6 @@
                 return;
             }
 
-            if (mPinnedHeadsUpCount != 0) {
-                updateHeadsUpScrim(false);
-            }
-
             setOrAdaptCurrentAnimation(mScrimBehind);
             setOrAdaptCurrentAnimation(mScrimInFront);
         }
@@ -478,11 +470,13 @@
             mLightBarController.setScrimColor(mScrimInFront.getColors());
         }
 
-        // We want to override the back scrim opacity for AOD and PULSING
+        // We want to override the back scrim opacity for the AOD state
         // when it's time to fade the wallpaper away.
-        boolean overrideBackScrimAlpha = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
-                && mWallpaperVisibilityTimedOut;
-        if (overrideBackScrimAlpha) {
+        boolean aodWallpaperTimeout = mState == ScrimState.AOD && mWallpaperVisibilityTimedOut;
+        // We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim.
+        boolean occludedKeyguard = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
+                && mKeyguardOccluded;
+        if (aodWallpaperTimeout || occludedKeyguard) {
             mCurrentBehindAlpha = 1;
         }
 
@@ -606,8 +600,6 @@
             return mCurrentInFrontAlpha;
         } else if (scrim == mScrimBehind) {
             return mCurrentBehindAlpha;
-        } else if (scrim == mHeadsUpScrim) {
-            return calculateHeadsUpAlpha();
         } else {
             throw new IllegalArgumentException("Unknown scrim view");
         }
@@ -618,8 +610,6 @@
             return mCurrentInFrontTint;
         } else if (scrim == mScrimBehind) {
             return mCurrentBehindTint;
-        } else if (scrim == mHeadsUpScrim) {
-            return Color.TRANSPARENT;
         } else {
             throw new IllegalArgumentException("Unknown scrim view");
         }
@@ -666,40 +656,6 @@
         mScrimBehind.setDrawAsSrc(asSrc);
     }
 
-    @Override
-    public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
-    }
-
-    @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
-        mPinnedHeadsUpCount++;
-        updateHeadsUpScrim(true);
-    }
-
-    @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
-        mPinnedHeadsUpCount--;
-        if (headsUp == mDraggedHeadsUpView) {
-            mDraggedHeadsUpView = null;
-            mTopHeadsUpDragAmount = 0.0f;
-        }
-        updateHeadsUpScrim(true);
-    }
-
-    @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
-    }
-
-    private void updateHeadsUpScrim(boolean animate) {
-        if (animate) {
-            mAnimationDuration = ANIMATION_DURATION;
-            cancelAnimator((ValueAnimator) mHeadsUpScrim.getTag(TAG_KEY_ANIM));
-            startScrimAnimation(mHeadsUpScrim, mHeadsUpScrim.getAlpha());
-        } else {
-            setOrAdaptCurrentAnimation(mHeadsUpScrim);
-        }
-    }
-
     @VisibleForTesting
     void setOnAnimationFinished(Runnable onAnimationFinished) {
         mOnAnimationFinished = onAnimationFinished;
@@ -806,33 +762,6 @@
         return Handler.getMain();
     }
 
-    /**
-     * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means
-     * the heads up is in its resting space and 1 means it's fully dragged out.
-     *
-     * @param draggedHeadsUpView the dragged view
-     * @param topHeadsUpDragAmount how far is it dragged
-     */
-    public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) {
-        mTopHeadsUpDragAmount = topHeadsUpDragAmount;
-        mDraggedHeadsUpView = draggedHeadsUpView;
-        updateHeadsUpScrim(false);
-    }
-
-    private float calculateHeadsUpAlpha() {
-        float alpha;
-        if (mPinnedHeadsUpCount >= 2) {
-            alpha = 1.0f;
-        } else if (mPinnedHeadsUpCount == 0) {
-            alpha = 0.0f;
-        } else {
-            alpha = 1.0f - mTopHeadsUpDragAmount;
-        }
-        float expandFactor = (1.0f - mExpansionFraction);
-        expandFactor = Math.max(expandFactor, 0.0f);
-        return alpha * expandFactor;
-    }
-
     public void setExcludedBackgroundArea(Rect area) {
         mScrimBehind.setExcludedArea(area);
     }
@@ -847,13 +776,6 @@
         mScrimBehind.setChangeRunnable(changeRunnable);
     }
 
-    public void onDensityOrFontScaleChanged() {
-        ViewGroup.LayoutParams layoutParams = mHeadsUpScrim.getLayoutParams();
-        layoutParams.height = mHeadsUpScrim.getResources().getDimensionPixelSize(
-                R.dimen.heads_up_scrim_height);
-        mHeadsUpScrim.setLayoutParams(layoutParams);
-    }
-
     public void setCurrentUser(int currentUser) {
         // Don't care in the base class.
     }
@@ -926,6 +848,10 @@
         mExpansionAffectsAlpha = expansionAffectsAlpha;
     }
 
+    public void setKeyguardOccluded(boolean keyguardOccluded) {
+        mKeyguardOccluded = keyguardOccluded;
+    }
+
     public interface Callback {
         default void onStart() {
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 55e8714..5b734eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -105,8 +105,7 @@
         @Override
         public void prepare(ScrimState previousState) {
             final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
-            final boolean wasPulsing = previousState == ScrimState.PULSING;
-            mBlankScreen = wasPulsing && !mCanControlScreenOff;
+            mBlankScreen = mDisplayRequiresBlanking;
             mCurrentBehindAlpha = mWallpaperSupportsAmbientMode
                     && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
             mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f;
@@ -114,7 +113,7 @@
             mCurrentBehindTint = Color.BLACK;
             // DisplayPowerManager will blank the screen for us, we just need
             // to set our state.
-            mAnimateChange = mCanControlScreenOff;
+            mAnimateChange = !mDisplayRequiresBlanking;
         }
 
         @Override
@@ -178,7 +177,6 @@
     ScrimView mScrimBehind;
     DozeParameters mDozeParameters;
     boolean mDisplayRequiresBlanking;
-    boolean mCanControlScreenOff;
     boolean mWallpaperSupportsAmbientMode;
     KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     int mIndex;
@@ -192,7 +190,6 @@
         mScrimBehind = scrimBehind;
         mDozeParameters = dozeParameters;
         mDisplayRequiresBlanking = dozeParameters.getDisplayNeedsBlanking();
-        mCanControlScreenOff = dozeParameters.getCanControlScreenOffAnimation();
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(scrimInFront.getContext());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c92f300..feb7dc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -189,6 +189,7 @@
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
@@ -602,6 +603,7 @@
     private NavigationBarFragment mNavigationBar;
     private View mNavigationBarView;
     protected ActivityLaunchAnimator mActivityLaunchAnimator;
+    private HeadsUpAppearanceController mHeadsUpAppearanceController;
 
     @Override
     public void start() {
@@ -807,6 +809,8 @@
                     mStatusBarView.setPanel(mNotificationPanel);
                     mStatusBarView.setScrimController(mScrimController);
                     mStatusBarView.setBouncerShowing(mBouncerShowing);
+                    mHeadsUpAppearanceController = new HeadsUpAppearanceController(
+                            mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
                     setAreThereNotifications();
                     checkBarModes();
                 }).getFragmentManager()
@@ -899,14 +903,14 @@
 
         ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
         ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
-        View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
         mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController,
-                scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper,
+                scrimBehind, scrimInFront, mLockscreenWallpaper,
                 scrimsVisible -> {
                     if (mStatusBarWindowManager != null) {
                         mStatusBarWindowManager.setScrimsVisibility(scrimsVisible);
                     }
-                }, new DozeParameters(mContext), mContext.getSystemService(AlarmManager.class));
+                }, DozeParameters.getInstance(mContext),
+                mContext.getSystemService(AlarmManager.class));
         if (mScrimSrcModeEnabled) {
             Runnable runnable = () -> {
                 boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
@@ -916,9 +920,9 @@
             mBackdrop.setOnVisibilityChangedRunnable(runnable);
             runnable.run();
         }
-        mHeadsUpManager.addListener(mScrimController);
         mStackScroller.setScrimController(mScrimController);
-        mDozeScrimController = new DozeScrimController(mScrimController, context);
+        mDozeScrimController = new DozeScrimController(mScrimController, context,
+                DozeParameters.getInstance(context));
 
         // Other icons
         mVolumeComponent = getComponent(VolumeComponent.class);
@@ -1073,7 +1077,6 @@
             mReinflateNotificationsOnUserSwitched = true;
         }
         // end old BaseStatusBar.onDensityOrFontScaleChanged().
-        mScrimController.onDensityOrFontScaleChanged();
         // TODO: Remove this.
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.onDensityOrFontScaleChanged();
@@ -1087,6 +1090,7 @@
             mKeyguardUserSwitcher.onDensityOrFontScaleChanged();
         }
         mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
+        mHeadsUpManager.onDensityOrFontScaleChanged();
 
         reevaluateStyles();
     }
@@ -1384,8 +1388,7 @@
         updateQsExpansionEnabled();
 
         // Let's also update the icons
-        mNotificationIconAreaController.updateNotificationIcons(
-                mEntryManager.getNotificationData());
+        mNotificationIconAreaController.updateNotificationIcons();
     }
 
     @Override
@@ -1867,7 +1870,7 @@
 
     @Override
     public boolean isDozing() {
-        return mDozing;
+        return mDozing && mStackScroller.isFullyDark();
     }
 
     @Override
@@ -1991,7 +1994,7 @@
         mVisualStabilityManager.setPanelExpanded(isExpanded);
         if (isExpanded && getBarState() != StatusBarState.KEYGUARD) {
             if (DEBUG) {
-                Log.v(TAG, "clearing notification effects from setPanelExpanded");
+                Log.v(TAG, "clearing notification effects from setExpandedHeight");
             }
             clearNotificationEffects();
         }
@@ -2040,6 +2043,7 @@
 
     public void setOccluded(boolean occluded) {
         mIsOccluded = occluded;
+        mScrimController.setKeyguardOccluded(occluded);
         updateHideIconsForBouncer(false /* animate */);
     }
 
@@ -2812,7 +2816,7 @@
                     public void setRemoteInputActive(NotificationData.Entry entry,
                             boolean remoteInputActive) {
                         mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
-                        entry.row.updateMaxHeights();
+                        entry.row.notifyHeightChanged(true /* needsAnimation */);
                     }
                     public void lockScrollTo(NotificationData.Entry entry) {
                         mStackScroller.lockScrollTo(entry.row);
@@ -3816,13 +3820,16 @@
     private void updateDozingState() {
         Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
         Trace.beginSection("StatusBar#updateDozingState");
+
+        boolean sleepingFromKeyguard =
+                mStatusBarKeyguardViewManager.isGoingToSleepVisibleNotOccluded();
         boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup())
-                || (mDozing && mDozeServiceHost.shouldAnimateScreenOff());
-        mNotificationPanel.setDozing(mDozing, animate);
+                || (mDozing && mDozeServiceHost.shouldAnimateScreenOff() && sleepingFromKeyguard);
+
         mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
         mDozeScrimController.setDozing(mDozing);
         mKeyguardIndicationController.setDozing(mDozing);
-        mNotificationPanel.setDark(mDozing, animate);
+        mNotificationPanel.setDozing(mDozing, animate);
         updateQsExpansionEnabled();
         mViewHierarchyManager.updateRowStates();
         Trace.endSection();
@@ -3832,6 +3839,9 @@
         if (mStackScroller == null) return;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
         boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
+        if (mHeadsUpAppearanceController != null) {
+            mHeadsUpAppearanceController.setPublicMode(publicMode);
+        }
         mStackScroller.setHideSensitive(publicMode, goingToFullShade);
         mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
         mStackScroller.setExpandingEnabled(!onKeyguard);
@@ -4738,6 +4748,7 @@
             if (mDozingRequested) {
                 mDozingRequested = false;
                 DozeLog.traceDozing(mContext, mDozing);
+                mWakefulnessLifecycle.dispatchStartedWakingUp();
                 updateDozing();
             }
         }
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 b26b7c9..a9c467e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -95,6 +95,7 @@
     protected boolean mLastRemoteInputActive;
     private boolean mLastDozing;
     private int mLastFpMode;
+    private boolean mGoingToSleepVisibleNotOccluded;
 
     private OnDismissAction mAfterKeyguardGoneAction;
     private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
@@ -262,11 +263,16 @@
         }
     }
 
+    public boolean isGoingToSleepVisibleNotOccluded() {
+        return mGoingToSleepVisibleNotOccluded;
+    }
+
     public void onStartedGoingToSleep() {
-        // TODO: remove
+        mGoingToSleepVisibleNotOccluded = isShowing() && !isOccluded();
     }
 
     public void onFinishedGoingToSleep() {
+        mGoingToSleepVisibleNotOccluded = false;
         mBouncer.onScreenTurnedOff();
     }
 
@@ -371,6 +377,7 @@
     public void startPreHideAnimation(Runnable finishRunnable) {
         if (mBouncer.isShowing()) {
             mBouncer.startPreHideAnimation(finishRunnable);
+            mNotificationPanelView.onBouncerPreHideAnimation();
         } else if (finishRunnable != null) {
             finishRunnable.run();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index defb46c..309a1a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -73,7 +73,7 @@
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mActivityManager = ActivityManager.getService();
         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
-        mDozeParameters = new DozeParameters(mContext);
+        mDozeParameters = DozeParameters.getInstance(mContext);
         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
     }
 
@@ -141,11 +141,9 @@
             mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
         }
 
-        final boolean showWallpaperOnAod = mDozeParameters.getAlwaysOn() &&
-                state.wallpaperSupportsAmbientMode &&
-                state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_OPAQUE;
-        if (state.keyguardShowing && !state.backdropShowing &&
-                (!state.dozing || showWallpaperOnAod)) {
+        final boolean scrimsOccludingWallpaper =
+                state.scrimsVisibility == ScrimController.VISIBILITY_FULLY_OPAQUE;
+        if (state.keyguardShowing && !state.backdropShowing && !scrimsOccludingWallpaper) {
             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
         } else {
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 040d7ec..aeda55a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -443,6 +443,9 @@
         entry.reset();
     }
 
+    public void onDensityOrFontScaleChanged() {
+    }
+
     /**
      * This represents a notification and how long it is in a heads up mode. It also manages its
      * lifecycle automatically when created.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 0f637fb..7c1c566 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -70,6 +70,8 @@
     private int mIntrinsicPadding;
     private int mExpandAnimationTopChange;
     private ExpandableNotificationRow mExpandingNotification;
+    private boolean mFullyDark;
+    private int mDarkTopPadding;
 
     public AmbientState(Context context) {
         reload(context);
@@ -409,4 +411,26 @@
     public int getExpandAnimationTopChange() {
         return mExpandAnimationTopChange;
     }
+
+    /**
+     * {@see isFullyDark}
+     */
+    public void setFullyDark(boolean fullyDark) {
+        mFullyDark = fullyDark;
+    }
+
+    /**
+     * @return {@code true } when shade is completely dark: in AOD or ambient display.
+     */
+    public boolean isFullyDark() {
+        return mFullyDark;
+    }
+
+    public void setDarkTopPadding(int darkTopPadding) {
+        mDarkTopPadding = darkTopPadding;
+    }
+
+    public int getDarkTopPadding() {
+        return mDarkTopPadding;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 53377d9..c26568e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -21,12 +21,12 @@
 import android.view.View;
 
 import java.util.ArrayList;
-import java.util.Set;
 
 /**
  * Filters the animations for only a certain type of properties.
  */
 public class AnimationFilter {
+    public static final int NO_DELAY = -1;
     boolean animateAlpha;
     boolean animateX;
     boolean animateY;
@@ -40,7 +40,7 @@
     public boolean animateShadowAlpha;
     boolean hasDelays;
     boolean hasGoToFullShadeEvent;
-    boolean hasHeadsUpDisappearClickEvent;
+    long customDelay;
     private ArraySet<Property> mAnimatedProperties = new ArraySet<>();
 
     public AnimationFilter animateAlpha() {
@@ -129,8 +129,14 @@
                 hasGoToFullShadeEvent = true;
             }
             if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
+                    .ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
+                customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
+            } else if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
                     .ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
-                hasHeadsUpDisappearClickEvent = true;
+                // We need both timeouts when clicking, one to delay it and one for the animation
+                // to look nice
+                customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP_CLICKED
+                        + StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
             }
         }
     }
@@ -165,7 +171,7 @@
         animateHideSensitive = false;
         hasDelays = false;
         hasGoToFullShadeEvent = false;
-        hasHeadsUpDisappearClickEvent = false;
+        customDelay = NO_DELAY;
         mAnimatedProperties.clear();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
index 3bf7d89..a7925aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
@@ -233,7 +233,8 @@
         expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay);
 
         if (properties.wasAdded(child) && !hidden) {
-            expandableView.performAddAnimation(properties.delay, properties.duration);
+            expandableView.performAddAnimation(properties.delay, properties.duration,
+                    false /* isHeadsUpAppear */);
         }
 
         if (!expandableView.isInShelf() && this.inShelf) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
index 05c0099..59ce0ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
@@ -23,6 +23,11 @@
  * An interpolator specifically designed for the appear animation of heads up notifications.
  */
 public class HeadsUpAppearInterpolator extends PathInterpolator {
+
+    private static float X1 = 250f;
+    private static float X2 = 200f;
+    private static float XTOT = (X1 + X2);;
+
     public HeadsUpAppearInterpolator() {
         super(getAppearPath());
     }
@@ -30,22 +35,18 @@
     private static Path getAppearPath() {
         Path path = new Path();
         path.moveTo(0, 0);
-        float x1 = 250f;
-        float x2 = 150f;
-        float x3 = 100f;
         float y1 = 90f;
-        float y2 = 78f;
-        float y3 = 80f;
-        float xTot = (x1 + x2 + x3);
-        path.cubicTo(x1 * 0.9f / xTot, 0f,
-                x1 * 0.8f / xTot, y1 / y3,
-                x1 / xTot , y1 / y3);
-        path.cubicTo((x1 + x2 * 0.4f) / xTot, y1 / y3,
-                (x1 + x2 * 0.2f) / xTot, y2 / y3,
-                (x1 + x2) / xTot, y2 / y3);
-        path.cubicTo((x1 + x2 + x3 * 0.4f) / xTot, y2 / y3,
-                (x1 + x2 + x3 * 0.2f) / xTot, 1f,
-                1f, 1f);
+        float y2 = 80f;
+        path.cubicTo(X1 * 0.8f / XTOT, y1 / y2,
+                X1 * 0.8f / XTOT, y1 / y2,
+                X1 / XTOT, y1 / y2);
+        path.cubicTo((X1 + X2 * 0.4f) / XTOT, y1 / y2,
+                (X1 + X2 * 0.2f) / XTOT, 1.0f,
+                1.0f , 1.0f);
         return path;
     }
+
+    public static float getFractionUntilOvershoot() {
+        return X1 / XTOT;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index ac2a1e1..e5ab712 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -102,6 +102,9 @@
 
     private boolean mShowDividersWhenExpanded;
     private boolean mHideDividersDuringExpand;
+    private int mTranslationForHeader;
+    private int mCurrentHeaderTranslation = 0;
+    private float mHeaderVisibleAmount = 1.0f;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -142,6 +145,9 @@
                 res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded);
         mHideDividersDuringExpand =
                 res.getBoolean(R.bool.config_hideDividersDuringExpand);
+        mTranslationForHeader = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin)
+                - mNotificationHeaderMargin;
     }
 
     @Override
@@ -486,7 +492,7 @@
         if (showingAsLowPriority()) {
             return mNotificationHeaderLowPriority.getHeight();
         }
-        int intrinsicHeight = mNotificationHeaderMargin;
+        int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         boolean firstChild = true;
@@ -541,7 +547,7 @@
     public void getState(StackScrollState resultState, ExpandableViewState parentState,
             AmbientState ambientState) {
         int childCount = mChildren.size();
-        int yPosition = mNotificationHeaderMargin;
+        int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
         boolean firstChild = true;
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
         int lastVisibleIndex = maxAllowedVisibleChildren - 1;
@@ -645,6 +651,11 @@
             mHeaderViewState.zTranslation = childrenExpandedAndNotAnimating
                     ? parentState.zTranslation
                     : 0;
+            mHeaderViewState.yTranslation = mCurrentHeaderTranslation;
+            mHeaderViewState.alpha = mHeaderVisibleAmount;
+            // The hiding is done automatically by the alpha, otherwise we'll pick it up again
+            // in the next frame with the initFrom call above and have an invisible header
+            mHeaderViewState.hidden = false;
         }
     }
 
@@ -1009,7 +1020,8 @@
             return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true
                     /* likeHighPriority */);
         }
-        int maxContentHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
+        int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
+                + mNotificatonTopPadding;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         for (int i = 0; i < childCount; i++) {
@@ -1071,7 +1083,8 @@
     }
 
     private int getVisibleChildrenExpandHeight() {
-        int intrinsicHeight = mNotificationHeaderMargin + mNotificatonTopPadding + mDividerHeight;
+        int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
+                + mNotificatonTopPadding + mDividerHeight;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
@@ -1110,7 +1123,7 @@
         if (!likeHighPriority && showingAsLowPriority()) {
             return mNotificationHeaderLowPriority.getHeight();
         }
-        int minExpandHeight = mNotificationHeaderMargin;
+        int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
         int visibleChildren = 0;
         boolean firstChild = true;
         int childCount = mChildren.size();
@@ -1190,7 +1203,8 @@
     }
 
     public int getPositionInLinearLayout(View childInGroup) {
-        int position = mNotificationHeaderMargin + mNotificatonTopPadding;
+        int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
+                + mNotificatonTopPadding;
 
         for (int i = 0; i < mChildren.size(); i++) {
             ExpandableNotificationRow child = mChildren.get(i);
@@ -1281,4 +1295,9 @@
             last = false;
         }
     }
+
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        mHeaderVisibleAmount = headerVisibleAmount;
+        mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
new file mode 100644
index 0000000..a36c966
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
@@ -0,0 +1,123 @@
+/*
+ * 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.stack;
+
+import android.view.View;
+
+import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.util.HashSet;
+
+/**
+ * A class that manages the roundness for notification views
+ */
+class NotificationRoundnessManager implements OnHeadsUpChangedListener {
+
+    private boolean mExpanded;
+    private ActivatableNotificationView mFirst;
+    private ActivatableNotificationView mLast;
+    private HashSet<View> mAnimatedChildren;
+    private Runnable mRoundingChangedCallback;
+    private ExpandableNotificationRow mTrackedHeadsUp;
+    private float mAppearFraction;
+
+    @Override
+    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+        updateRounding(headsUp, false /* animate */);
+    }
+
+    @Override
+    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+        updateRounding(headsUp, true /* animate */);
+    }
+
+    private void updateRounding(ActivatableNotificationView view, boolean animate) {
+        float topRoundness = getRoundness(view, true /* top */);
+        float bottomRoundness = getRoundness(view, false /* top */);
+        boolean firstChanged = view.setTopRoundness(topRoundness, animate);
+        boolean secondChanged = view.setBottomRoundness(bottomRoundness, animate);
+        if ((view == mFirst || view == mLast) && (firstChanged || secondChanged)) {
+            mRoundingChangedCallback.run();
+        }
+    }
+
+    private float getRoundness(ActivatableNotificationView view, boolean top) {
+        if ((view.isPinned() || view.isHeadsUpAnimatingAway()) && !mExpanded) {
+            return 1.0f;
+        }
+        if (view == mFirst && top) {
+            return 1.0f;
+        }
+        if (view == mLast && !top) {
+            return 1.0f;
+        }
+        if (view == mTrackedHeadsUp && mAppearFraction <= 0.0f) {
+            // If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be
+            // rounded.
+            return 1.0f;
+        }
+        return 0.0f;
+    }
+
+    public void setExpanded(float expandedHeight, float appearFraction) {
+        mExpanded = expandedHeight != 0.0f;
+        mAppearFraction = appearFraction;
+        if (mTrackedHeadsUp != null) {
+            updateRounding(mTrackedHeadsUp, true);
+        }
+    }
+
+    public void setFirstAndLastBackgroundChild(ActivatableNotificationView first,
+            ActivatableNotificationView last) {
+        boolean firstChanged = mFirst != first;
+        boolean lastChanged = mLast != last;
+        if (!firstChanged && !lastChanged) {
+            return;
+        }
+        ActivatableNotificationView oldFirst = mFirst;
+        ActivatableNotificationView oldLast = mLast;
+        mFirst = first;
+        mLast = last;
+        if (firstChanged && oldFirst != null && !oldFirst.isRemoved()) {
+            updateRounding(oldFirst, oldFirst.isShown());
+        }
+        if (lastChanged && oldLast != null && !oldLast.isRemoved()) {
+            updateRounding(oldLast, oldLast.isShown());
+        }
+        if (mFirst != null) {
+            updateRounding(mFirst, mFirst.isShown() && !mAnimatedChildren.contains(mFirst));
+        }
+        if (mLast != null) {
+            updateRounding(mLast, mLast.isShown() && !mAnimatedChildren.contains(mLast));
+        }
+        mRoundingChangedCallback.run();
+    }
+
+    public void setAnimatedChildren(HashSet<View> animatedChildren) {
+        mAnimatedChildren = animatedChildren;
+    }
+
+    public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) {
+        mRoundingChangedCallback = roundingChangedCallback;
+    }
+
+    public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+        mTrackedHeadsUp = row;
+    }
+}
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 a85f4e2..a572450 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -94,8 +94,8 @@
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 
@@ -108,6 +108,7 @@
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
+import java.util.function.BiConsumer;
 
 /**
  * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
@@ -289,6 +290,7 @@
     private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
             = new HashSet<>();
     private HeadsUpManagerPhone mHeadsUpManager;
+    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
     private boolean mTrackingHeadsUp;
     private ScrimController mScrimController;
     private boolean mForceNoOverlappingRendering;
@@ -400,9 +402,11 @@
     private int mSidePaddings;
     private final int mSeparatorWidth;
     private final int mSeparatorThickness;
-    private final Rect mTmpRect = new Rect();
+    private final Rect mBackgroundAnimationRect = new Rect();
     private int mClockBottom;
     private int mAntiBurnInOffsetX;
+    private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
+    private int mHeadsUpInset;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -440,6 +444,9 @@
         mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
         mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
         mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
+        mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
+        mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
+        addOnExpandedHeightListener(mRoundnessManager::setExpanded);
 
         updateWillNotDraw();
         mBackgroundPaint.setAntiAlias(true);
@@ -515,26 +522,29 @@
         final int darkBottom = darkTop + mSeparatorThickness;
 
         if (mAmbientState.hasPulsingNotifications()) {
-            // TODO draw divider between notification and shelf
-        } else if (mAmbientState.isDark()) {
+            // No divider, we have a notification icon instead
+        } else if (mAmbientState.isFullyDark()) {
             // Only draw divider on AOD if we actually have notifications
             if (mFirstVisibleBackgroundChild != null) {
                 canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
             }
-            setClipBounds(null);
         } else {
             float animProgress = Interpolators.FAST_OUT_SLOW_IN
                     .getInterpolation(1f - mDarkAmount);
             float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN
                     .getInterpolation((1f - mDarkAmount) * 2);
-            mTmpRect.set((int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
+            mBackgroundAnimationRect.set(
+                    (int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
                     (int) MathUtils.lerp(darkTop, lockScreenTop, animProgress),
                     (int) MathUtils.lerp(darkRight, lockScreenRight, sidePaddingsProgress),
                     (int) MathUtils.lerp(darkBottom, lockScreenBottom, animProgress));
-            canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
-                    mCornerRadius, mCornerRadius, mBackgroundPaint);
-            setClipBounds(animProgress == 1 ? null : mTmpRect);
+            if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
+                canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
+                        mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
+                        mCornerRadius, mCornerRadius, mBackgroundPaint);
+            }
         }
+        updateClipping();
     }
 
     private void updateBackgroundDimming() {
@@ -582,13 +592,15 @@
                 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
         mMinTopOverScrollToEscape = res.getDimensionPixelSize(
                 R.dimen.min_top_overscroll_to_qs);
-        mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
+        mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
         mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
         mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
         mMinInteractionHeight = res.getDimensionPixelSize(
                 R.dimen.notification_min_interaction_height);
         mCornerRadius = res.getDimensionPixelSize(
                 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
+        mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
+                R.dimen.heads_up_status_bar_padding);
     }
 
     public void setDrawBackgroundAsSrc(boolean asSrc) {
@@ -693,7 +705,7 @@
         if (mPulsing) {
             mTopPadding = mClockBottom;
         } else {
-            mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
+            mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
         }
         mAmbientState.setLayoutHeight(getLayoutHeight());
         updateAlgorithmLayoutMinHeight();
@@ -701,7 +713,8 @@
     }
 
     private void updateAlgorithmLayoutMinHeight() {
-        mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
+        mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() || isHeadsUpTransition()
+                ? getLayoutMinHeight() : 0);
     }
 
     /**
@@ -820,6 +833,7 @@
         if (mRegularTopPadding != topPadding) {
             mRegularTopPadding = topPadding;
             mDarkTopPadding = topPadding + mDarkSeparatorPadding;
+            mAmbientState.setDarkTopPadding(mDarkTopPadding);
             updateAlgorithmHeightAndPadding();
             updateContentHeight();
             if (animate && mAnimationsEnabled && mIsExpanded) {
@@ -854,11 +868,12 @@
         float translationY;
         float appearEndPosition = getAppearEndPosition();
         float appearStartPosition = getAppearStartPosition();
+        float appearFraction = 1.0f;
         if (height >= appearEndPosition) {
             translationY = 0;
             stackHeight = (int) height;
         } else {
-            float appearFraction = getAppearFraction(height);
+            appearFraction = getAppearFraction(height);
             if (appearFraction >= 0) {
                 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
                         appearFraction);
@@ -867,7 +882,12 @@
                 // start
                 translationY = height - appearStartPosition + getExpandTranslationStart();
             }
-            stackHeight = (int) (height - translationY);
+            if (isHeadsUpTransition()) {
+                stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
+                translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
+            } else {
+                stackHeight = (int) (height - translationY);
+            }
         }
         if (stackHeight != mCurrentStackHeight) {
             mCurrentStackHeight = stackHeight;
@@ -875,6 +895,10 @@
             requestChildrenUpdate();
         }
         setStackTranslation(translationY);
+        for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
+            BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
+            listener.accept(mExpandedHeight, appearFraction);
+        }
     }
 
     private void setRequestedClipBounds(Rect clipRect) {
@@ -883,13 +907,17 @@
     }
 
     public void updateClipping() {
+        boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1;
         boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
                 && !mHeadsUpAnimatingAway;
         if (mIsClipped != clipped) {
             mIsClipped = clipped;
             updateFadingState();
         }
-        if (clipped) {
+
+        if (animatingClipping) {
+            setClipBounds(mBackgroundAnimationRect);
+        } else if (clipped) {
             setClipBounds(mRequestedClipBounds);
         } else {
             setClipBounds(null);
@@ -909,12 +937,8 @@
      *         Measured in absolute height.
      */
     private float getAppearStartPosition() {
-        if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) {
-            if (mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild)) {
-                // If we ever expanded beyond the first notification, it's allowed to merge into
-                // the shelf
-                return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
-            }
+        if (isHeadsUpTransition()) {
+            return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
         }
         return getMinExpansionHeight();
     }
@@ -948,17 +972,14 @@
         int appearPosition;
         int notGoneChildCount = getNotGoneChildCount();
         if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
-            int minNotificationsForShelf = 1;
-            if (mTrackingHeadsUp
+            if (isHeadsUpTransition()
                     || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
                 appearPosition = getTopHeadsUpPinnedHeight();
-                minNotificationsForShelf = 2;
             } else {
                 appearPosition = 0;
-            }
-            if (notGoneChildCount >= minNotificationsForShelf
-                    && mShelf.getVisibility() != GONE) {
-                appearPosition += mShelf.getIntrinsicHeight();
+                if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
+                    appearPosition += mShelf.getIntrinsicHeight();
+                }
             }
         } else {
             appearPosition = mEmptyShadeView.getHeight();
@@ -966,6 +987,11 @@
         return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
     }
 
+    private boolean isHeadsUpTransition() {
+        return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
+                && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
+    }
+
     /**
      * @param height the height of the panel
      * @return the fraction of the appear animation that has been performed
@@ -1076,10 +1102,6 @@
 
     @Override
     public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
-        if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
-            mScrimController.setTopHeadsUpDragAmount(animView,
-                    Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
-        }
         // Returning true prevents alpha fading.
         return !mFadeNotificationsOnDismiss;
     }
@@ -2075,7 +2097,7 @@
         float previousPaddingAmount = 0.0f;
         int numShownItems = 0;
         boolean finish = false;
-        int maxDisplayedNotifications = mAmbientState.isDark()
+        int maxDisplayedNotifications = mAmbientState.isFullyDark()
                 ? (hasPulsingNotifications() ? 1 : 0)
                 : mMaxDisplayedNotifications;
 
@@ -2085,7 +2107,7 @@
                     && !expandableView.hasNoContentHeight()) {
                 boolean limitReached = maxDisplayedNotifications != -1
                         && numShownItems >= maxDisplayedNotifications;
-                boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isDark()
+                boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
                         && hasPulsingNotifications()
                         && expandableView instanceof ExpandableNotificationRow
                         && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
@@ -2168,7 +2190,7 @@
 
     private void updateBackground() {
         // No need to update the background color if it's not being drawn.
-        if (!mShouldDrawNotificationBackground || mAmbientState.isDark()) {
+        if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
             return;
         }
 
@@ -2521,6 +2543,9 @@
     }
 
     public int getLayoutMinHeight() {
+        if (isHeadsUpTransition()) {
+            return getTopHeadsUpPinnedHeight();
+        }
         return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
     }
 
@@ -2929,42 +2954,18 @@
     private void updateFirstAndLastBackgroundViews() {
         ActivatableNotificationView firstChild = getFirstChildWithBackground();
         ActivatableNotificationView lastChild = getLastChildWithBackground();
-        boolean firstChanged = firstChild != mFirstVisibleBackgroundChild;
-        boolean lastChanged = lastChild != mLastVisibleBackgroundChild;
         if (mAnimationsEnabled && mIsExpanded) {
-            mAnimateNextBackgroundTop = firstChanged;
-            mAnimateNextBackgroundBottom = lastChanged;
+            mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
+            mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
         } else {
             mAnimateNextBackgroundTop = false;
             mAnimateNextBackgroundBottom = false;
         }
-        if (firstChanged && mFirstVisibleBackgroundChild != null
-                && !mFirstVisibleBackgroundChild.isRemoved()) {
-            mFirstVisibleBackgroundChild.setTopRoundness(0.0f,
-                    mFirstVisibleBackgroundChild.isShown());
-        }
-        if (lastChanged && mLastVisibleBackgroundChild != null
-                && !mLastVisibleBackgroundChild.isRemoved()) {
-            mLastVisibleBackgroundChild.setBottomRoundness(0.0f,
-                    mLastVisibleBackgroundChild.isShown());
-        }
         mFirstVisibleBackgroundChild = firstChild;
         mLastVisibleBackgroundChild = lastChild;
         mAmbientState.setLastVisibleBackgroundChild(lastChild);
-        applyRoundedNess();
-    }
-
-    private void applyRoundedNess() {
-        if (mFirstVisibleBackgroundChild != null) {
-            mFirstVisibleBackgroundChild.setTopRoundness(1.0f,
-                    mFirstVisibleBackgroundChild.isShown()
-                            && !mChildrenToAddAnimated.contains(mFirstVisibleBackgroundChild));
-        }
-        if (mLastVisibleBackgroundChild != null) {
-            mLastVisibleBackgroundChild.setBottomRoundness(1.0f,
-                    mLastVisibleBackgroundChild.isShown()
-                            && !mChildrenToAddAnimated.contains(mLastVisibleBackgroundChild));
-        }
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
+                mLastVisibleBackgroundChild);
         invalidate();
     }
 
@@ -3298,7 +3299,7 @@
                             .animateY(mShelf));
             ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
             mAnimationEvents.add(ev);
-            startBackgroundFadeIn();
+            startBackgroundFade();
         }
         mDarkNeedsAnimation = false;
     }
@@ -3889,7 +3890,6 @@
         requestChildrenUpdate();
         applyCurrentBackgroundBounds();
         updateWillNotDraw();
-        updateContentHeight();
         updateAntiBurnInTranslation();
         notifyHeightChangeListener(mShelf);
     }
@@ -3910,6 +3910,11 @@
 
     private void setDarkAmount(float darkAmount) {
         mDarkAmount = darkAmount;
+        final boolean fullyDark = darkAmount == 1;
+        if (mAmbientState.isFullyDark() != fullyDark) {
+            mAmbientState.setFullyDark(fullyDark);
+            updateContentHeight();
+        }
         updateBackgroundDimming();
     }
 
@@ -3917,8 +3922,9 @@
         return mDarkAmount;
     }
 
-    private void startBackgroundFadeIn() {
-        ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount, 0f);
+    private void startBackgroundFade() {
+        ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount,
+                mAmbientState.isDark() ? 1f : 0);
         fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
         fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
         fadeAnimator.start();
@@ -4288,6 +4294,7 @@
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         mHeadsUpManager = headsUpManager;
         mAmbientState.setHeadsUpManager(headsUpManager);
+        mHeadsUpManager.addListener(mRoundnessManager);
     }
 
     public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
@@ -4319,8 +4326,9 @@
         requestChildrenUpdate();
     }
 
-    public void setTrackingHeadsUp(boolean trackingHeadsUp) {
-        mTrackingHeadsUp = trackingHeadsUp;
+    public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+        mTrackingHeadsUp = row != null;
+        mRoundnessManager.setTrackingHeadsUp(row);
     }
 
     public void setScrimController(ScrimController scrimController) {
@@ -4502,6 +4510,20 @@
                 mAmbientState.getScrollY()));
     }
 
+    public boolean isFullyDark() {
+        return mAmbientState.isFullyDark();
+    }
+
+    /**
+     * Add a listener whenever the expanded height changes. The first value passed as an argument
+     * is the expanded height and the second one is the appearFraction.
+     *
+     * @param listener the listener to notify.
+     */
+    public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+        mExpandedHeightListeners.add(listener);
+    }
+
     /**
      * A listener that is notified when the empty space below the notifications is clicked on
      */
@@ -4880,7 +4902,8 @@
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
-                        .animateZ(),
+                        .animateZ()
+                        .hasDelays(),
 
                 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
                 new AnimationFilter()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 51737a8..7c8e0fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -50,6 +50,8 @@
     private boolean mIsExpanded;
     private boolean mClipNotificationScrollToTop;
     private int mStatusBarHeight;
+    private float mHeadsUpInset;
+    private int mPinnedZTranslationExtra;
 
     public StackScrollAlgorithm(Context context) {
         initView(context);
@@ -68,6 +70,10 @@
         mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
         mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
         mClipNotificationScrollToTop = res.getBoolean(R.bool.config_clipNotificationScrollToTop);
+        mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
+                R.dimen.heads_up_status_bar_padding);
+        mPinnedZTranslationExtra = res.getDimensionPixelSize(
+                R.dimen.heads_up_pinned_elevation);
     }
 
     public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
@@ -184,7 +190,7 @@
     private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
             StackScrollState resultState, StackScrollAlgorithmState algorithmState) {
         boolean dimmed = ambientState.isDimmed();
-        boolean dark = ambientState.isDark();
+        boolean dark = ambientState.isFullyDark();
         boolean hideSensitive = ambientState.isHideSensitive();
         View activatedChild = ambientState.getActivatedChild();
         int childCount = algorithmState.visibleChildren.size();
@@ -457,7 +463,7 @@
                 }
             }
             if (row.isPinned()) {
-                childState.yTranslation = Math.max(childState.yTranslation, 0);
+                childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset);
                 childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
                 childState.hidden = false;
                 ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
@@ -522,9 +528,6 @@
             childViewState.inShelf = true;
             childViewState.headsUpIsVisible = false;
         }
-        if (!ambientState.isShadeExpanded()) {
-            childViewState.height = (int) (mStatusBarHeight - childViewState.yTranslation);
-        }
     }
 
     protected int getMaxAllowedChildHeight(View child) {
@@ -592,6 +595,13 @@
         } else {
             childViewState.zTranslation = baseZ;
         }
+
+        // We need to scrim the notification more from its surrounding content when we are pinned,
+        // and we therefore elevate it higher.
+        // We can use the headerVisibleAmount for this, since the value nicely goes from 0 to 1 when
+        // expanding after which we have a normal elevation again.
+        childViewState.zTranslation += (1.0f - child.getHeaderVisibleAmount())
+                * mPinnedZTranslationExtra;
         return childrenOnTop;
     }
 
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 236c348..d48ae76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.StatusBarIconView;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -45,13 +46,17 @@
     public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
     public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
     public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
-    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650;
-    public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230;
+    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550;
+    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED
+            = (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
+                    * HeadsUpAppearInterpolator.getFractionUntilOvershoot());
+    public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
     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;
     public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
     public static final int ANIMATION_DELAY_HEADS_UP = 120;
+    public static final int ANIMATION_DELAY_HEADS_UP_CLICKED= 120;
 
     private final int mGoToFullShadeAppearingTranslation;
     private final ExpandableViewState mTmpState = new ExpandableViewState();
@@ -74,8 +79,9 @@
     private ValueAnimator mBottomOverScrollAnimator;
     private int mHeadsUpAppearHeightBottom;
     private boolean mShadeExpanded;
-    private ArrayList<View> mChildrenToClearFromOverlay = new ArrayList<>();
     private NotificationShelf mShelf;
+    private float mStatusBarIconLocation;
+    private int[] mTmpLocation = new int[2];
 
     public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
         mHostLayout = hostLayout;
@@ -222,8 +228,8 @@
         if (mAnimationFilter.hasGoToFullShadeEvent) {
             return calculateDelayGoToFullShade(viewState);
         }
-        if (mAnimationFilter.hasHeadsUpDisappearClickEvent) {
-            return ANIMATION_DELAY_HEADS_UP;
+        if (mAnimationFilter.customDelay != AnimationFilter.NO_DELAY) {
+            return mAnimationFilter.customDelay;
         }
         long minDelay = 0;
         for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
@@ -327,10 +333,6 @@
 
     private void onAnimationFinished() {
         mHostLayout.onChildAnimationFinished();
-        for (View v : mChildrenToClearFromOverlay) {
-            removeFromOverlay(v);
-        }
-        mChildrenToClearFromOverlay.clear();
     }
 
     /**
@@ -396,13 +398,14 @@
 
                 }
                 changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
-                        translationDirection, new Runnable() {
+                        0 /* delay */, translationDirection,  false /* isHeadsUpAppear */,
+                        0, new Runnable() {
                     @Override
                     public void run() {
                         // remove the temporary overlay
                         removeFromOverlay(changingView);
                     }
-                });
+                }, null);
             } else if (event.animationType ==
                 NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
                 // A race condition can trigger the view to be added to the overlay even though
@@ -424,7 +427,9 @@
                 if (event.headsUpFromBottom) {
                     mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
                 } else {
-                    mTmpState.yTranslation = -mTmpState.height;
+                    mTmpState.yTranslation = 0;
+                    changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED,
+                            true /* isHeadsUpAppear */);
                 }
                 mHeadsUpAppearChildren.add(changingView);
                 mTmpState.applyToView(changingView);
@@ -433,22 +438,56 @@
                     event.animationType == NotificationStackScrollLayout
                             .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
                 mHeadsUpDisappearChildren.add(changingView);
+                Runnable endRunnable = null;
+                // We need some additional delay in case we were removed to make sure we're not
+                // lagging
+                int extraDelay = event.animationType == NotificationStackScrollLayout
+                        .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                        ? ANIMATION_DELAY_HEADS_UP_CLICKED
+                        : 0;
                 if (changingView.getParent() == null) {
                     // This notification was actually removed, so we need to add it to the overlay
                     mHostLayout.getOverlay().add(changingView);
                     mTmpState.initFrom(changingView);
-                    mTmpState.yTranslation = -changingView.getActualHeight();
+                    mTmpState.yTranslation = 0;
                     // We temporarily enable Y animations, the real filter will be combined
                     // afterwards anyway
                     mAnimationFilter.animateY = true;
-                    mAnimationProperties.delay =
-                            event.animationType == NotificationStackScrollLayout
-                                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
-                            ? ANIMATION_DELAY_HEADS_UP
-                            : 0;
+                    mAnimationProperties.delay = extraDelay + ANIMATION_DELAY_HEADS_UP;
                     mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
                     mTmpState.animateTo(changingView, mAnimationProperties);
-                    mChildrenToClearFromOverlay.add(changingView);
+                    endRunnable = () -> {
+                        // remove the temporary overlay
+                        removeFromOverlay(changingView);
+                    };
+                }
+                float targetLocation = 0;
+                boolean needsAnimation = true;
+                if (changingView instanceof ExpandableNotificationRow) {
+                    ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
+                    if (row.isDismissed()) {
+                        needsAnimation = false;
+                    }
+                    StatusBarIconView icon = row.getEntry().icon;
+                    if (icon.getParent() != null) {
+                        icon.getLocationOnScreen(mTmpLocation);
+                        float iconPosition = mTmpLocation[0] - icon.getTranslationX()
+                                + ViewState.getFinalTranslationX(icon) + icon.getWidth() * 0.25f;
+                        mHostLayout.getLocationOnScreen(mTmpLocation);
+                        targetLocation = iconPosition - mTmpLocation[0];
+                    }
+                }
+
+                if (needsAnimation) {
+                    // We need to add the global animation listener, since once no animations are
+                    // running anymore, the panel will instantly hide itself. We need to wait until
+                    // the animation is fully finished for this though.
+                    changingView.performRemoveAnimation(ANIMATION_DURATION_HEADS_UP_DISAPPEAR
+                                    + ANIMATION_DELAY_HEADS_UP, extraDelay, 0.0f,
+                            true /* isHeadsUpAppear */, targetLocation, endRunnable,
+                            getGlobalAnimationFinishedListener());
+                } else if (endRunnable != null) {
+                    endRunnable.run();
                 }
             }
             mNewEvents.add(event);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 04a7bd7..4b3643f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -641,6 +641,22 @@
     }
 
     /**
+     * Get the end value of the xTranslation animation running on a view or the xTranslation
+     * if no animation is running.
+     */
+    public static float getFinalTranslationX(View view) {
+        if (view == null) {
+            return 0;
+        }
+        ValueAnimator xAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
+        if (xAnimator == null) {
+            return view.getTranslationX();
+        } else {
+            return getChildTag(view, TAG_END_TRANSLATION_X);
+        }
+    }
+
+    /**
      * Get the end value of the yTranslation animation running on a view or the yTranslation
      * if no animation is running.
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 8c4fd73..66836365 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -28,6 +28,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.os.Looper;
@@ -37,6 +40,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.wakelock.WakeLock;
 import com.android.systemui.utils.os.FakeHandler;
 
 import org.junit.Before;
@@ -54,14 +58,17 @@
     FakeHandler mHandlerFake;
     @Mock
     DozeParameters mDozeParameters;
+    @Mock
+    WakeLock mWakeLock;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
         mServiceFake = new DozeServiceFake();
         mHandlerFake = new FakeHandler(Looper.getMainLooper());
-        mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters);
+        mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters, mWakeLock);
     }
 
     @Test
@@ -142,4 +149,23 @@
         assertFalse(mServiceFake.screenStateSet);
     }
 
+    @Test
+    public void test_holdsWakeLockWhenGoingToLowPowerDelayed() {
+        // Transition to low power mode will be delayed to let
+        // animations play at 60 fps.
+        when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+        mHandlerFake.setMode(QUEUEING);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mHandlerFake.dispatchQueuedMessages();
+        reset(mWakeLock);
+
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        verify(mWakeLock).acquire();
+        verify(mWakeLock, never()).release();
+
+        mHandlerFake.dispatchQueuedMessages();
+        verify(mWakeLock).release();
+    }
+
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index 75ade9d..0d8d952 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -28,15 +28,20 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.AlarmManager;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.PowerManager;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.wakelock.WakeLockFake;
@@ -46,33 +51,39 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DozeUiTest extends SysuiTestCase {
 
+    @Mock
     private AlarmManager mAlarmManager;
+    @Mock
     private DozeMachine mMachine;
+    @Mock
+    private DozeParameters mDozeParameters;
+    @Mock
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    private DozeHost mHost;
     private WakeLockFake mWakeLock;
-    private DozeHostFake mHost;
     private Handler mHandler;
     private HandlerThread mHandlerThread;
     private DozeUi mDozeUi;
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
         mHandlerThread = new HandlerThread("DozeUiTest");
         mHandlerThread.start();
-        mAlarmManager = mock(AlarmManager.class);
-        mMachine = mock(DozeMachine.class);
         mWakeLock = new WakeLockFake();
-        mHost = new DozeHostFake();
         mHandler = mHandlerThread.getThreadHandler();
-        DozeParameters params = mock(DozeParameters.class);
-        when(params.getCanControlScreenOffAnimation()).thenReturn(true);
-        when(params.getDisplayNeedsBlanking()).thenReturn(false);
 
-        mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, params);
+        mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
+                mDozeParameters, mKeyguardUpdateMonitor);
     }
 
     @After
@@ -96,18 +107,69 @@
     }
 
     @Test
-    public void propagatesAnimateScreenOff() {
-        Assert.assertTrue("animateScreenOff should be true", mHost.animateScreenOff);
+    public void propagatesAnimateScreenOff_noAlwaysOn() {
+        reset(mHost);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(false);
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
 
-        DozeParameters params = mock(DozeParameters.class);
-        new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, params);
-        Assert.assertFalse("animateScreenOff should be false", mHost.animateScreenOff);
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
+        verify(mHost).setAnimateScreenOff(eq(false));
     }
 
     @Test
-    public void transitionSetsAnimateWakeup() {
-        mHost.animateWakeup = false;
+    public void propagatesAnimateScreenOff_alwaysOn() {
+        reset(mHost);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+
+        // Take over when the keyguard is visible.
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
+        verify(mHost).setAnimateScreenOff(eq(true));
+
+        // Do not animate screen-off when keyguard isn't visible - PowerManager will do it.
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
+        verify(mHost).setAnimateScreenOff(eq(false));
+    }
+
+    @Test
+    public void neverAnimateScreenOff_whenNotSupported() {
+        // Re-initialize DozeParameters saying that the display requires blanking.
+        reset(mDozeParameters);
+        reset(mHost);
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
+        mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
+                mDozeParameters, mKeyguardUpdateMonitor);
+
+        // Never animate if display doesn't support it.
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
+        verify(mHost, never()).setAnimateScreenOff(eq(false));
+    }
+
+    @Test
+    public void transitionSetsAnimateWakeup_alwaysOn() {
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
         mDozeUi.transitionTo(UNINITIALIZED, DOZE);
-        Assert.assertTrue("animateScreenOff should be true", mHost.animateWakeup);
+        verify(mHost).setAnimateWakeup(eq(true));
+    }
+
+    @Test
+    public void keyguardVisibility_changesControlScreenOffAnimation() {
+        // Pre-condition
+        reset(mDozeParameters);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
+        verify(mDozeParameters).setControlScreenOffAnimation(eq(false));
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
+        verify(mDozeParameters).setControlScreenOffAnimation(eq(true));
+    }
+
+    @Test
+    public void transitionSetsAnimateWakeup_noAlwaysOn() {
+        mDozeUi.transitionTo(UNINITIALIZED, DOZE);
+        verify(mHost).setAnimateWakeup(eq(false));
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index 2705bca..5c80bb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -24,7 +24,6 @@
 import static org.mockito.Mockito.when;
 
 import android.app.IWallpaperManager;
-import android.os.Handler;
 import android.os.RemoteException;
 import android.support.test.filters.SmallTest;
 
@@ -37,7 +36,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @RunWith(JUnit4.class)
@@ -77,7 +75,7 @@
     public void testAnimates_whenSupported() throws RemoteException {
         // Pre-conditions
         when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
-        when(mDozeParameters.getCanControlScreenOffAnimation()).thenReturn(true);
+        when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
         when(mDozeParameters.getAlwaysOn()).thenReturn(true);
 
         mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
@@ -92,8 +90,8 @@
     public void testDoesNotAnimate_whenNotSupported() throws RemoteException {
         // Pre-conditions
         when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
-        when(mDozeParameters.getCanControlScreenOffAnimation()).thenReturn(false);
         when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+        when(mDozeParameters.shouldControlScreenOff()).thenReturn(false);
 
         mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
                 DozeMachine.State.DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index e15e0b4..9eba9b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
@@ -99,6 +100,23 @@
     }
 
     @Test
+    public void doesNotDispatchTwice() throws Exception {
+        mWakefulness.dispatchStartedWakingUp();
+        mWakefulness.dispatchStartedWakingUp();
+        mWakefulness.dispatchFinishedWakingUp();
+        mWakefulness.dispatchFinishedWakingUp();
+        mWakefulness.dispatchStartedGoingToSleep();
+        mWakefulness.dispatchStartedGoingToSleep();
+        mWakefulness.dispatchFinishedGoingToSleep();
+        mWakefulness.dispatchFinishedGoingToSleep();
+
+        verify(mWakefulnessObserver, times(1)).onStartedGoingToSleep();
+        verify(mWakefulnessObserver, times(1)).onFinishedGoingToSleep();
+        verify(mWakefulnessObserver, times(1)).onStartedWakingUp();
+        verify(mWakefulnessObserver, times(1)).onFinishedWakingUp();
+    }
+
+    @Test
     public void dump() throws Exception {
         mWakefulness.dump(null, new PrintWriter(new ByteArrayOutputStream()), new String[0]);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
index b1e1c02..2000bff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
@@ -60,6 +60,7 @@
 
     private static final int UID_NORMAL = 123;
     private static final int UID_ALLOW_DURING_SETUP = 456;
+    private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
 
     private final StatusBarNotification mMockStatusBarNotification =
             mock(StatusBarNotification.class);
@@ -247,6 +248,22 @@
         assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
     }
 
+    @Test
+    public void testShouldFilterHiddenNotifications() {
+        // setup
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+        // test should filter out hidden notifications:
+        // hidden
+        when(mMockStatusBarNotification.getKey()).thenReturn(TEST_HIDDEN_NOTIFICATION_KEY);
+        assertTrue(mNotificationData.shouldFilterOut(mMockStatusBarNotification));
+
+        // not hidden
+        when(mMockStatusBarNotification.getKey()).thenReturn("not hidden");
+        assertFalse(mNotificationData.shouldFilterOut(mMockStatusBarNotification));
+    }
+
     private void initStatusBarNotification(boolean allowDuringSetup) {
         Bundle bundle = new Bundle();
         bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
@@ -269,6 +286,21 @@
         @Override
         protected boolean getRanking(String key, NotificationListenerService.Ranking outRanking) {
             super.getRanking(key, outRanking);
+            if (key.equals(TEST_HIDDEN_NOTIFICATION_KEY)) {
+                outRanking.populate(key, outRanking.getRank(),
+                        outRanking.matchesInterruptionFilter(),
+                        outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
+                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
+                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
+            } else {
+                outRanking.populate(key, outRanking.getRank(),
+                        outRanking.matchesInterruptionFilter(),
+                        outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
+                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
+                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), false);
+            }
             return true;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
index 7e5db34..3703d6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
@@ -140,7 +140,7 @@
                     0,
                     NotificationManager.IMPORTANCE_DEFAULT,
                     null, null,
-                    null, null, null, true, sentiment);
+                    null, null, null, true, sentiment, false);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index e89ff97..550a35d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -16,11 +16,15 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.content.Context;
+import android.os.PowerManager;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.DozeParameters.IntInOutMatcher;
+
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -28,6 +32,14 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.fail;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DozeParametersTest extends SysuiTestCase {
@@ -186,4 +198,38 @@
         }
     }
 
+    @Test
+    public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
+        TestableDozeParameters dozeParameters = new TestableDozeParameters(getContext());
+        PowerManager mockedPowerManager = dozeParameters.getPowerManager();
+        dozeParameters.setControlScreenOffAnimation(true);
+        reset(mockedPowerManager);
+        dozeParameters.setControlScreenOffAnimation(false);
+        verify(mockedPowerManager).setDozeAfterScreenOff(eq(true));
+    }
+
+    @Test
+    public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
+        TestableDozeParameters dozeParameters = new TestableDozeParameters(getContext());
+        PowerManager mockedPowerManager = dozeParameters.getPowerManager();
+        dozeParameters.setControlScreenOffAnimation(false);
+        reset(mockedPowerManager);
+        dozeParameters.setControlScreenOffAnimation(true);
+        verify(dozeParameters.getPowerManager()).setDozeAfterScreenOff(eq(false));
+    }
+
+    private class TestableDozeParameters extends DozeParameters {
+        private PowerManager mPowerManager;
+
+        TestableDozeParameters(Context context) {
+            super(context);
+            mPowerManager = mock(PowerManager.class);
+        }
+
+        @Override
+        protected PowerManager getPowerManager() {
+            return mPowerManager;
+        }
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
index ca2f713..203ebe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
@@ -34,18 +34,23 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class DozeScrimControllerTest extends SysuiTestCase {
 
+    @Mock
     private ScrimController mScrimController;
+    @Mock
+    private DozeParameters mDozeParameters;
     private DozeScrimController mDozeScrimController;
 
     @Before
     public void setup() {
-        mScrimController = mock(ScrimController.class);
+        MockitoAnnotations.initMocks(this);
         // Make sure callbacks will be invoked to complete the lifecycle.
         doAnswer(invocationOnMock -> {
             ScrimController.Callback callback = invocationOnMock.getArgument(1);
@@ -56,7 +61,8 @@
         }).when(mScrimController).transitionTo(any(ScrimState.class),
                 any(ScrimController.Callback.class));
 
-        mDozeScrimController = new DozeScrimController(mScrimController, getContext());
+        mDozeScrimController = new DozeScrimController(mScrimController, getContext(),
+                mDozeParameters);
         mDozeScrimController.setDozing(true);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
new file mode 100644
index 0000000..c904ef3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.phone;
+
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.TestableDependency;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
+
+    private HeadsUpAppearanceController mHeadsUpAppearanceController;
+    private ExpandableNotificationRow mFirst;
+    private HeadsUpStatusBarView mHeadsUpStatusBarView;
+    private HeadsUpManagerPhone mHeadsUpManager;
+
+    @Before
+    public void setUp() throws Exception {
+        NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+        mFirst = testHelper.createRow();
+        mDependency.injectMockDependency(DarkIconDispatcher.class);
+        mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
+                mock(TextView.class));
+        mHeadsUpManager = mock(HeadsUpManagerPhone.class);
+        mHeadsUpAppearanceController = new HeadsUpAppearanceController(
+                mock(NotificationIconAreaController.class),
+                mHeadsUpManager,
+                mHeadsUpStatusBarView,
+                mock(NotificationStackScrollLayout.class),
+                mock(NotificationPanelView.class),
+                new View(mContext));
+        mHeadsUpAppearanceController.setExpandedHeight(0.0f, 0.0f);
+    }
+
+    @Test
+    public void testShowinEntryUpdated() {
+        mFirst.setPinned(true);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+        when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        Assert.assertEquals(mFirst.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
+
+        mFirst.setPinned(false);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        Assert.assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
+    }
+
+    @Test
+    public void testShownUpdated() {
+        mFirst.setPinned(true);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+        when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        Assert.assertTrue(mHeadsUpAppearanceController.isShown());
+
+        mFirst.setPinned(false);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        Assert.assertFalse(mHeadsUpAppearanceController.isShown());
+    }
+
+    @Test
+    public void testHeaderUpdated() {
+        mFirst.setPinned(true);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+        when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 0.0f, 0.0f);
+
+        mFirst.setPinned(false);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index f088c0b..45845fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -64,7 +64,6 @@
     private SynchronousScrimController mScrimController;
     private ScrimView mScrimBehind;
     private ScrimView mScrimInFront;
-    private View mHeadsUpScrim;
     private Consumer<Integer> mScrimVisibilityCallback;
     private int mScrimVisibility;
     private LightBarController mLightBarController;
@@ -78,7 +77,6 @@
         mLightBarController = mock(LightBarController.class);
         mScrimBehind = new ScrimView(getContext());
         mScrimInFront = new ScrimView(getContext());
-        mHeadsUpScrim = new View(getContext());
         mWakeLock = mock(WakeLock.class);
         mAlarmManager = mock(AlarmManager.class);
         mAlwaysOnEnabled = true;
@@ -87,7 +85,7 @@
         when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
         when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
         mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind,
-                mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters,
+                mScrimInFront, mScrimVisibilityCallback, mDozeParamenters,
                 mAlarmManager);
     }
 
@@ -349,7 +347,7 @@
     }
 
     @Test
-    public void testWillHideAoDWallpaper() {
+    public void testWillHideAodWallpaper() {
         mScrimController.setWallpaperSupportsAmbientMode(true);
         mScrimController.transitionTo(ScrimState.AOD);
         verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
@@ -415,56 +413,6 @@
     }
 
     @Test
-    public void testHeadsUpScrimOpacity() {
-        mScrimController.setPanelExpansion(0f);
-        mScrimController.onHeadsUpPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertNotEquals("Heads-up scrim should be visible", 0f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-
-        mScrimController.onHeadsUpUnPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertEquals("Heads-up scrim should have disappeared", 0f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-    }
-
-    @Test
-    public void testHeadsUpScrimCounting() {
-        mScrimController.setPanelExpansion(0f);
-        mScrimController.onHeadsUpPinned(null /* row */);
-        mScrimController.onHeadsUpPinned(null /* row */);
-        mScrimController.onHeadsUpPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertNotEquals("Heads-up scrim should be visible", 0f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-
-        mScrimController.onHeadsUpUnPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertEquals("Heads-up scrim should only disappear when counter reaches 0", 1f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-
-        mScrimController.onHeadsUpUnPinned(null /* row */);
-        mScrimController.onHeadsUpUnPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-        Assert.assertEquals("Heads-up scrim should have disappeared", 0f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-    }
-
-    @Test
-    public void testNoHeadsUpScrimExpanded() {
-        mScrimController.setPanelExpansion(1f);
-        mScrimController.onHeadsUpPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertEquals("Heads-up scrim should not be visible when shade is expanded", 0f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-    }
-
-    @Test
     public void testScrimFocus() {
         mScrimController.transitionTo(ScrimState.AOD);
         Assert.assertFalse("Should not be focusable on AOD", mScrimBehind.isFocusable());
@@ -475,6 +423,19 @@
         Assert.assertTrue("Should be focusable on keyguard", mScrimInFront.isFocusable());
     }
 
+    @Test
+    public void testHidesShowWhenLockedActivity() {
+        mScrimController.setWallpaperSupportsAmbientMode(true);
+        mScrimController.setKeyguardOccluded(true);
+        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.finishAnimationsImmediately();
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+
+        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.finishAnimationsImmediately();
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+    }
+
     /**
      * Conserves old notification density after leaving state and coming back.
      *
@@ -527,16 +488,23 @@
 
         private FakeHandler mHandler;
         private boolean mAnimationCancelled;
+        boolean mOnPreDrawCalled;
 
         SynchronousScrimController(LightBarController lightBarController,
-                ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
+                ScrimView scrimBehind, ScrimView scrimInFront,
                 Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
                 AlarmManager alarmManager) {
-            super(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
+            super(lightBarController, scrimBehind, scrimInFront,
                     scrimVisibleListener, dozeParameters, alarmManager);
             mHandler = new FakeHandler(Looper.myLooper());
         }
 
+        @Override
+        public boolean onPreDraw() {
+            mOnPreDrawCalled = true;
+            return super.onPreDraw();
+        }
+
         void finishAnimationsImmediately() {
             boolean[] animationFinished = {false};
             setOnAnimationFinished(()-> animationFinished[0] = true);
@@ -549,7 +517,6 @@
             // Force finish all animations.
             endAnimation(mScrimBehind, TAG_KEY_ANIM);
             endAnimation(mScrimInFront, TAG_KEY_ANIM);
-            endAnimation(mHeadsUpScrim, TAG_KEY_ANIM);
 
             if (!animationFinished[0]) {
                 throw new IllegalStateException("Animation never finished");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 14baaeb..f13fa4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -547,6 +548,16 @@
         verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
     }
 
+    @Test
+    public void testSetOccluded_propagatesToScrimController() {
+        mStatusBar.setOccluded(true);
+        verify(mScrimController).setKeyguardOccluded(eq(true));
+
+        reset(mScrimController);
+        mStatusBar.setOccluded(false);
+        verify(mScrimController).setKeyguardOccluded(eq(false));
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
new file mode 100644
index 0000000..1d2c01d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.stack;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.StatusBarState;
+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.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationRoundnessManagerTest extends SysuiTestCase {
+
+    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
+    private HashSet<View> mAnimatedChildren = new HashSet<>();
+    private Runnable mRoundnessCallback = mock(Runnable.class);
+    private ExpandableNotificationRow mFirst;
+    private ExpandableNotificationRow mSecond;
+
+    @Before
+    public void setUp() throws Exception {
+        NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+        mFirst = testHelper.createRow();
+        mSecond = testHelper.createRow();
+        mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback);
+        mRoundnessManager.setAnimatedChildren(mAnimatedChildren);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mFirst);
+        mRoundnessManager.setExpanded(1.0f, 1.0f);
+    }
+
+    @Test
+    public void testCallbackCalledWhenSecondChanged() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond);
+        verify(mRoundnessCallback, atLeast(1)).run();
+    }
+
+    @Test
+    public void testCallbackCalledWhenFirstChanged() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mFirst);
+        verify(mRoundnessCallback, atLeast(1)).run();
+    }
+
+    @Test
+    public void testRoundnessSetOnLast() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond);
+        Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mSecond.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundnessSetOnNew() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, null);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testCompleteReplacement() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testNotCalledWhenRemoved() {
+        mFirst.setRemoved();
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundedWhenPinnedAndCollapsed() {
+        mFirst.setPinned(true);
+        mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundedWhenGoingAwayAndCollapsed() {
+        mFirst.setHeadsUpAnimatingAway(true);
+        mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundedNormalRoundingWhenExpanded() {
+        mFirst.setHeadsUpAnimatingAway(true);
+        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.0f /* appearFraction */);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testTrackingHeadsUpRoundedIfPushingUp() {
+        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, -0.5f /* appearFraction */);
+        mRoundnessManager.setTrackingHeadsUp(mFirst);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testTrackingHeadsUpNotRoundedIfPushingDown() {
+        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.5f /* appearFraction */);
+        mRoundnessManager.setTrackingHeadsUp(mFirst);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index ed068b9..5c5978a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -88,7 +88,7 @@
 
     final int mId;
 
-    final AccessibilityServiceInfo mAccessibilityServiceInfo;
+    protected final AccessibilityServiceInfo mAccessibilityServiceInfo;
 
     // Lock must match the one used by AccessibilityManagerService
     protected final Object mLock;
@@ -340,6 +340,10 @@
         }
     }
 
+    public int getCapabilities() {
+        return mAccessibilityServiceInfo.getCapabilities();
+    }
+
     int getRelevantEventTypes() {
         return (mUsesAccessibilityCache ? AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK : 0)
                 | mEventTypes;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ecd47e8..8941b49 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -621,7 +621,7 @@
             for (int i = 0; i < serviceCount; ++i) {
                 final AccessibilityServiceConnection service = services.get(i);
                 if ((service.mFeedbackType & feedbackType) != 0) {
-                    result.add(service.mAccessibilityServiceInfo);
+                    result.add(service.getServiceInfo());
                 }
             }
             return result;
@@ -1874,7 +1874,7 @@
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
             AccessibilityServiceConnection service = userState.mBoundServices.get(i);
-            if ((service.mAccessibilityServiceInfo.getCapabilities()
+            if ((service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) {
                 userState.mIsPerformGesturesEnabled = true;
                 return;
@@ -1888,7 +1888,7 @@
         for (int i = 0; i < serviceCount; i++) {
             AccessibilityServiceConnection service = userState.mBoundServices.get(i);
             if (service.mRequestFilterKeyEvents
-                    && (service.mAccessibilityServiceInfo.getCapabilities()
+                    && (service.getCapabilities()
                             & AccessibilityServiceInfo
                             .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) {
                 userState.mIsFilterKeyEventsEnabled = true;
@@ -2124,7 +2124,7 @@
             // Starting in JB-MR2 we request an accessibility service to declare
             // certain capabilities in its meta-data to allow it to enable the
             // corresponding features.
-            if ((service.mAccessibilityServiceInfo.getCapabilities()
+            if ((service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION) != 0) {
                 return true;
             }
@@ -3446,22 +3446,22 @@
         }
 
         public boolean canRetrieveWindowContentLocked(AbstractAccessibilityServiceConnection service) {
-            return (service.mAccessibilityServiceInfo.getCapabilities()
+            return (service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
         }
 
         public boolean canControlMagnification(AbstractAccessibilityServiceConnection service) {
-            return (service.mAccessibilityServiceInfo.getCapabilities()
+            return (service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION) != 0;
         }
 
         public boolean canPerformGestures(AccessibilityServiceConnection service) {
-            return (service.mAccessibilityServiceInfo.getCapabilities()
+            return (service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0;
         }
 
         public boolean canCaptureFingerprintGestures(AccessibilityServiceConnection service) {
-            return (service.mAccessibilityServiceInfo.getCapabilities()
+            return (service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES) != 0;
         }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 89bf82d..eb18f06 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -165,7 +165,14 @@
         }
     }
 
-    public void initializeService() {
+    @Override
+    public AccessibilityServiceInfo getServiceInfo() {
+        // Update crashed data
+        mAccessibilityServiceInfo.crashed = mWasConnectedAndDied;
+        return mAccessibilityServiceInfo;
+    }
+
+    private void initializeService() {
         IAccessibilityServiceClient serviceInterface = null;
         synchronized (mLock) {
             UserState userState = mUserStateWeakReference.get();
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index 5b5d18f..9f44197 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -55,7 +55,7 @@
  * magnification region. If a value is out of bounds, it will be adjusted to guarantee these
  * constraints.
  */
-class MagnificationController implements Handler.Callback {
+public class MagnificationController implements Handler.Callback {
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "MagnificationController";
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 755fc54..7f57615 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1438,11 +1438,25 @@
                 final AutofillValue filledValue = viewState.getAutofilledValue();
 
                 if (!value.equals(filledValue)) {
-                    if (sDebug) {
-                        Slog.d(TAG, "found a change on required " + id + ": " + filledValue
-                                + " => " + value);
+                    boolean changed = true;
+                    if (filledValue == null) {
+                        // Dataset was not autofilled, make sure initial value didn't change.
+                        final AutofillValue initialValue = getValueFromContextsLocked(id);
+                        if (initialValue != null && initialValue.equals(value)) {
+                            if (sDebug) {
+                                Slog.d(TAG, "id " + id + " is part of dataset but initial value "
+                                        + "didn't change: " + value);
+                            }
+                            changed = false;
+                        }
                     }
-                    atLeastOneChanged = true;
+                    if (changed) {
+                        if (sDebug) {
+                            Slog.d(TAG, "found a change on required " + id + ": " + filledValue
+                                    + " => " + value);
+                        }
+                        atLeastOneChanged = true;
+                    }
                 }
             }
         }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d6f6c6c..4cac707 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -215,13 +215,6 @@
     // Timeout interval for deciding that a bind or clear-data has taken too long
     private static final long TIMEOUT_INTERVAL = 10 * 1000;
 
-    // Timeout intervals for agent backup & restore operations
-    public static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
-    public static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
-    public static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
-    public static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
-    public static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
-
     // User confirmation timeout for a full backup/restore operation.  It's this long in
     // order to give them time to enter the backup password.
     private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
@@ -232,6 +225,7 @@
     private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
 
     private BackupManagerConstants mConstants;
+    private BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private Context mContext;
     private PackageManager mPackageManager;
     private IPackageManager mPackageManagerBinder;
@@ -315,6 +309,10 @@
         return mConstants;
     }
 
+    public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
+        return mAgentTimeoutParameters;
+    }
+
     public Context getContext() {
         return mContext;
     }
@@ -856,6 +854,10 @@
         // require frequent starting and stopping.
         mConstants.start();
 
+        mAgentTimeoutParameters = new
+                BackupAgentTimeoutParameters(mBackupHandler, mContext.getContentResolver());
+        mAgentTimeoutParameters.start();
+
         // Set up the various sorts of package tracking we do
         mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
         initPackageTracking();
@@ -3407,7 +3409,7 @@
             }
             mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
             mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
-                    TIMEOUT_RESTORE_INTERVAL);
+                    mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
         }
         return mActiveRestoreSession;
     }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
index 7b021c6..aabe7f6 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
@@ -191,4 +191,7 @@
   void dump(FileDescriptor fd, PrintWriter pw, String[] args);
 
   IBackupManager getBackupManagerBinder();
+
+  // Gets access to the backup/restore agent timeout parameters.
+  BackupAgentTimeoutParameters getAgentTimeoutParameters();
 }
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 4755877..f08c655 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -4,8 +4,8 @@
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
@@ -59,6 +59,7 @@
     private ParcelFileDescriptor mSavedState;
     private ParcelFileDescriptor mBackupData;
     private ParcelFileDescriptor mNewState;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
             BackupManagerServiceInterface backupManagerService, PackageManager packageManager,
@@ -81,6 +82,7 @@
                 pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
 
         mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+        mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
     }
 
     public void backupOnePackage() throws IOException {
@@ -148,8 +150,9 @@
     // Return true on backup success, false otherwise
     private boolean invokeAgentForAdbBackup(String packageName, IBackupAgent agent) {
         int token = mBackupManagerService.generateRandomIntegerToken();
+        long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
         try {
-            mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null,
+            mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
                     OP_TYPE_BACKUP_WAIT);
 
             // Start backup and wait for BackupManagerService to get callback for success or timeout
@@ -231,14 +234,14 @@
     }
 
     private void writeBackupData() throws IOException {
-
         int token = mBackupManagerService.generateRandomIntegerToken();
+        long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
 
         ParcelFileDescriptor[] pipes = null;
         try {
             pipes = ParcelFileDescriptor.createPipe();
 
-            mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null,
+            mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
                     OP_TYPE_BACKUP_WAIT);
 
             // We will have to create a runnable that will read the manifest and backup data we
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 0582aba..597da21 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -25,9 +25,6 @@
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
-import static com.android.server.backup.BackupManagerService
-        .TIMEOUT_SHARED_BACKUP_INTERVAL;
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
@@ -45,8 +42,9 @@
 import android.util.StringBuilderPrinter;
 
 import com.android.server.AppWidgetBackupBridge;
-import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.utils.FullBackupUtils;
 
 import java.io.BufferedOutputStream;
@@ -75,6 +73,7 @@
     private final long mQuota;
     private final int mOpToken;
     private final int mTransportFlags;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     class FullBackupRunner implements Runnable {
 
@@ -137,8 +136,8 @@
                 final boolean isSharedStorage =
                         mPackage.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
                 final long timeout = isSharedStorage ?
-                        TIMEOUT_SHARED_BACKUP_INTERVAL :
-                        TIMEOUT_FULL_BACKUP_INTERVAL;
+                        mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
+                        mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
 
                 if (DEBUG) {
                     Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
@@ -180,6 +179,7 @@
         mQuota = quota;
         mOpToken = opToken;
         mTransportFlags = transportFlags;
+        mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
     }
 
     public int preflightCheck() throws RemoteException {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index 40b6967..d441cf6 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -19,7 +19,6 @@
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
 
 import android.app.backup.IBackupManager;
 import android.content.ComponentName;
@@ -33,6 +32,7 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IObbBackupService;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.utils.FullBackupUtils;
 
@@ -46,10 +46,12 @@
 
     private BackupManagerService backupManagerService;
     volatile IObbBackupService mService;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public FullBackupObbConnection(BackupManagerService backupManagerService) {
         this.backupManagerService = backupManagerService;
         mService = null;
+        mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
     }
 
     public void establish() {
@@ -75,8 +77,10 @@
         try {
             pipes = ParcelFileDescriptor.createPipe();
             int token = backupManagerService.generateRandomIntegerToken();
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             backupManagerService.prepareOperationTimeout(
-                    token, TIMEOUT_FULL_BACKUP_INTERVAL, null, OP_TYPE_BACKUP_WAIT);
+                    token, fullBackupAgentTimeoutMillis, null, OP_TYPE_BACKUP_WAIT);
             mService.backupObbs(pkg.packageName, pipes[1], token,
                     backupManagerService.getBackupManagerBinder());
             FullBackupUtils.routeSocketDataToOutput(pipes[0], out);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 2c2dd85..1ea3eb5 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -22,7 +22,6 @@
 import static com.android.server.backup.BackupManagerService.OP_PENDING;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
 
 import android.annotation.Nullable;
 import android.app.IBackupAgent;
@@ -44,6 +43,7 @@
 
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FullBackupJob;
 import com.android.server.backup.BackupManagerService;
@@ -146,6 +146,7 @@
     private volatile boolean mIsDoingBackup;
     private volatile boolean mCancelAll;
     private final int mCurrentOpToken;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public PerformFullTransportBackupTask(BackupManagerService backupManagerService,
             TransportClient transportClient,
@@ -167,6 +168,7 @@
         mUserInitiated = userInitiated;
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
         mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken();
+        mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
 
         if (backupManagerService.isBackupOperationInProgress()) {
             if (DEBUG) {
@@ -698,9 +700,11 @@
         @Override
         public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
             int result;
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
                 backupManagerService.prepareOperationTimeout(
-                        mCurrentOpToken, TIMEOUT_FULL_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT);
+                        mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
                 backupManagerService.addBackupTrace("preflighting");
                 if (MORE_DEBUG) {
                     Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
@@ -713,7 +717,7 @@
                 // timeout had been produced.  In case of a real backstop timeout, mResult
                 // will still contain the value it was constructed with, AGENT_ERROR, which
                 // intentionaly falls into the "just report failure" code.
-                mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
 
                 long totalSize = mResult.get();
                 // If preflight timed out, mResult will contain error code as int.
@@ -769,8 +773,10 @@
 
         @Override
         public long getExpectedSizeOrErrorCode() {
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
-                mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
                 return mResult.get();
             } catch (InterruptedException e) {
                 return BackupTransport.NO_MORE_DATA;
@@ -863,8 +869,10 @@
         // If preflight succeeded, returns positive number - preflight size,
         // otherwise return negative error code.
         long getPreflightResultBlocking() {
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
-                mPreflightLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mPreflightLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
                 if (mIsCancelled) {
                     return BackupManager.ERROR_BACKUP_CANCELLED;
                 }
@@ -879,8 +887,10 @@
         }
 
         int getBackupResultBlocking() {
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
-                mBackupLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mBackupLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
                 if (mIsCancelled) {
                     return BackupManager.ERROR_BACKUP_CANCELLED;
                 }
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 136fada..5886862 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -19,7 +19,6 @@
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
 
 import android.app.backup.RestoreSet;
 import android.content.Intent;
@@ -34,6 +33,7 @@
 
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
@@ -81,10 +81,12 @@
     public static final int MSG_OP_COMPLETE = 21;
 
     private final BackupManagerService backupManagerService;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public BackupHandler(BackupManagerService backupManagerService, Looper looper) {
         super(looper);
         this.backupManagerService = backupManagerService;
+        mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
     }
 
     public void handleMessage(Message msg) {
@@ -322,7 +324,8 @@
 
                     // Done: reset the session timeout clock
                     removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
-                    sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
+                    sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
+                            mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
 
                     params.listener.onFinished(callerLogString);
                 }
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index 11394e66..0313066 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -24,7 +24,6 @@
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
 
@@ -57,6 +56,7 @@
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.KeyValueBackupJob;
@@ -142,6 +142,7 @@
     private boolean mFinished;
     private final boolean mUserInitiated;
     private final boolean mNonIncremental;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     private volatile boolean mCancelAll;
 
@@ -162,6 +163,7 @@
         mPendingFullBackups = pendingFullBackups;
         mUserInitiated = userInitiated;
         mNonIncremental = nonIncremental;
+        mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
 
         mStateDir = new File(backupManagerService.getBaseStateDir(), dirName);
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
@@ -711,8 +713,10 @@
 
             // Initiate the target's backup pass
             backupManagerService.addBackupTrace("setting timeout");
+            long kvBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
             backupManagerService.prepareOperationTimeout(
-                    mEphemeralOpToken, TIMEOUT_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT);
+                    mEphemeralOpToken, kvBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
             backupManagerService.addBackupTrace("calling agent doBackup()");
 
             agent.doBackup(
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index e4f3a9d..6175629 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -18,10 +18,10 @@
 
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
 
 import android.util.Slog;
 
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 
@@ -37,18 +37,22 @@
     private BackupManagerService backupManagerService;
     final CountDownLatch mLatch;
     private final int mCurrentOpToken;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public AdbRestoreFinishedLatch(BackupManagerService backupManagerService,
             int currentOpToken) {
         this.backupManagerService = backupManagerService;
         mLatch = new CountDownLatch(1);
         mCurrentOpToken = currentOpToken;
+        mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
     }
 
     void await() {
         boolean latched = false;
+        long fullBackupAgentTimeoutMillis =
+                mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
         try {
-            latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+            latched = mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
         } catch (InterruptedException e) {
             Slog.w(TAG, "Interrupted!");
         }
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index c1a1c1d..f168afed 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -23,9 +23,6 @@
 import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
-import static com.android.server.backup.BackupManagerService
-        .TIMEOUT_SHARED_BACKUP_INTERVAL;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
 import android.app.ApplicationThreadConstants;
@@ -43,10 +40,11 @@
 import android.util.Slog;
 
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FileMetadata;
 import com.android.server.backup.KeyValueAdbRestoreEngine;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.fullbackup.FullBackupObbConnection;
 import com.android.server.backup.utils.BytesReadListener;
 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
@@ -121,6 +119,8 @@
 
     final int mEphemeralOpToken;
 
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+
     public FullRestoreEngine(BackupManagerService backupManagerService,
             BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
             IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
@@ -135,6 +135,7 @@
         mAllowObbs = allowObbs;
         mBuffer = new byte[32 * 1024];
         mBytes = 0;
+        mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
     }
 
     public IBackupAgent getAgent() {
@@ -381,8 +382,8 @@
                         long toCopy = info.size;
                         final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE);
                         final long timeout = isSharedStorage ?
-                                TIMEOUT_SHARED_BACKUP_INTERVAL :
-                                TIMEOUT_RESTORE_INTERVAL;
+                                mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
+                                mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
                         try {
                             mBackupManagerService.prepareOperationTimeout(token,
                                     timeout,
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index dacde0b..221637c 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -16,8 +16,6 @@
 
 package com.android.server.backup.restore;
 
-import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
-import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
 import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
 import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
 import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
@@ -28,8 +26,8 @@
 import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
 import android.app.ApplicationThreadConstants;
@@ -49,6 +47,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.FileMetadata;
 import com.android.server.backup.KeyValueAdbRestoreEngine;
@@ -101,6 +100,7 @@
     private byte[] mWidgetData = null;
 
     private long mBytes;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     // Runner that can be placed on a separate thread to do in-process invocation
     // of the "restore finished" API asynchronously.  Used by adb restore.
@@ -155,6 +155,7 @@
         mAgentPackage = null;
         mTargetApp = null;
         mObbConnection = new FullBackupObbConnection(backupManagerService);
+        mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
 
         // Which packages we've already wiped data on.  We prepopulate this
         // with a whitelist of packages known to be unclearable.
@@ -643,9 +644,11 @@
                     if (okay) {
                         boolean agentSuccess = true;
                         long toCopy = info.size;
+                        long restoreAgentTimeoutMillis =
+                                mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
                         try {
                             mBackupManagerService.prepareOperationTimeout(
-                                    token, TIMEOUT_RESTORE_INTERVAL, null, OP_TYPE_RESTORE_WAIT);
+                                    token, restoreAgentTimeoutMillis, null, OP_TYPE_RESTORE_WAIT);
 
                             if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
                                 if (DEBUG) {
@@ -820,10 +823,12 @@
                 // In the adb restore case, we do restore-finished here
                 if (doRestoreFinished) {
                     final int token = mBackupManagerService.generateRandomIntegerToken();
+                    long fullBackupAgentTimeoutMillis =
+                            mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
                     final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
                             mBackupManagerService, token);
                     mBackupManagerService.prepareOperationTimeout(
-                            token, TIMEOUT_FULL_BACKUP_INTERVAL, latch, OP_TYPE_RESTORE_WAIT);
+                            token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
                     if (mTargetApp.processName.equals("system")) {
                         if (MORE_DEBUG) {
                             Slog.d(TAG, "system agent - restoreFinished on thread");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 4b467e5..069e3b6 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -23,9 +23,6 @@
 import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
 import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService
-        .TIMEOUT_RESTORE_FINISHED_INTERVAL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
@@ -59,6 +56,7 @@
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.BackupUtils;
 import com.android.server.backup.PackageManagerBackupAgent;
@@ -160,6 +158,7 @@
     ParcelFileDescriptor mNewState;
 
     private final int mEphemeralOpToken;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     // This task can assume that the wakelock is properly held for it and doesn't have to worry
     // about releasing it.
@@ -190,6 +189,7 @@
         mFinished = false;
         mDidLaunch = false;
         mListener = listener;
+        mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
 
         if (targetPackage != null) {
             // Single package restore
@@ -760,8 +760,9 @@
             // Kick off the restore, checking for hung agents.  The timeout or
             // the operationComplete() callback will schedule the next step,
             // so we do not do that here.
+            long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
             backupManagerService.prepareOperationTimeout(
-                    mEphemeralOpToken, TIMEOUT_RESTORE_INTERVAL, this, OP_TYPE_RESTORE_WAIT);
+                    mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
             mAgent.doRestore(mBackupData, appVersionCode, mNewState,
                     mEphemeralOpToken, backupManagerService.getBackupManagerBinder());
         } catch (Exception e) {
@@ -813,9 +814,11 @@
             Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName);
         }
         try {
+            long restoreAgentFinishedTimeoutMillis =
+                    mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis();
             backupManagerService
                     .prepareOperationTimeout(mEphemeralOpToken,
-                            TIMEOUT_RESTORE_FINISHED_INTERVAL, this,
+                            restoreAgentFinishedTimeoutMillis, this,
                             OP_TYPE_RESTORE_WAIT);
             mAgent.doRestoreFinished(mEphemeralOpToken,
                     backupManagerService.getBackupManagerBinder());
@@ -1109,9 +1112,10 @@
         } else {
             // We were invoked via an active restore session, not by the Package
             // Manager, so start up the session timeout again.
+            long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
             backupManagerService.getBackupHandler().sendEmptyMessageDelayed(
                     MSG_RESTORE_SESSION_TIMEOUT,
-                    TIMEOUT_RESTORE_INTERVAL);
+                    restoreAgentTimeoutMillis);
         }
 
         // Kick off any work that may be needed regarding app widget restores
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 45a4dfb9..f1f251f 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -36,6 +36,7 @@
 import android.net.IpSecTransformResponse;
 import android.net.IpSecTunnelInterfaceResponse;
 import android.net.IpSecUdpEncapResponse;
+import android.net.LinkAddress;
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
@@ -618,10 +619,8 @@
                                 spi,
                                 mConfig.getMarkValue(),
                                 mConfig.getMarkMask());
-            } catch (ServiceSpecificException e) {
-                // FIXME: get the error code and throw is at an IOException from Errno Exception
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
+            } catch (RemoteException | ServiceSpecificException e) {
+                Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
             }
 
             getResourceTracker().give();
@@ -681,10 +680,8 @@
                         .getNetdInstance()
                         .ipSecDeleteSecurityAssociation(
                                 mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
-            } catch (ServiceSpecificException e) {
-                // FIXME: get the error code and throw is at an IOException from Errno Exception
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId);
+            } catch (ServiceSpecificException | RemoteException e) {
+                Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
             }
 
             mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
@@ -829,15 +826,13 @@
                                         0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff);
                     }
                 }
-            } catch (ServiceSpecificException e) {
-                // FIXME: get the error code and throw is at an IOException from Errno Exception
-            } catch (RemoteException e) {
+            } catch (ServiceSpecificException | RemoteException e) {
                 Log.e(
                         TAG,
                         "Failed to delete VTI with interface name: "
                                 + mInterfaceName
                                 + " and id: "
-                                + mResourceId);
+                                + mResourceId, e);
             }
 
             getResourceTracker().give();
@@ -1319,7 +1314,9 @@
      * from multiple local IP addresses over the same tunnel.
      */
     @Override
-    public synchronized void addAddressToTunnelInterface(int tunnelResourceId, String localAddr) {
+    public synchronized void addAddressToTunnelInterface(
+            int tunnelResourceId, LinkAddress localAddr) {
+        enforceNetworkStackPermission();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
 
         // Get tunnelInterface record; if no such interface is found, will throw
@@ -1327,8 +1324,21 @@
         TunnelInterfaceRecord tunnelInterfaceInfo =
                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
 
-        // TODO: Add calls to netd:
-        //       Add address to TunnelInterface
+        try {
+            // We can assume general validity of the IP address, since we get them as a
+            // LinkAddress, which does some validation.
+            mSrvConfig
+                    .getNetdInstance()
+                    .interfaceAddAddress(
+                            tunnelInterfaceInfo.mInterfaceName,
+                            localAddr.getAddress().getHostAddress(),
+                            localAddr.getPrefixLength());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
+            throw new IllegalArgumentException(e);
+        }
     }
 
     /**
@@ -1337,7 +1347,8 @@
      */
     @Override
     public synchronized void removeAddressFromTunnelInterface(
-            int tunnelResourceId, String localAddr) {
+            int tunnelResourceId, LinkAddress localAddr) {
+        enforceNetworkStackPermission();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
 
         // Get tunnelInterface record; if no such interface is found, will throw
@@ -1345,8 +1356,21 @@
         TunnelInterfaceRecord tunnelInterfaceInfo =
                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
 
-        // TODO: Add calls to netd:
-        //       Remove address from TunnelInterface
+        try {
+            // We can assume general validity of the IP address, since we get them as a
+            // LinkAddress, which does some validation.
+            mSrvConfig
+                    .getNetdInstance()
+                    .interfaceDelAddress(
+                            tunnelInterfaceInfo.mInterfaceName,
+                            localAddr.getAddress().getHostAddress(),
+                            localAddr.getPrefixLength());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
+            throw new IllegalArgumentException(e);
+        }
     }
 
     /**
@@ -1467,6 +1491,13 @@
         IpSecAlgorithm crypt = c.getEncryption();
         IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
 
+        String cryptName;
+        if (crypt == null) {
+            cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
+        } else {
+            cryptName = crypt.getName();
+        }
+
         mSrvConfig
                 .getNetdInstance()
                 .ipSecAddSecurityAssociation(
@@ -1481,7 +1512,7 @@
                         (auth != null) ? auth.getName() : "",
                         (auth != null) ? auth.getKey() : new byte[] {},
                         (auth != null) ? auth.getTruncationLengthBits() : 0,
-                        (crypt != null) ? crypt.getName() : "",
+                        cryptName,
                         (crypt != null) ? crypt.getKey() : new byte[] {},
                         (crypt != null) ? crypt.getTruncationLengthBits() : 0,
                         (authCrypt != null) ? authCrypt.getName() : "",
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 305c1d0..fb8f749 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -187,6 +187,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
 import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
@@ -755,21 +756,6 @@
     private final RecentTasks mRecentTasks;
 
     /**
-     * For addAppTask: cached of the last activity component that was added.
-     */
-    ComponentName mLastAddedTaskComponent;
-
-    /**
-     * For addAppTask: cached of the last activity uid that was added.
-     */
-    int mLastAddedTaskUid;
-
-    /**
-     * For addAppTask: cached of the last ActivityInfo that was added.
-     */
-    ActivityInfo mLastAddedTaskActivity;
-
-    /**
      * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
      */
     String mDeviceOwnerName;
@@ -10611,27 +10597,24 @@
                         intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
                     }
                 }
-                if (!comp.equals(mLastAddedTaskComponent) || callingUid != mLastAddedTaskUid) {
-                    mLastAddedTaskActivity = null;
-                }
-                ActivityInfo ainfo = mLastAddedTaskActivity;
-                if (ainfo == null) {
-                    ainfo = mLastAddedTaskActivity = AppGlobals.getPackageManager().getActivityInfo(
-                            comp, 0, UserHandle.getUserId(callingUid));
-                    if (ainfo.applicationInfo.uid != callingUid) {
-                        throw new SecurityException(
-                                "Can't add task for another application: target uid="
-                                + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
-                    }
+                final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp, 0,
+                        UserHandle.getUserId(callingUid));
+                if (ainfo.applicationInfo.uid != callingUid) {
+                    throw new SecurityException(
+                            "Can't add task for another application: target uid="
+                            + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
                 }
 
-                TaskRecord task = TaskRecord.create(this,
-                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
-                        ainfo, intent, description);
+                final ActivityStack stack = r.getStack();
+                final TaskRecord task = stack.createTaskRecord(
+                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
+                        null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
                 if (!mRecentTasks.addToBottom(task)) {
+                    // The app has too many tasks already and we can't add any more
+                    stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING);
                     return INVALID_TASK_ID;
                 }
-                r.getStack().addTask(task, !ON_TOP, "addAppTask");
+                task.lastTaskDescription.copyFrom(description);
 
                 // TODO: Send the thumbnail to WM to store it.
 
@@ -21770,6 +21753,54 @@
     // INSTRUMENTATION
     // =========================================================
 
+    private static String[] HIDDENAPI_EXEMPT_PACKAGES = {
+        "com.android.bluetooth.tests",
+        "com.android.managedprovisioning.tests",
+        "com.android.frameworks.coretests",
+        "com.android.frameworks.coretests.binderproxycountingtestapp",
+        "com.android.frameworks.coretests.binderproxycountingtestservice",
+        "com.android.frameworks.tests.net",
+        "com.android.frameworks.tests.uiservices",
+        "com.android.coretests.apps.bstatstestapp",
+        "com.android.servicestests.apps.conntestapp",
+        "com.android.frameworks.servicestests",
+        "com.android.frameworks.utiltests",
+        "com.android.mtp.tests",
+        "android.mtp",
+        "com.android.documentsui.tests",
+        "com.android.shell.tests",
+        "com.android.systemui.tests",
+        "com.android.testables",
+        "android.net.wifi.test",
+        "com.android.server.wifi.test",
+        "com.android.frameworks.telephonytests",
+        "com.android.providers.contacts.tests",
+        "com.android.providers.contacts.tests2",
+        "com.android.settings.tests.unit",
+        "com.android.server.telecom.tests",
+        "com.android.vcard.tests",
+        "com.android.providers.blockednumber.tests",
+        "android.settings.functional",
+        "com.android.notification.functional",
+        "com.android.frameworks.dexloggertest",
+        "com.android.server.usb",
+        "com.android.providers.downloads.tests",
+        "com.android.emergency.tests.unit",
+        "com.android.providers.calendar.tests",
+        "com.android.settingslib",
+        "com.android.rs.test",
+        "com.android.printspooler.outofprocess.tests",
+        "com.android.cellbroadcastreceiver.tests.unit",
+        "com.android.providers.telephony.tests",
+        "com.android.carrierconfig.tests",
+        "com.android.phone.tests",
+        "com.android.service.ims.presence.tests",
+        "com.android.providers.setting.test",
+        "com.android.frameworks.locationtests",
+        "com.android.frameworks.coretests.privacy",
+        "com.android.settings.ui",
+    };
+
     public boolean startInstrumentation(ComponentName className,
             String profileFile, int flags, Bundle arguments,
             IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection,
@@ -21852,6 +21883,14 @@
             }
             boolean disableHiddenApiChecks =
                     (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
+
+            // TODO: Temporary whitelist of packages which need to be exempt from hidden API
+            //       checks. Remove this as soon as the testing infrastructure allows to set
+            //       the flag in AndroidTest.xml.
+            if (Arrays.asList(HIDDENAPI_EXEMPT_PACKAGES).contains(ai.packageName)) {
+                disableHiddenApiChecks = true;
+            }
+
             ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
                     abiOverride);
             app.instr = activeInstr;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 19fffbb..e4695b6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -643,7 +643,7 @@
     void setWindowManager(WindowManagerService wm) {
         synchronized (mService) {
             mWindowManager = wm;
-            mKeyguardController.setWindowManager(wm);
+            getKeyguardController().setWindowManager(wm);
 
             mDisplayManager =
                     (DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
@@ -1312,7 +1312,7 @@
 
             r.setProcess(app);
 
-            if (mKeyguardController.isKeyguardLocked()) {
+            if (getKeyguardController().isKeyguardLocked()) {
                 r.notifyUnknownVisibilityLaunched();
             }
 
@@ -3377,7 +3377,7 @@
                 } else {
                     stack.awakeFromSleepingLocked();
                     if (isFocusedStack(stack)
-                            && !mKeyguardController.isKeyguardShowing(display.mDisplayId)) {
+                            && !getKeyguardController().isKeyguardShowing(display.mDisplayId)) {
                         // If the keyguard is unlocked - resume immediately.
                         // It is possible that the display will not be awake at the time we
                         // process the keyguard going away, which can happen before the sleep token
@@ -3501,7 +3501,7 @@
 
     void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
             boolean preserveWindows) {
-        mKeyguardController.beginActivityVisibilityUpdate();
+        getKeyguardController().beginActivityVisibilityUpdate();
         try {
             // First the front stacks. In case any are not fullscreen and are in front of home.
             for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -3512,7 +3512,7 @@
                 }
             }
         } finally {
-            mKeyguardController.endActivityVisibilityUpdate();
+            getKeyguardController().endActivityVisibilityUpdate();
         }
     }
 
@@ -3799,7 +3799,7 @@
         pw.print(prefix); pw.print("isHomeRecentsComponent=");
         pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
 
-        mKeyguardController.dump(pw, prefix);
+        getKeyguardController().dump(pw, prefix);
         mService.mLockTaskController.dump(pw, prefix);
     }
 
@@ -3810,7 +3810,7 @@
             ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
             activityDisplay.writeToProto(proto, DISPLAYS);
         }
-        mKeyguardController.writeToProto(proto, KEYGUARD_CONTROLLER);
+        getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
         if (mFocusedStack != null) {
             proto.write(FOCUSED_STACK_ID, mFocusedStack.mStackId);
             ActivityRecord focusedActivity = getResumedActivityLocked();
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index bd53eac..a30a944 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1834,7 +1834,8 @@
                                     mNoAnimation, mOptions, mStartActivity.appTimeTracker,
                                     "bringToFrontInsteadOfAdjacentLaunch");
                         }
-                        mMovedToFront = true;
+                        mMovedToFront = launchStack != launchStack.getDisplay()
+                                .getTopStackInWindowingMode(launchStack.getWindowingMode());
                     } else if (launchStack.mDisplayId != mTargetStack.mDisplayId) {
                         // Target and computed stacks are on different displays and we've
                         // found a matching task - move the existing instance to that display and
@@ -2393,6 +2394,11 @@
         return this;
     }
 
+    @VisibleForTesting
+    Intent getIntent() {
+        return mRequest.intent;
+    }
+
     ActivityStarter setReason(String reason) {
         mRequest.reason = reason;
         return this;
diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
index 690d985..5bf5020 100644
--- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
@@ -43,7 +43,7 @@
         {Settings.Global.FPS_DEVISOR, ThreadedRenderer.DEBUG_FPS_DIVISOR},
         {Settings.Global.DISPLAY_PANEL_LPM, "sys.display_panel_lpm"},
         {Settings.Global.SYS_UIDCPUPOWER, "sys.uidcpupower"},
-        {Settings.Global.SYS_TRACED, "persist.traced.enable"},
+        {Settings.Global.SYS_TRACED, "sys.traced.enable_override"},
     };
 
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9ef84d2..c036549 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1649,16 +1649,7 @@
 
             // Check if volume update should be send to Hearing Aid
             if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
-                synchronized (mHearingAidLock) {
-                    if (mHearingAid != null) {
-                        //hearing aid expect volume value in range -128dB to 0dB
-                        int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, newIndex/10,
-                                AudioSystem.DEVICE_OUT_HEARING_AID);
-                        if (gainDB < BT_HEARING_AID_GAIN_MIN)
-                            gainDB = BT_HEARING_AID_GAIN_MIN;
-                        mHearingAid.setVolume(gainDB);
-                    }
-                }
+                setHearingAidVolume(newIndex);
             }
 
             // Check if volume update should be sent to Hdmi system audio.
@@ -1907,16 +1898,7 @@
             }
 
             if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
-                synchronized (mHearingAidLock) {
-                    if (mHearingAid != null) {
-                        //hearing aid expect volume value in range -128dB to 0dB
-                        int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, index/10, AudioSystem.DEVICE_OUT_HEARING_AID);
-                        if (gainDB < BT_HEARING_AID_GAIN_MIN)
-                            gainDB = BT_HEARING_AID_GAIN_MIN;
-                        mHearingAid.setVolume(gainDB);
-
-                    }
-                }
+                setHearingAidVolume(index);
             }
 
             if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
@@ -5624,8 +5606,24 @@
                 makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
     }
 
+    private void setHearingAidVolume(int index) {
+        synchronized (mHearingAidLock) {
+            if (mHearingAid != null) {
+                //hearing aid expect volume value in range -128dB to 0dB
+                int gainDB = (int)AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, index/10,
+                        AudioSystem.DEVICE_OUT_HEARING_AID);
+                if (gainDB < BT_HEARING_AID_GAIN_MIN)
+                    gainDB = BT_HEARING_AID_GAIN_MIN;
+                mHearingAid.setVolume(gainDB);
+            }
+        }
+    }
+
     // must be called synchronized on mConnectedDevices
     private void makeHearingAidDeviceAvailable(String address, String name, String eventSource) {
+        int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(AudioSystem.DEVICE_OUT_HEARING_AID);
+        setHearingAidVolume(index);
+
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
                 AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
         mConnectedDevices.put(
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index cb53521..5e1afeb 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -282,6 +282,10 @@
         return mBrightnessMapper.isDefaultConfig();
     }
 
+    public BrightnessConfiguration getDefaultConfig() {
+        return mBrightnessMapper.getDefaultConfig();
+    }
+
     private boolean setDisplayPolicy(int policy) {
         if (mDisplayPolicy == policy) {
             return false;
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index c0d2599..4313d17 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -206,6 +206,8 @@
     /** @return true if the current brightness config is the default one */
     public abstract boolean isDefaultConfig();
 
+    public abstract BrightnessConfiguration getDefaultConfig();
+
     public abstract void dump(PrintWriter pw);
 
     private static float normalizeAbsoluteBrightness(int brightness) {
@@ -406,6 +408,9 @@
         }
 
         @Override
+        public BrightnessConfiguration getDefaultConfig() { return null; }
+
+        @Override
         public void dump(PrintWriter pw) {
             pw.println("SimpleMappingStrategy");
             pw.println("  mSpline=" + mSpline);
@@ -533,6 +538,9 @@
         }
 
         @Override
+        public BrightnessConfiguration getDefaultConfig() { return mDefaultConfig; }
+
+        @Override
         public void dump(PrintWriter pw) {
             pw.println("PhysicalMappingStrategy");
             pw.println("  mConfig=" + mConfig);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9861ea7..c4b2b5e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1878,6 +1878,48 @@
         }
 
         @Override // Binder call
+        public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
+                    "Permission required to read the display's brightness configuration");
+            if (userId != UserHandle.getCallingUserId()) {
+                mContext.enforceCallingOrSelfPermission(
+                        Manifest.permission.INTERACT_ACROSS_USERS,
+                        "Permission required to read the display brightness"
+                                + " configuration of another user");
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final int userSerial = getUserManager().getUserSerialNumber(userId);
+                synchronized (mSyncRoot) {
+                    BrightnessConfiguration config =
+                            mPersistentDataStore.getBrightnessConfiguration(userSerial);
+                    if (config == null) {
+                        config = mDisplayPowerController.getDefaultBrightnessConfiguration();
+                    }
+                    return config;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
+                    "Permission required to read the display's default brightness configuration");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mSyncRoot) {
+                    return mDisplayPowerController.getDefaultBrightnessConfiguration();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void setTemporaryBrightness(int brightness) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index fa39ce4..ff8b88b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -567,6 +567,10 @@
         }
     }
 
+    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+        return mAutomaticBrightnessController.getDefaultConfig();
+    }
+
     private void sendUpdatePowerState() {
         synchronized (mLock) {
             sendUpdatePowerStateLocked();
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
index 3eae157..4100a9a 100644
--- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java
+++ b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
@@ -21,6 +21,7 @@
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.media.AudioAttributes;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.VibrationEffect;
@@ -39,6 +40,11 @@
     protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h.
     protected static final boolean DEBUG = FingerprintService.DEBUG;
     private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30};
+    private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
+            new AudioAttributes.Builder()
+                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+                    .build();
     private final Context mContext;
     private final long mHalDeviceId;
     private final int mTargetUserId;
@@ -223,14 +229,14 @@
     public final void vibrateSuccess() {
         Vibrator vibrator = mContext.getSystemService(Vibrator.class);
         if (vibrator != null) {
-            vibrator.vibrate(mSuccessVibrationEffect);
+            vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
         }
     }
 
     public final void vibrateError() {
         Vibrator vibrator = mContext.getSystemService(Vibrator.class);
         if (vibrator != null) {
-            vibrator.vibrate(mErrorVibrationEffect);
+            vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
         }
     }
 
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 729ac0c..e02feec 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1721,7 +1721,9 @@
         if (mItarSpeedLimitExceeded) {
             Log.i(TAG, "Hal reported a speed in excess of ITAR limit." +
                     "  GPS/GNSS Navigation output blocked.");
-            mGnssMetrics.logReceivedLocationStatus(false);
+            if (mStarted) {
+                mGnssMetrics.logReceivedLocationStatus(false);
+            }
             return;  // No output of location allowed
         }
 
@@ -1738,14 +1740,16 @@
             Log.e(TAG, "RemoteException calling reportLocation");
         }
 
-        mGnssMetrics.logReceivedLocationStatus(hasLatLong);
-        if (hasLatLong) {
-            if (location.hasAccuracy()) {
-                mGnssMetrics.logPositionAccuracyMeters(location.getAccuracy());
-            }
-            if (mTimeToFirstFix > 0) {
-                int timeBetweenFixes = (int) (SystemClock.elapsedRealtime() - mLastFixTime);
-                mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes);
+        if (mStarted) {
+            mGnssMetrics.logReceivedLocationStatus(hasLatLong);
+            if (hasLatLong) {
+                if (location.hasAccuracy()) {
+                    mGnssMetrics.logPositionAccuracyMeters(location.getAccuracy());
+                }
+                if (mTimeToFirstFix > 0) {
+                    int timeBetweenFixes = (int) (SystemClock.elapsedRealtime() - mLastFixTime);
+                    mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes);
+                }
             }
         }
 
@@ -1754,7 +1758,9 @@
         if (mTimeToFirstFix == 0 && hasLatLong) {
             mTimeToFirstFix = (int) (mLastFixTime - mFixRequestTime);
             if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
-            mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
+            if (mStarted) {
+                mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
+            }
 
             // notify status listeners
             mListenerHelper.onFirstFix(mTimeToFirstFix);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 74ebf3e4..a87a113 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2051,11 +2051,13 @@
 
     @Override
     public byte[] startRecoverySessionWithCertPath(@NonNull String sessionId,
-            @NonNull RecoveryCertPath verifierCertPath, @NonNull byte[] vaultParams,
-            @NonNull byte[] vaultChallenge, @NonNull List<KeyChainProtectionParams> secrets)
+            @NonNull String rootCertificateAlias, @NonNull RecoveryCertPath verifierCertPath,
+            @NonNull byte[] vaultParams, @NonNull byte[] vaultChallenge,
+            @NonNull List<KeyChainProtectionParams> secrets)
             throws RemoteException {
         return mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
-                sessionId, verifierCertPath, vaultParams, vaultChallenge, secrets);
+                sessionId, rootCertificateAlias, verifierCertPath, vaultParams, vaultChallenge,
+                secrets);
     }
 
     public void closeSession(@NonNull String sessionId) throws RemoteException {
@@ -2063,11 +2065,19 @@
     }
 
     @Override
+    public Map<String, String> recoverKeyChainSnapshot(
+            @NonNull String sessionId,
+            @NonNull byte[] recoveryKeyBlob,
+            @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
+        return mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
+                sessionId, recoveryKeyBlob, applicationKeys);
+    }
+
+    @Override
     public Map<String, byte[]> recoverKeys(@NonNull String sessionId,
             @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys)
             throws RemoteException {
-        return mRecoverableKeyStoreManager.recoverKeys(
-                sessionId, recoveryKeyBlob, applicationKeys);
+        return mRecoverableKeyStoreManager.recoverKeys(sessionId, recoveryKeyBlob, applicationKeys);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 20f3403..5b10add 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -31,6 +31,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.os.Binder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
@@ -38,8 +39,10 @@
 import android.security.keystore.recovery.KeyChainSnapshot;
 import android.security.keystore.recovery.RecoveryCertPath;
 import android.security.keystore.recovery.RecoveryController;
+import android.security.keystore.recovery.TrustedRootCertificates;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.security.KeyStore;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -50,7 +53,6 @@
 import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
 import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
 import com.android.server.locksettings.recoverablekeystore.certificate.CertXml;
-import com.android.server.locksettings.recoverablekeystore.certificate.TrustedRootCert;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -64,6 +66,7 @@
 import java.security.cert.CertPath;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.X509EncodedKeySpec;
 import java.util.Arrays;
@@ -200,15 +203,19 @@
         }
         Log.i(TAG, "Updating the certificate with the new serial number " + newSerial);
 
+        // Randomly choose and validate an endpoint certificate from the list
         CertPath certPath;
+        X509Certificate rootCert = getRootCertificate(rootCertificateAlias);
         try {
             Log.d(TAG, "Getting and validating a random endpoint certificate");
-            certPath = certXml.getRandomEndpointCert(TrustedRootCert.TRUSTED_ROOT_CERT);
+            certPath = certXml.getRandomEndpointCert(rootCert);
         } catch (CertValidationException e) {
             Log.e(TAG, "Invalid endpoint cert", e);
             throw new ServiceSpecificException(
                     ERROR_INVALID_CERTIFICATE, "Failed to validate certificate.");
         }
+
+        // Save the chosen and validated certificate into database
         try {
             Log.d(TAG, "Saving the randomly chosen endpoint certificate to database");
             if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) {
@@ -253,8 +260,9 @@
                     ERROR_BAD_CERTIFICATE_FORMAT, "Failed to parse the sig file.");
         }
 
+        X509Certificate rootCert = getRootCertificate(rootCertificateAlias);
         try {
-            sigXml.verifyFileSignature(TrustedRootCert.TRUSTED_ROOT_CERT, recoveryServiceCertFile);
+            sigXml.verifyFileSignature(rootCert, recoveryServiceCertFile);
         } catch (CertValidationException e) {
             Log.d(TAG, "The signature over the cert file is invalid."
                     + " Cert: " + HexDump.toHexString(recoveryServiceCertFile)
@@ -479,6 +487,7 @@
      */
     public @NonNull byte[] startRecoverySessionWithCertPath(
             @NonNull String sessionId,
+            @NonNull String rootCertificateAlias,
             @NonNull RecoveryCertPath verifierCertPath,
             @NonNull byte[] vaultParams,
             @NonNull byte[] vaultChallenge,
@@ -495,11 +504,10 @@
         }
 
         try {
-            CertUtils.validateCertPath(TrustedRootCert.TRUSTED_ROOT_CERT, certPath);
+            CertUtils.validateCertPath(getRootCertificate(rootCertificateAlias), certPath);
         } catch (CertValidationException e) {
             Log.e(TAG, "Failed to validate the given cert path", e);
-            // TODO: Change this to ERROR_INVALID_CERTIFICATE once ag/3666620 is submitted
-            throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, e.getMessage());
+            throw new ServiceSpecificException(ERROR_INVALID_CERTIFICATE, e.getMessage());
         }
 
         byte[] verifierPublicKey = certPath.getCertificates().get(0).getPublicKey().getEncoded();
@@ -550,6 +558,78 @@
     }
 
     /**
+     * Invoked by a recovery agent after a successful recovery claim is sent to the remote vault
+     * service.
+     *
+     * @param sessionId The session ID used to generate the claim. See
+     *     {@link #startRecoverySession(String, byte[], byte[], byte[], List)}.
+     * @param encryptedRecoveryKey The encrypted recovery key blob returned by the remote vault
+     *     service.
+     * @param applicationKeys The encrypted key blobs returned by the remote vault service. These
+     *     were wrapped with the recovery key.
+     * @throws RemoteException if an error occurred recovering the keys.
+     */
+    public Map<String, String> recoverKeyChainSnapshot(
+            @NonNull String sessionId,
+            @NonNull byte[] encryptedRecoveryKey,
+            @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
+        checkRecoverKeyStorePermission();
+        int userId = UserHandle.getCallingUserId();
+        int uid = Binder.getCallingUid();
+        RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId);
+        if (sessionEntry == null) {
+            throw new ServiceSpecificException(ERROR_SESSION_EXPIRED,
+                    String.format(Locale.US,
+                            "Application uid=%d does not have pending session '%s'",
+                            uid,
+                            sessionId));
+        }
+
+        try {
+            byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey);
+            Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys);
+            return importKeyMaterials(userId, uid, keysByAlias);
+        } catch (KeyStoreException e) {
+            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+        } finally {
+            sessionEntry.destroy();
+            mRecoverySessionStorage.remove(uid);
+        }
+    }
+
+    /**
+     * Imports the key materials, returning a map from alias to grant alias for the calling user.
+     *
+     * @param userId The calling user ID.
+     * @param uid The calling uid.
+     * @param keysByAlias The key materials, keyed by alias.
+     * @throws KeyStoreException if an error occurs importing the key or getting the grant.
+     */
+    private Map<String, String> importKeyMaterials(
+            int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException {
+        ArrayMap<String, String> grantAliasesByAlias = new ArrayMap<>(keysByAlias.size());
+        for (String alias : keysByAlias.keySet()) {
+            mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keysByAlias.get(alias));
+            String grantAlias = getAlias(userId, uid, alias);
+            Log.i(TAG, String.format(Locale.US, "Import %s -> %s", alias, grantAlias));
+            grantAliasesByAlias.put(alias, grantAlias);
+        }
+        return grantAliasesByAlias;
+    }
+
+    /**
+     * Returns an alias for the key.
+     *
+     * @param userId The user ID of the calling process.
+     * @param uid The uid of the calling process.
+     * @param alias The alias of the key.
+     * @return The alias in the calling process's keystore.
+     */
+    private String getAlias(int userId, int uid, String alias) {
+        return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+    }
+
+    /**
      * Deprecated
      * Generates a key named {@code alias} in the recoverable store for the calling uid. Then
      * returns the raw key material.
@@ -626,7 +706,7 @@
             byte[] secretKey =
                     mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
             mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey);
-            return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+            return getAlias(userId, uid, alias);
         } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
         }
@@ -677,7 +757,7 @@
 
             // Import the key to Android KeyStore and get grant
             mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes);
-            return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+            return getAlias(userId, uid, alias);
         } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
         }
@@ -691,8 +771,7 @@
     public String getKey(@NonNull String alias) throws RemoteException {
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
-        String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
-        return grantAlias;
+        return getAlias(userId, uid, alias);
     }
 
     private byte[] decryptRecoveryKey(
@@ -837,6 +916,21 @@
         }
     }
 
+    private X509Certificate getRootCertificate(String rootCertificateAlias) throws RemoteException {
+        if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
+            // Use the default Google Key Vault Service CA certificate if the alias is not provided
+            rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
+        }
+
+        X509Certificate rootCertificate =
+                TrustedRootCertificates.getRootCertificate(rootCertificateAlias);
+        if (rootCertificate == null) {
+            throw new ServiceSpecificException(
+                    ERROR_INVALID_CERTIFICATE, "The provided root certificate alias is invalid");
+        }
+        return rootCertificate;
+    }
+
     private void checkRecoverKeyStorePermission() {
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.RECOVER_KEYSTORE,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java
deleted file mode 100644
index 7195d62..0000000
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.locksettings.recoverablekeystore.certificate;
-
-import java.security.cert.X509Certificate;
-
-/**
- * Holds the X509 certificate of the trusted root CA cert for the recoverable key store service.
- *
- * TODO: Read the certificate from a PEM file directly and remove this class.
- */
-public final class TrustedRootCert {
-
-    private  static final String TRUSTED_ROOT_CERT_BASE64 = ""
-            + "MIIFJjCCAw6gAwIBAgIJAIobXsJlzhNdMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV"
-            + "BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDIxOTM5MTRaFw0zODAx"
-            + "MjgxOTM5MTRaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCAiIw"
-            + "DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2OT5i40/H7LINg/lq/0G0hR65P"
-            + "Q4Mud3OnuVt6UIYV2T18+v6qW1yJd5FcnND/ZKPau4aUAYklqJuSVjOXQD0BjgS2"
-            + "98Xa4dSn8Ci1rUR+5tdmrxqbYUdT2ZvJIUMMR6fRoqi+LlAbKECrV+zYQTyLU68w"
-            + "V66hQpAButjJKiZzkXjmKLfJ5IWrNEn17XM988rk6qAQn/BYCCQGf3rQuJeksGmA"
-            + "N1lJOwNYxmWUyouVwqwZthNEWqTuEyBFMkAT+99PXW7oVDc7oU5cevuihxQWNTYq"
-            + "viGB8cck6RW3cmqrDSaJF/E+N0cXFKyYC7FDcggt6k3UrxNKTuySdDEa8+2RTQqU"
-            + "Y9npxBlQE+x9Ig56OI1BG3bSBsGdPgjpyHadZeh2tgk+oqlGsSsum24YxaxuSysT"
-            + "Qfcu/XhyfUXavfmGrBOXerTzIl5oBh/F5aHTV85M2tYEG0qsPPvSpZAWtdJ/2rca"
-            + "OxvhwOL+leZKr8McjXVR00lBsRuKXX4nTUMwya09CO3QHFPFZtZvqjy2HaMOnVLQ"
-            + "I6b6dHEfmsHybzVOe3yPEoFQSU9UhUdmi71kwwoanPD3j9fJHmXTx4PzYYBRf1ZE"
-            + "o+uPgMPk7CDKQFZLjnR40z1uzu3O8aZ3AKZzP+j7T4XQKJLQLmllKtPgLgNdJyib"
-            + "2Glg7QhXH/jBTL6hAgMBAAGjYzBhMB0GA1UdDgQWBBSbZfrqOYH54EJpkdKMZjMc"
-            + "z/Hp+DAfBgNVHSMEGDAWgBSbZfrqOYH54EJpkdKMZjMcz/Hp+DAPBgNVHRMBAf8E"
-            + "BTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQ0FAAOCAgEAKh9nm/vW"
-            + "glMWp3vcCwWwJW286ecREDlI+CjGh5h+f2N4QRrXd/tKE3qQJWCqGx8sFfIUjmI7"
-            + "KYdsC2gyQ2cA2zl0w7pB2QkuqE6zVbnh1D17Hwl19IMyAakFaM9ad4/EoH7oQmqX"
-            + "nF/f5QXGZw4kf1HcgKgoCHWXjqR8MqHOcXR8n6WFqxjzJf1jxzi6Yo2dZ7PJbnE6"
-            + "+kHIJuiCpiHL75v5g1HM41gT3ddFFSrn88ThNPWItT5Z8WpFjryVzank2Yt02LLl"
-            + "WqZg9IC375QULc5B58NMnaiVJIDJQ8zoNgj1yaxqtUMnJX570lotO2OXe4ec9aCQ"
-            + "DIJ84YLM/qStFdeZ9416E80dchskbDG04GuVJKlzWjxAQNMRFhyaPUSBTLLg+kwP"
-            + "t9+AMmc+A7xjtFQLZ9fBYHOBsndJOmeSQeYeckl+z/1WQf7DdwXn/yijon7mxz4z"
-            + "cCczfKwTJTwBh3wR5SQr2vQm7qaXM87qxF8PCAZrdZaw5I80QwkgTj0WTZ2/GdSw"
-            + "d3o5SyzzBAjpwtG+4bO/BD9h9wlTsHpT6yWOZs4OYAKU5ykQrncI8OyavMggArh3"
-            + "/oM58v0orUWINtIc2hBlka36PhATYQiLf+AiWKnwhCaaHExoYKfQlMtXBodNvOK8"
-            + "xqx69x05q/qbHKEcTHrsss630vxrp1niXvA=";
-
-    /**
-     * The X509 certificate of the trusted root CA cert for the recoverable key store service.
-     *
-     * TODO: Change it to the production certificate root CA before the final launch.
-     */
-    public static final X509Certificate TRUSTED_ROOT_CERT;
-
-    static {
-        try {
-            TRUSTED_ROOT_CERT = CertUtils.decodeCert(
-                    CertUtils.decodeBase64(TRUSTED_ROOT_CERT_BASE64));
-        } catch (CertParsingException e) {
-            // Should not happen
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
index 3d97623..84ddbf7 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
@@ -24,6 +24,7 @@
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
 import android.security.KeyStore;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.locksettings.recoverablekeystore.KeyStoreProxy;
@@ -31,6 +32,8 @@
 
 import java.security.KeyStore.SecretKeyEntry;
 import java.security.KeyStoreException;
+import java.util.Locale;
+
 import javax.crypto.spec.SecretKeySpec;
 
 /**
@@ -40,11 +43,13 @@
  * revealing key material
  */
 public class ApplicationKeyStorage {
+    private static final String TAG = "RecoverableAppKeyStore";
+
     private static final String APPLICATION_KEY_ALIAS_PREFIX =
             "com.android.server.locksettings.recoverablekeystore/application/";
 
-    KeyStoreProxy mKeyStore;
-    KeyStore mKeystoreService;
+    private final KeyStoreProxy mKeyStore;
+    private final KeyStore mKeystoreService;
 
     public static ApplicationKeyStorage getInstance(KeyStore keystoreService)
             throws KeyStoreException {
@@ -65,12 +70,15 @@
     public @Nullable String getGrantAlias(int userId, int uid, String alias) {
         // Aliases used by {@link KeyStore} are different than used by public API.
         // {@code USER_PRIVATE_KEY} prefix is used secret keys.
+        Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias));
         String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias);
         return mKeystoreService.grant(keystoreAlias, uid);
     }
 
     public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
             throws KeyStoreException {
+        Log.i(TAG, String.format(Locale.US, "Set %d/%d/%s: %d bytes of key material",
+                userId, uid, alias, secretKey.length));
         try {
             mKeyStore.setEntry(
                 getInternalAlias(userId, uid, alias),
@@ -87,6 +95,7 @@
     }
 
     public void deleteEntry(int userId, int uid, String alias) {
+        Log.i(TAG, String.format(Locale.US, "Del %d/%d/%s", userId, uid, alias));
         try {
             mKeyStore.deleteEntry(getInternalAlias(userId, uid, alias));
         } catch (KeyStoreException e) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 8983ec3..bda2ed3 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -175,7 +175,7 @@
         /**
          * The algorithm used to derive cryptographic material from the key and salt. One of
          * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SHA256} or
-         * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_ARGON2ID}.
+         * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SCRYPT}.
          */
         static final String COLUMN_NAME_KEY_DERIVATION_ALGORITHM = "key_derivation_algorithm";
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1db373d..27eeb93 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -959,6 +959,8 @@
             boolean queryRemove = false;
             boolean packageChanged = false;
             boolean cancelNotifications = true;
+            boolean hideNotifications = false;
+            boolean unhideNotifications = false;
             int reason = REASON_PACKAGE_CHANGED;
 
             if (action.equals(Intent.ACTION_PACKAGE_ADDED)
@@ -967,7 +969,8 @@
                     || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
                     || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
-                    || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
+                    || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)
+                    || action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)) {
                 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
                         UserHandle.USER_ALL);
                 String pkgList[] = null;
@@ -980,7 +983,12 @@
                     uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                    reason = REASON_PACKAGE_SUSPENDED;
+                    cancelNotifications = false;
+                    hideNotifications = true;
+                } else if (action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                    cancelNotifications = false;
+                    unhideNotifications = true;
                 } else if (queryRestart) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
                     uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
@@ -1022,9 +1030,15 @@
                         if (cancelNotifications) {
                             cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
                                     !queryRestart, changeUserId, reason, null);
+                        } else if (hideNotifications) {
+                            hideNotificationsForPackages(pkgList);
+                        } else if (unhideNotifications) {
+                            unhideNotificationsForPackages(pkgList);
                         }
+
                     }
                 }
+
                 mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
                 mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
                 mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
@@ -1294,7 +1308,8 @@
             NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
             ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
             NotificationUsageStats usageStats, AtomicFile policyFile,
-            ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am) {
+            ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
+            UsageStatsManagerInternal appUsageStats) {
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
                 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1307,7 +1322,7 @@
         mPackageManagerClient = packageManagerClient;
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
         mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
-        mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+        mAppUsageStats = appUsageStats;
         mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
         mCompanionManager = companionManager;
         mActivityManager = activityManager;
@@ -1449,7 +1464,8 @@
                 null, snoozeHelper, new NotificationUsageStats(getContext()),
                 new AtomicFile(new File(systemDir, "notification_policy.xml"), "notification-policy"),
                 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
-                getGroupHelper(), ActivityManager.getService());
+                getGroupHelper(), ActivityManager.getService(),
+                LocalServices.getService(UsageStatsManagerInternal.class));
 
         // register for various Intents
         IntentFilter filter = new IntentFilter();
@@ -1477,6 +1493,7 @@
 
         IntentFilter suspendedPkgFilter = new IntentFilter();
         suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
+        suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
         getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
                 suspendedPkgFilter, null, null);
 
@@ -2484,6 +2501,7 @@
             try {
                 synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+
                     if (keys != null) {
                         final int N = keys.length;
                         for (int i = 0; i < N; i++) {
@@ -4269,6 +4287,14 @@
         }
     }
 
+    @GuardedBy("mNotificationLock")
+    private boolean isPackageSuspendedLocked(NotificationRecord r) {
+        final String pkg = r.sbn.getPackageName();
+        final int callingUid = r.sbn.getUid();
+
+        return isPackageSuspendedForUser(pkg, callingUid);
+    }
+
     protected class PostNotificationRunnable implements Runnable {
         private final String key;
 
@@ -4293,6 +4319,8 @@
                         Slog.i(TAG, "Cannot find enqueued record for key: " + key);
                         return;
                     }
+
+                    r.setHidden(isPackageSuspendedLocked(r));
                     NotificationRecord old = mNotificationsByKey.get(key);
                     final StatusBarNotification n = r.sbn;
                     final Notification notification = n.getNotification();
@@ -4300,6 +4328,7 @@
                     if (index < 0) {
                         mNotificationList.add(r);
                         mUsageStats.registerPostedByApp(r);
+                        r.setInterruptive(true);
                     } else {
                         old = mNotificationList.get(index);
                         mNotificationList.set(index, r);
@@ -4310,6 +4339,7 @@
                         // revoke uri permissions for changed uris
                         revokeUriPermissions(r, old);
                         r.isUpdate = true;
+                        r.setInterruptive(isVisuallyInterruptive(old, r));
                     }
 
                     mNotificationsByKey.put(n.getKey(), r);
@@ -4343,7 +4373,7 @@
                     } else {
                         Slog.e(TAG, "Not posting notification without small icon: " + notification);
                         if (old != null && !old.isCanceled) {
-                            mListeners.notifyRemovedLocked(n,
+                            mListeners.notifyRemovedLocked(r,
                                     NotificationListenerService.REASON_ERROR, null);
                             mHandler.post(new Runnable() {
                                 @Override
@@ -4359,7 +4389,9 @@
                                 + n.getPackageName());
                     }
 
-                    buzzBeepBlinkLocked(r);
+                    if (!r.isHidden()) {
+                        buzzBeepBlinkLocked(r);
+                    }
                     maybeRecordInterruptionLocked(r);
                 } finally {
                     int N = mEnqueuedNotifications.size();
@@ -4376,6 +4408,52 @@
     }
 
     /**
+     * If the notification differs enough visually, consider it a new interruptive notification.
+     */
+    @GuardedBy("mNotificationLock")
+    @VisibleForTesting
+    protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
+        Notification oldN = old.sbn.getNotification();
+        Notification newN = r.sbn.getNotification();
+        if (oldN.extras == null || newN.extras == null) {
+            return false;
+        }
+        if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TITLE),
+                newN.extras.get(Notification.EXTRA_TITLE))) {
+            return true;
+        }
+        if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TEXT),
+                newN.extras.get(Notification.EXTRA_TEXT))) {
+            return true;
+        }
+        if (oldN.extras.containsKey(Notification.EXTRA_PROGRESS) && newN.hasCompletedProgress()) {
+            return true;
+        }
+        // Actions
+        if (Notification.areActionsVisiblyDifferent(oldN, newN)) {
+            return true;
+        }
+
+        try {
+            Notification.Builder oldB = Notification.Builder.recoverBuilder(getContext(), oldN);
+            Notification.Builder newB = Notification.Builder.recoverBuilder(getContext(), newN);
+
+            // Style based comparisons
+            if (Notification.areStyledNotificationsVisiblyDifferent(oldB, newB)) {
+                return true;
+            }
+
+            // Remote views
+            if (Notification.areRemoteViewsChanged(oldB, newB)) {
+                return true;
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "error recovering builder", e);
+        }
+        return false;
+    }
+
+    /**
      * Keeps the last 5 packages that have notified, by user.
      */
     @GuardedBy("mNotificationLock")
@@ -4972,7 +5050,7 @@
 
     private void handleSendRankingUpdate() {
         synchronized (mNotificationLock) {
-            mListeners.notifyRankingUpdateLocked();
+            mListeners.notifyRankingUpdateLocked(null);
         }
     }
 
@@ -5157,7 +5235,7 @@
                 if (reason != REASON_SNOOZED) {
                     r.isCanceled = true;
                 }
-                mListeners.notifyRemovedLocked(r.sbn, reason, r.getStats());
+                mListeners.notifyRemovedLocked(r, reason, r.getStats());
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
@@ -5266,6 +5344,7 @@
             final String pkg, final String tag, final int id,
             final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
             final int userId, final int reason, final ManagedServiceInfo listener) {
+
         // In enqueueNotificationInternal notifications are added by scheduling the
         // work on the worker handler. Hence, we also schedule the cancel on this
         // handler to avoid a scenario where an add notification call followed by a
@@ -5657,6 +5736,42 @@
         return -1;
     }
 
+    @VisibleForTesting
+    protected void hideNotificationsForPackages(String[] pkgs) {
+        synchronized (mNotificationLock) {
+            List<String> pkgList = Arrays.asList(pkgs);
+            List<NotificationRecord> changedNotifications = new ArrayList<>();
+            int numNotifications = mNotificationList.size();
+            for (int i = 0; i < numNotifications; i++) {
+                NotificationRecord rec = mNotificationList.get(i);
+                if (pkgList.contains(rec.sbn.getPackageName())) {
+                    rec.setHidden(true);
+                    changedNotifications.add(rec);
+                }
+            }
+
+            mListeners.notifyHiddenLocked(changedNotifications);
+        }
+    }
+
+    @VisibleForTesting
+    protected void unhideNotificationsForPackages(String[] pkgs) {
+        synchronized (mNotificationLock) {
+            List<String> pkgList = Arrays.asList(pkgs);
+            List<NotificationRecord> changedNotifications = new ArrayList<>();
+            int numNotifications = mNotificationList.size();
+            for (int i = 0; i < numNotifications; i++) {
+                NotificationRecord rec = mNotificationList.get(i);
+                if (pkgList.contains(rec.sbn.getPackageName())) {
+                    rec.setHidden(false);
+                    changedNotifications.add(rec);
+                }
+            }
+
+            mListeners.notifyUnhiddenLocked(changedNotifications);
+        }
+    }
+
     private void updateNotificationPulse() {
         synchronized (mNotificationLock) {
             updateLightsLocked();
@@ -5776,6 +5891,7 @@
         Bundle snoozeCriteria = new Bundle();
         Bundle showBadge = new Bundle();
         Bundle userSentiment = new Bundle();
+        Bundle hidden = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
             if (!isVisibleToListener(record.sbn, info)) {
@@ -5802,6 +5918,7 @@
             snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
             showBadge.putBoolean(key, record.canShowBadge());
             userSentiment.putInt(key, record.getUserSentiment());
+            hidden.putBoolean(key, record.isHidden());
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -5812,7 +5929,7 @@
         }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
                 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
-                channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
+                channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden);
     }
 
     boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -6101,6 +6218,16 @@
          */
         @GuardedBy("mNotificationLock")
         public void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn) {
+            notifyPostedLocked(r, oldSbn, true);
+        }
+
+        /**
+         * @param notifyAllListeners notifies all listeners if true, else only notifies listeners
+         *                           targetting <= O_MR1
+         */
+        @GuardedBy("mNotificationLock")
+        private void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn,
+                boolean notifyAllListeners) {
             // Lazily initialized snapshots of the notification.
             StatusBarNotification sbn = r.sbn;
             TrimCache trimCache = new TrimCache(sbn);
@@ -6114,6 +6241,21 @@
                 if (!oldSbnVisible && !sbnVisible) {
                     continue;
                 }
+
+                // If the notification is hidden, don't notifyPosted listeners targeting < P.
+                // Instead, those listeners will receive notifyPosted when the notification is
+                // unhidden.
+                if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
+                    continue;
+                }
+
+                // If we shouldn't notify all listeners, this means the hidden state of
+                // a notification was changed.  Don't notifyPosted listeners targeting >= P.
+                // Instead, those listeners will receive notifyRankingUpdate.
+                if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
+                    continue;
+                }
+
                 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
 
                 // This notification became invisible -> remove the old one.
@@ -6168,8 +6310,9 @@
          * asynchronously notify all listeners about a removed notification
          */
         @GuardedBy("mNotificationLock")
-        public void notifyRemovedLocked(StatusBarNotification sbn, int reason,
+        public void notifyRemovedLocked(NotificationRecord r, int reason,
                 NotificationStats notificationStats) {
+            final StatusBarNotification sbn = r.sbn;
             // make a copy in case changes are made to the underlying Notification object
             // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
             // notification
@@ -6178,6 +6321,21 @@
                 if (!isVisibleToListener(sbn, info)) {
                     continue;
                 }
+
+                // don't notifyRemoved for listeners targeting < P
+                // if not for reason package suspended
+                if (r.isHidden() && reason != REASON_PACKAGE_SUSPENDED
+                        && info.targetSdkVersion < Build.VERSION_CODES.P) {
+                    continue;
+                }
+
+                // don't notifyRemoved for listeners targeting >= P
+                // if the reason is package suspended
+                if (reason == REASON_PACKAGE_SUSPENDED
+                        && info.targetSdkVersion >= Build.VERSION_CODES.P) {
+                    continue;
+                }
+
                 // Only assistants can get stats
                 final NotificationStats stats = mAssistants.isServiceTokenValidLocked(info.service)
                         ? notificationStats : null;
@@ -6192,21 +6350,44 @@
         }
 
         /**
-         * asynchronously notify all listeners about a reordering of notifications
+         * Asynchronously notify all listeners about a reordering of notifications
+         * unless changedHiddenNotifications is populated.
+         * If changedHiddenNotifications is populated, there was a change in the hidden state
+         * of the notifications.  In this case, we only send updates to listeners that
+         * target >= P.
          */
         @GuardedBy("mNotificationLock")
-        public void notifyRankingUpdateLocked() {
+        public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
+            boolean isHiddenRankingUpdate = changedHiddenNotifications != null
+                    && changedHiddenNotifications.size() > 0;
+
             for (final ManagedServiceInfo serviceInfo : getServices()) {
                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
                     continue;
                 }
-                final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        notifyRankingUpdate(serviceInfo, update);
+
+                boolean notifyThisListener = false;
+                if (isHiddenRankingUpdate && serviceInfo.targetSdkVersion >=
+                        Build.VERSION_CODES.P) {
+                    for (NotificationRecord rec : changedHiddenNotifications) {
+                        if (isVisibleToListener(rec.sbn, serviceInfo)) {
+                            notifyThisListener = true;
+                            break;
+                        }
                     }
-                });
+                }
+
+                if (notifyThisListener || !isHiddenRankingUpdate) {
+                    final NotificationRankingUpdate update = makeRankingUpdateLocked(
+                            serviceInfo);
+
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            notifyRankingUpdate(serviceInfo, update);
+                        }
+                    });
+                }
             }
         }
 
@@ -6225,6 +6406,52 @@
             }
         }
 
+        /**
+         * asynchronously notify relevant listeners their notification is hidden
+         * NotificationListenerServices that target P+:
+         *      NotificationListenerService#notifyRankingUpdateLocked()
+         * NotificationListenerServices that target <= P:
+         *      NotificationListenerService#notifyRemovedLocked() with REASON_PACKAGE_SUSPENDED.
+         */
+        @GuardedBy("mNotificationLock")
+        public void notifyHiddenLocked(List<NotificationRecord> changedNotifications) {
+            if (changedNotifications == null || changedNotifications.size() == 0) {
+                return;
+            }
+
+            notifyRankingUpdateLocked(changedNotifications);
+
+            // for listeners that target < P, notifyRemoveLocked
+            int numChangedNotifications = changedNotifications.size();
+            for (int i = 0; i < numChangedNotifications; i++) {
+                NotificationRecord rec = changedNotifications.get(i);
+                mListeners.notifyRemovedLocked(rec, REASON_PACKAGE_SUSPENDED, rec.getStats());
+            }
+        }
+
+        /**
+         * asynchronously notify relevant listeners their notification is unhidden
+         * NotificationListenerServices that target P+:
+         *      NotificationListenerService#notifyRankingUpdateLocked()
+         * NotificationListenerServices that target <= P:
+         *      NotificationListeners#notifyPostedLocked()
+         */
+        @GuardedBy("mNotificationLock")
+        public void notifyUnhiddenLocked(List<NotificationRecord> changedNotifications) {
+            if (changedNotifications == null || changedNotifications.size() == 0) {
+                return;
+            }
+
+            notifyRankingUpdateLocked(changedNotifications);
+
+            // for listeners that target < P, notifyPostedLocked
+            int numChangedNotifications = changedNotifications.size();
+            for (int i = 0; i < numChangedNotifications; i++) {
+                NotificationRecord rec = changedNotifications.get(i);
+                mListeners.notifyPostedLocked(rec, rec.sbn, false);
+            }
+        }
+
         public void notifyInterruptionFilterChanged(final int interruptionFilter) {
             for (final ManagedServiceInfo serviceInfo : getServices()) {
                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
@@ -6453,6 +6680,22 @@
         }
     }
 
+    @VisibleForTesting
+    protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) {
+        // only use for testing: mimic receive broadcast that package is (un)suspended
+        // but does not actually (un)suspend the package
+        final Bundle extras = new Bundle();
+        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
+                new String[]{pkg});
+
+        final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED
+            : Intent.ACTION_PACKAGES_UNSUSPENDED;
+        final Intent intent = new Intent(action);
+        intent.putExtras(extras);
+
+        mPackageIntentReceiver.onReceive(getContext(), intent);
+    }
+
     /**
      * Wrapper for a StatusBarNotification object that allows transfer across a oneway
      * binder without sending large amounts of data over a oneway transaction.
@@ -6481,7 +6724,9 @@
                 + "allow_assistant COMPONENT\n"
                 + "remove_assistant COMPONENT\n"
                 + "allow_dnd PACKAGE\n"
-                + "disallow_dnd PACKAGE";
+                + "disallow_dnd PACKAGE\n"
+                + "suspend_package PACKAGE\n"
+                + "unsuspend_package PACKAGE";
 
         @Override
         public int onCommand(String cmd) {
@@ -6550,7 +6795,16 @@
                         getBinderService().setNotificationAssistantAccessGranted(cn, false);
                     }
                     break;
-
+                    case "suspend_package": {
+                        // only use for testing
+                        simulatePackageSuspendBroadcast(true, getNextArgRequired());
+                    }
+                    break;
+                    case "unsuspend_package": {
+                        // only use for testing
+                        simulatePackageSuspendBroadcast(false, getNextArgRequired());
+                    }
+                    break;
                     default:
                         return handleDefaultCommands(cmd);
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f1908bf..c887085 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -103,6 +103,9 @@
     // is this notification currently being intercepted by Zen Mode?
     private boolean mIntercept;
 
+    // is this notification hidden since the app pkg is suspended?
+    private boolean mHidden;
+
     // The timestamp used for ranking.
     private long mRankingTimeMs;
 
@@ -353,6 +356,7 @@
         mPackagePriority = previous.mPackagePriority;
         mPackageVisibility = previous.mPackageVisibility;
         mIntercept = previous.mIntercept;
+        mHidden = previous.mHidden;
         mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
         mCreationTimeMs = previous.mCreationTimeMs;
         mVisibleSinceMs = previous.mVisibleSinceMs;
@@ -498,6 +502,7 @@
                 + NotificationListenerService.Ranking.importanceToString(mImportance));
         pw.println(prefix + "mImportanceExplanation=" + mImportanceExplanation);
         pw.println(prefix + "mIntercept=" + mIntercept);
+        pw.println(prefix + "mHidden==" + mHidden);
         pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
         pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
         pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
@@ -702,6 +707,15 @@
         return mIntercept;
     }
 
+    public void setHidden(boolean hidden) {
+        mHidden = hidden;
+    }
+
+    public boolean isHidden() {
+        return mHidden;
+    }
+
+
     public void setSuppressedVisualEffects(int effects) {
         mSuppressedVisualEffects = effects;
     }
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index bf85f30..83fe1c9 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -55,6 +55,7 @@
 import android.security.Credentials;
 import android.service.textclassifier.TextClassifierService;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -829,13 +830,11 @@
         }
 
         // TextClassifier Service
-        ComponentName textClassifierComponent =
-                TextClassifierService.getServiceComponentName(mContext);
-        if (textClassifierComponent != null) {
-            Intent textClassifierServiceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
-                    .setComponent(textClassifierComponent);
+        String textClassifierPackageName =
+                mContext.getPackageManager().getSystemTextClassifierPackageName();
+        if (!TextUtils.isEmpty(textClassifierPackageName)) {
             PackageParser.Package textClassifierPackage =
-                    getDefaultSystemHandlerServicePackage(textClassifierServiceIntent, userId);
+                    getSystemPackage(textClassifierPackageName);
             if (textClassifierPackage != null
                     && doesPackageSupportRuntimePermissions(textClassifierPackage)) {
                 grantRuntimePermissions(textClassifierPackage, PHONE_PERMISSIONS, true, userId);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6cd60e6a..61b1eb4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -999,7 +999,7 @@
             resolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.POLICY_CONTROL), false, this,
                     UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED), false, this,
                     UserHandle.USER_ALL);
             updateSettings();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 055e6ea..adbf7ed 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -69,7 +69,6 @@
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
 import android.util.KeyValueListParser;
-import android.util.MathUtils;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -408,7 +407,7 @@
     private boolean mDreamsActivateOnDockSetting;
 
     // True if doze should not be started until after the screen off transition.
-    private boolean mDozeAfterScreenOffConfig;
+    private boolean mDozeAfterScreenOff;
 
     // The minimum screen off timeout, in milliseconds.
     private long mMinimumScreenOffTimeoutConfig;
@@ -896,7 +895,7 @@
                 com.android.internal.R.integer.config_dreamsBatteryLevelMinimumWhenNotPowered);
         mDreamsBatteryLevelDrainCutoffConfig = resources.getInteger(
                 com.android.internal.R.integer.config_dreamsBatteryLevelDrainCutoff);
-        mDozeAfterScreenOffConfig = resources.getBoolean(
+        mDozeAfterScreenOff = resources.getBoolean(
                 com.android.internal.R.bool.config_dozeAfterScreenOff);
         mMinimumScreenOffTimeoutConfig = resources.getInteger(
                 com.android.internal.R.integer.config_minimumScreenOffTimeout);
@@ -1724,7 +1723,7 @@
 
                 // Update wireless dock detection state.
                 final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(
-                        mIsPowered, mPlugType, mBatteryLevel);
+                        mIsPowered, mPlugType);
 
                 // Treat plugging and unplugging the devices as a user activity.
                 // Users find it disconcerting when they plug or unplug the device
@@ -2507,7 +2506,7 @@
             if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
                 return DisplayPowerRequest.POLICY_DOZE;
             }
-            if (mDozeAfterScreenOffConfig) {
+            if (mDozeAfterScreenOff) {
                 return DisplayPowerRequest.POLICY_OFF;
             }
             // Fall through and preserve the current screen policy if not configured to
@@ -3094,6 +3093,12 @@
         light.setFlashing(color, Light.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
     }
 
+    private void setDozeAfterScreenOffInternal(boolean on) {
+        synchronized (mLock) {
+            mDozeAfterScreenOff = on;
+        }
+    }
+
     private void boostScreenBrightnessInternal(long eventTime, int uid) {
         synchronized (mLock) {
             if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP
@@ -3372,7 +3377,7 @@
             pw.println("  mDreamsEnabledSetting=" + mDreamsEnabledSetting);
             pw.println("  mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
             pw.println("  mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
-            pw.println("  mDozeAfterScreenOffConfig=" + mDozeAfterScreenOffConfig);
+            pw.println("  mDozeAfterScreenOff=" + mDozeAfterScreenOff);
             pw.println("  mLowPowerModeSetting=" + mLowPowerModeSetting);
             pw.println("  mAutoLowPowerModeConfigured=" + mAutoLowPowerModeConfigured);
             pw.println("  mAutoLowPowerModeSnoozing=" + mAutoLowPowerModeSnoozing);
@@ -3656,7 +3661,7 @@
                     mDreamsActivateOnDockSetting);
             proto.write(
                     PowerServiceSettingsAndConfigurationDumpProto.IS_DOZE_AFTER_SCREEN_OFF_CONFIG,
-                    mDozeAfterScreenOffConfig);
+                    mDozeAfterScreenOff);
             proto.write(
                     PowerServiceSettingsAndConfigurationDumpProto.IS_LOW_POWER_MODE_SETTING,
                     mLowPowerModeSetting);
@@ -4603,6 +4608,19 @@
         }
 
         @Override // Binder call
+        public void setDozeAfterScreenOff(boolean on) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, null);
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                setDozeAfterScreenOffInternal(on);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
         public void boostScreenBrightness(long eventTime) {
             if (eventTime > SystemClock.uptimeMillis()) {
                 throw new IllegalArgumentException("event time must not be in the future");
diff --git a/services/core/java/com/android/server/power/WirelessChargerDetector.java b/services/core/java/com/android/server/power/WirelessChargerDetector.java
index 54487e3..18e5ce4 100644
--- a/services/core/java/com/android/server/power/WirelessChargerDetector.java
+++ b/services/core/java/com/android/server/power/WirelessChargerDetector.java
@@ -84,10 +84,6 @@
     // The minimum number of samples that must be collected.
     private static final int MIN_SAMPLES = 3;
 
-    // Upper bound on the battery charge percentage in order to consider turning
-    // the screen on when the device starts charging wirelessly.
-    private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95;
-
     // To detect movement, we compute the angle between the gravity vector
     // at rest and the current gravity vector.  This field specifies the
     // cosine of the maximum angle variance that we tolerate while at rest.
@@ -214,11 +210,10 @@
      *
      * @param isPowered True if the device is powered.
      * @param plugType The current plug type.
-     * @param batteryLevel The current battery level.
      * @return True if the device is determined to have just been docked on a wireless
      * charger, after suppressing spurious docking or undocking signals.
      */
-    public boolean update(boolean isPowered, int plugType, int batteryLevel) {
+    public boolean update(boolean isPowered, int plugType) {
         synchronized (mLock) {
             final boolean wasPoweredWirelessly = mPoweredWirelessly;
 
@@ -249,13 +244,9 @@
             }
 
             // Report that the device has been docked only if the device just started
-            // receiving power wirelessly, has a high enough battery level that we
-            // can be assured that charging was not delayed due to the battery previously
-            // having been full, and the device is not known to already be at rest
+            // receiving power wirelessly and the device is not known to already be at rest
             // on the wireless charger from earlier.
-            return mPoweredWirelessly && !wasPoweredWirelessly
-                    && batteryLevel < WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT
-                    && !mAtRest;
+            return mPoweredWirelessly && !wasPoweredWirelessly && !mAtRest;
         }
     }
 
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index a7dfd35..0b7d9d0 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.slice;
 
+import static android.app.usage.UsageEvents.Event.SLICE_PINNED;
+import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;
 import static android.content.ContentProvider.getUriWithoutUserId;
 import static android.content.ContentProvider.getUserIdFromUri;
 import static android.content.ContentProvider.maybeAddUserId;
@@ -31,6 +33,7 @@
 import android.app.slice.ISliceManager;
 import android.app.slice.SliceManager;
 import android.app.slice.SliceSpec;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -95,6 +98,7 @@
     private final AtomicFile mSliceAccessFile;
     @GuardedBy("mAccessList")
     private final SliceFullAccessList mAccessList;
+    private final UsageStatsManagerInternal mAppUsageStats;
 
     public SliceManagerService(Context context) {
         this(context, createHandler().getLooper());
@@ -112,6 +116,7 @@
         final File systemDir = new File(Environment.getDataDirectory(), "system");
         mSliceAccessFile = new AtomicFile(new File(systemDir, "slice_access.xml"));
         mAccessList = new SliceFullAccessList(mContext);
+        mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
 
         synchronized (mSliceAccessFile) {
             if (!mSliceAccessFile.exists()) return;
@@ -166,8 +171,19 @@
     public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) throws RemoteException {
         verifyCaller(pkg);
         enforceAccess(pkg, uri);
-        uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
+        int user = Binder.getCallingUserHandle().getIdentifier();
+        uri = maybeAddUserId(uri, user);
         getOrCreatePinnedSlice(uri, pkg).pin(pkg, specs, token);
+
+        Uri finalUri = uri;
+        mHandler.post(() -> {
+            String slicePkg = getProviderPkg(finalUri, user);
+            if (slicePkg != null && !Objects.equals(pkg, slicePkg)) {
+                mAppUsageStats.reportEvent(slicePkg, user,
+                        isAssistant(pkg, user) || isDefaultHomeApp(pkg, user)
+                                ? SLICE_PINNED_PRIV : SLICE_PINNED);
+            }
+        });
     }
 
     @Override
@@ -352,38 +368,45 @@
             if (getContext().checkUriPermission(uri, pid, uid,
                     Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) {
                 // Last fallback (if the calling app owns the authority, then it can have access).
-                long ident = Binder.clearCallingIdentity();
-                try {
-                    IBinder token = new Binder();
-                    IActivityManager activityManager = ActivityManager.getService();
-                    ContentProviderHolder holder = null;
-                    String providerName = getUriWithoutUserId(uri).getAuthority();
-                    try {
-                        try {
-                            holder = activityManager.getContentProviderExternal(
-                                    providerName, getUserIdFromUri(uri, user), token);
-                            if (holder == null || holder.info == null
-                                    || !Objects.equals(holder.info.packageName, pkg)) {
-                                return PERMISSION_DENIED;
-                            }
-                        } finally {
-                            if (holder != null && holder.provider != null) {
-                                activityManager.removeContentProviderExternal(providerName, token);
-                            }
-                        }
-                    } catch (RemoteException e) {
-                        // Can't happen.
-                        e.rethrowAsRuntimeException();
-                    }
-                } finally {
-                    // I know, the double finally seems ugly, but seems safest for the identity.
-                    Binder.restoreCallingIdentity(ident);
+                if (!Objects.equals(getProviderPkg(uri, user), pkg)) {
+                    return PERMISSION_DENIED;
                 }
             }
         }
         return PERMISSION_GRANTED;
     }
 
+    private String getProviderPkg(Uri uri, int user) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            IBinder token = new Binder();
+            IActivityManager activityManager = ActivityManager.getService();
+            ContentProviderHolder holder = null;
+            String providerName = getUriWithoutUserId(uri).getAuthority();
+            try {
+                try {
+                    holder = activityManager.getContentProviderExternal(
+                            providerName, getUserIdFromUri(uri, user), token);
+                    if (holder != null && holder.info != null) {
+                        return holder.info.packageName;
+                    } else {
+                        return null;
+                    }
+                } finally {
+                    if (holder != null && holder.provider != null) {
+                        activityManager.removeContentProviderExternal(providerName, token);
+                    }
+                }
+            } catch (RemoteException e) {
+                // Can't happen.
+                throw e.rethrowAsRuntimeException();
+            }
+        } finally {
+            // I know, the double finally seems ugly, but seems safest for the identity.
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     private void enforceCrossUser(String pkg, Uri uri) {
         int user = Binder.getCallingUserHandle().getIdentifier();
         if (getUserIdFromUri(uri, user) != user) {
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 6053512..6df7092 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -16,7 +16,9 @@
 
 package com.android.server.textclassifier;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -25,12 +27,14 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.util.Slog;
-import android.service.textclassifier.ITextClassifierService;
+import android.os.UserHandle;
 import android.service.textclassifier.ITextClassificationCallback;
+import android.service.textclassifier.ITextClassifierService;
 import android.service.textclassifier.ITextLinksCallback;
 import android.service.textclassifier.ITextSelectionCallback;
 import android.service.textclassifier.TextClassifierService;
+import android.util.Slog;
+import android.util.SparseArray;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassifier;
@@ -38,12 +42,13 @@
 import android.view.textclassifier.TextSelection;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FunctionalUtils;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
 
-import java.util.LinkedList;
+import java.util.ArrayDeque;
 import java.util.Queue;
-import java.util.concurrent.Callable;
 
 /**
  * A manager for TextClassifier services.
@@ -73,58 +78,44 @@
                 Slog.e(LOG_TAG, "Could not start the TextClassificationManagerService.", t);
             }
         }
+
+        @Override
+        public void onStartUser(int userId) {
+            processAnyPendingWork(userId);
+        }
+
+        @Override
+        public void onUnlockUser(int userId) {
+            // Rebind if we failed earlier due to locked encrypted user
+            processAnyPendingWork(userId);
+        }
+
+        private void processAnyPendingWork(int userId) {
+            synchronized (mManagerService.mLock) {
+                mManagerService.getUserStateLocked(userId).bindIfHasPendingRequestsLocked();
+            }
+        }
+
+        @Override
+        public void onStopUser(int userId) {
+            synchronized (mManagerService.mLock) {
+                UserState userState = mManagerService.peekUserStateLocked(userId);
+                if (userState != null) {
+                    userState.mConnection.cleanupService();
+                    mManagerService.mUserStates.remove(userId);
+                }
+            }
+        }
+
     }
 
     private final Context mContext;
-    private final Intent mServiceIntent;
-    private final ServiceConnection mConnection;
     private final Object mLock;
     @GuardedBy("mLock")
-    private final Queue<PendingRequest> mPendingRequests;
-
-    @GuardedBy("mLock")
-    private ITextClassifierService mService;
-    @GuardedBy("mLock")
-    private boolean mBinding;
+    final SparseArray<UserState> mUserStates = new SparseArray<>();
 
     private TextClassificationManagerService(Context context) {
         mContext = Preconditions.checkNotNull(context);
-        mServiceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
-                .setComponent(TextClassifierService.getServiceComponentName(mContext));
-        mConnection = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                synchronized (mLock) {
-                    mService = ITextClassifierService.Stub.asInterface(service);
-                    setBindingLocked(false);
-                    handlePendingRequestsLocked();
-                }
-            }
-
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-                cleanupService();
-            }
-
-            @Override
-            public void onBindingDied(ComponentName name) {
-                cleanupService();
-            }
-
-            @Override
-            public void onNullBinding(ComponentName name) {
-                cleanupService();
-            }
-
-            private void cleanupService() {
-                synchronized (mLock) {
-                    mService = null;
-                    setBindingLocked(false);
-                    handlePendingRequestsLocked();
-                }
-            }
-        };
-        mPendingRequests = new LinkedList<>();
         mLock = new Object();
     }
 
@@ -133,30 +124,20 @@
             CharSequence text, int selectionStartIndex, int selectionEndIndex,
             TextSelection.Options options, ITextSelectionCallback callback)
             throws RemoteException {
-        // TODO(b/72481438): All remote calls need to take userId.
         validateInput(text, selectionStartIndex, selectionEndIndex, callback);
 
-        if (!bind()) {
-            callback.onFailure();
-            return;
-        }
-
         synchronized (mLock) {
-            if (isBoundLocked()) {
-                mService.onSuggestSelection(
+            UserState userState = getCallingUserStateLocked();
+            if (!userState.bindLocked()) {
+                callback.onFailure();
+            } else if (userState.isBoundLocked()) {
+                userState.mService.onSuggestSelection(
                         text, selectionStartIndex, selectionEndIndex, options, callback);
             } else {
-                final Callable<Void> request = () -> {
-                    onSuggestSelection(
-                            text, selectionStartIndex, selectionEndIndex,
-                            options, callback);
-                    return null;
-                };
-                final Callable<Void> onServiceFailure = () -> {
-                    callback.onFailure();
-                    return null;
-                };
-                enqueueRequestLocked(request, onServiceFailure, callback.asBinder());
+                userState.mPendingRequests.add(new PendingRequest(
+                        () -> onSuggestSelection(
+                                text, selectionStartIndex, selectionEndIndex, options, callback),
+                        callback::onFailure, callback.asBinder(), this, userState));
             }
         }
     }
@@ -168,24 +149,16 @@
             throws RemoteException {
         validateInput(text, startIndex, endIndex, callback);
 
-        if (!bind()) {
-            callback.onFailure();
-            return;
-        }
-
         synchronized (mLock) {
-            if (isBoundLocked()) {
-                mService.onClassifyText(text, startIndex, endIndex, options, callback);
+            UserState userState = getCallingUserStateLocked();
+            if (!userState.bindLocked()) {
+                callback.onFailure();
+            } else if (userState.isBoundLocked()) {
+                userState.mService.onClassifyText(text, startIndex, endIndex, options, callback);
             } else {
-                final Callable<Void> request = () -> {
-                    onClassifyText(text, startIndex, endIndex, options, callback);
-                    return null;
-                };
-                final Callable<Void> onServiceFailure = () -> {
-                    callback.onFailure();
-                    return null;
-                };
-                enqueueRequestLocked(request, onServiceFailure, callback.asBinder());
+                userState.mPendingRequests.add(new PendingRequest(
+                        () -> onClassifyText(text, startIndex, endIndex, options, callback),
+                        callback::onFailure, callback.asBinder(), this, userState));
             }
         }
     }
@@ -196,24 +169,16 @@
             throws RemoteException {
         validateInput(text, callback);
 
-        if (!bind()) {
-            callback.onFailure();
-            return;
-        }
-
         synchronized (mLock) {
-            if (isBoundLocked()) {
-                mService.onGenerateLinks(text, options, callback);
+            UserState userState = getCallingUserStateLocked();
+            if (!userState.bindLocked()) {
+                callback.onFailure();
+            } else if (userState.isBoundLocked()) {
+                userState.mService.onGenerateLinks(text, options, callback);
             } else {
-                final Callable<Void> request = () -> {
-                    onGenerateLinks(text, options, callback);
-                    return null;
-                };
-                final Callable<Void> onServiceFailure = () -> {
-                    callback.onFailure();
-                    return null;
-                };
-                enqueueRequestLocked(request, onServiceFailure, callback.asBinder());
+                userState.mPendingRequests.add(new PendingRequest(
+                        () -> onGenerateLinks(text, options, callback),
+                        callback::onFailure, callback.asBinder(), this, userState));
             }
         }
     }
@@ -223,99 +188,63 @@
         validateInput(event, mContext);
 
         synchronized (mLock) {
-            if (isBoundLocked()) {
-                mService.onSelectionEvent(event);
+            UserState userState = getCallingUserStateLocked();
+            if (userState.isBoundLocked()) {
+                userState.mService.onSelectionEvent(event);
             } else {
-                final Callable<Void> request = () -> {
-                    onSelectionEvent(event);
-                    return null;
-                };
-                enqueueRequestLocked(request, null /* onServiceFailure */, null /* binder */);
+                userState.mPendingRequests.add(new PendingRequest(
+                        () -> onSelectionEvent(event),
+                        null /* onServiceFailure */, null /* binder */, this, userState));
             }
         }
     }
 
-    /**
-     * @return true if the service is bound or in the process of being bound.
-     *      Returns false otherwise.
-     */
-    private boolean bind() {
-        synchronized (mLock) {
-            if (isBoundLocked() || isBindingLocked()) {
-                return true;
-            }
+    private UserState getCallingUserStateLocked() {
+        return getUserStateLocked(UserHandle.getCallingUserId());
+    }
 
-            // TODO: Handle bind timeout.
-            final boolean willBind;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                Slog.d(LOG_TAG, "Binding to " + mServiceIntent.getComponent());
-                willBind = mContext.bindServiceAsUser(
-                        mServiceIntent, mConnection,
-                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
-                        Binder.getCallingUserHandle());
-                setBindingLocked(willBind);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-            return willBind;
+    private UserState getUserStateLocked(int userId) {
+        UserState result = mUserStates.get(userId);
+        if (result == null) {
+            result = new UserState(userId, mContext, mLock);
+            mUserStates.put(userId, result);
         }
+        return result;
     }
 
-    @GuardedBy("mLock")
-    private boolean isBoundLocked() {
-        return mService != null;
+    UserState peekUserStateLocked(int userId) {
+        return mUserStates.get(userId);
     }
 
-    @GuardedBy("mLock")
-    private boolean isBindingLocked() {
-        return mBinding;
-    }
+    private static final class PendingRequest implements IBinder.DeathRecipient {
 
-    @GuardedBy("mLock")
-    private void setBindingLocked(boolean binding) {
-        mBinding = binding;
-    }
-
-    @GuardedBy("mLock")
-    private void enqueueRequestLocked(
-            Callable<Void> request, Callable<Void> onServiceFailure, IBinder binder) {
-        mPendingRequests.add(new PendingRequest(request, onServiceFailure, binder));
-    }
-
-    @GuardedBy("mLock")
-    private void handlePendingRequestsLocked() {
-        // TODO(b/72481146): Implement PendingRequest similar to that in RemoteFillService.
-        final PendingRequest[] pendingRequests =
-                mPendingRequests.toArray(new PendingRequest[mPendingRequests.size()]);
-        for (PendingRequest pendingRequest : pendingRequests) {
-            if (isBoundLocked()) {
-                pendingRequest.executeLocked();
-            } else {
-                pendingRequest.notifyServiceFailureLocked();
-            }
-        }
-    }
-
-    private final class PendingRequest implements IBinder.DeathRecipient {
-
-        private final Callable<Void> mRequest;
-        @Nullable private final Callable<Void> mOnServiceFailure;
         @Nullable private final IBinder mBinder;
+        @NonNull private final Runnable mRequest;
+        @Nullable private final Runnable mOnServiceFailure;
+        @GuardedBy("mLock")
+        @NonNull private final UserState mOwningUser;
+        @NonNull private final TextClassificationManagerService mService;
 
         /**
          * Initializes a new pending request.
-         *
          * @param request action to perform when the service is bound
          * @param onServiceFailure action to perform when the service dies or disconnects
          * @param binder binder to the process that made this pending request
+         * @param service
+         * @param owningUser
          */
         PendingRequest(
-                Callable<Void> request, @Nullable Callable<Void> onServiceFailure,
-                @Nullable IBinder binder) {
-            mRequest = Preconditions.checkNotNull(request);
-            mOnServiceFailure = onServiceFailure;
+                @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
+                @Nullable IBinder binder,
+                TextClassificationManagerService service,
+                UserState owningUser) {
+            mRequest =
+                    logOnFailure(Preconditions.checkNotNull(request), "handling pending request");
+            mOnServiceFailure =
+                    logOnFailure(onServiceFailure, "notifying callback of service failure");
             mBinder = binder;
+            mService = service;
+            mOwningUser = owningUser;
             if (mBinder != null) {
                 try {
                     mBinder.linkToDeath(this, 0);
@@ -325,32 +254,9 @@
             }
         }
 
-        @GuardedBy("mLock")
-        void executeLocked() {
-            removeLocked();
-            try {
-                mRequest.call();
-            } catch (Exception e) {
-                Slog.d(LOG_TAG, "Error handling pending request: " + e.getMessage());
-            }
-        }
-
-        @GuardedBy("mLock")
-        void notifyServiceFailureLocked() {
-            removeLocked();
-            if (mOnServiceFailure != null) {
-                try {
-                    mOnServiceFailure.call();
-                } catch (Exception e) {
-                    Slog.d(LOG_TAG, "Error notifying callback of service failure: "
-                            + e.getMessage());
-                }
-            }
-        }
-
         @Override
         public void binderDied() {
-            synchronized (mLock) {
+            synchronized (mService.mLock) {
                 // No need to handle this pending request anymore. Remove.
                 removeLocked();
             }
@@ -358,13 +264,19 @@
 
         @GuardedBy("mLock")
         private void removeLocked() {
-            mPendingRequests.remove(this);
+            mOwningUser.mPendingRequests.remove(this);
             if (mBinder != null) {
                 mBinder.unlinkToDeath(this, 0);
             }
         }
     }
 
+    private static Runnable logOnFailure(@Nullable ThrowingRunnable r, String opDesc) {
+        if (r == null) return null;
+        return FunctionalUtils.handleExceptions(r,
+                e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
+    }
+
     private static void validateInput(
             CharSequence text, int startIndex, int endIndex, Object callback)
             throws RemoteException {
@@ -396,4 +308,119 @@
             throw new RemoteException(e.getMessage());
         }
     }
+
+    private static final class UserState {
+        @UserIdInt final int mUserId;
+        final TextClassifierServiceConnection mConnection = new TextClassifierServiceConnection();
+        @GuardedBy("mLock")
+        final Queue<PendingRequest> mPendingRequests = new ArrayDeque<>();
+        @GuardedBy("mLock")
+        ITextClassifierService mService;
+        @GuardedBy("mLock")
+        boolean mBinding;
+
+        private final Context mContext;
+        private final Object mLock;
+
+        private UserState(int userId, Context context, Object lock) {
+            mUserId = userId;
+            mContext = Preconditions.checkNotNull(context);
+            mLock = Preconditions.checkNotNull(lock);
+        }
+
+        @GuardedBy("mLock")
+        boolean isBoundLocked() {
+            return mService != null;
+        }
+
+        @GuardedBy("mLock")
+        private void handlePendingRequestsLocked() {
+            // TODO(b/72481146): Implement PendingRequest similar to that in RemoteFillService.
+            PendingRequest request;
+            while ((request = mPendingRequests.poll()) != null) {
+                if (isBoundLocked()) {
+                    request.mRequest.run();
+                } else {
+                    if (request.mOnServiceFailure != null) {
+                        request.mOnServiceFailure.run();
+                    }
+                }
+
+                if (request.mBinder != null) {
+                    request.mBinder.unlinkToDeath(request, 0);
+                }
+            }
+        }
+
+        private boolean bindIfHasPendingRequestsLocked() {
+            return !mPendingRequests.isEmpty() && bindLocked();
+        }
+
+        /**
+         * @return true if the service is bound or in the process of being bound.
+         *      Returns false otherwise.
+         */
+        private boolean bindLocked() {
+            if (isBoundLocked() || mBinding) {
+                return true;
+            }
+
+            // TODO: Handle bind timeout.
+            final boolean willBind;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                ComponentName componentName =
+                        TextClassifierService.getServiceComponentName(mContext);
+                if (componentName == null) {
+                    // Might happen if the storage is encrypted and the user is not unlocked
+                    return false;
+                }
+                Intent serviceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
+                        .setComponent(componentName);
+                Slog.d(LOG_TAG, "Binding to " + serviceIntent.getComponent());
+                willBind = mContext.bindServiceAsUser(
+                        serviceIntent, mConnection,
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                        UserHandle.of(mUserId));
+                mBinding = willBind;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            return willBind;
+        }
+
+        private final class TextClassifierServiceConnection implements ServiceConnection {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                init(ITextClassifierService.Stub.asInterface(service));
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                cleanupService();
+            }
+
+            @Override
+            public void onBindingDied(ComponentName name) {
+                cleanupService();
+            }
+
+            @Override
+            public void onNullBinding(ComponentName name) {
+                cleanupService();
+            }
+
+            void cleanupService() {
+                init(null);
+            }
+
+            private void init(@Nullable ITextClassifierService service) {
+                synchronized (mLock) {
+                    mService = service;
+                    mBinding = false;
+                    handlePendingRequestsLocked();
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index c31cdec..641a1ba 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1096,10 +1096,7 @@
                     // Add windows of certain types not covered by modal windows.
                     if (isReportedWindowType(windowState.mAttrs.type)) {
                         // Add the window to the ones to be reported.
-                        WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
-                        window.layer = addedWindows.size();
-                        addedWindows.add(window.token);
-                        windows.add(window);
+                        addPopulatedWindowInfo(windowState, boundsInScreen, windows, addedWindows);
                         if (windowState.isFocused()) {
                             focusedWindowAdded = true;
                         }
@@ -1150,10 +1147,8 @@
                             computeWindowBoundsInScreen(windowState, boundsInScreen);
 
                             // Add the window to the ones to be reported.
-                            WindowInfo window = obtainPopulatedWindowInfo(windowState,
-                                    boundsInScreen);
-                            addedWindows.add(window.token);
-                            windows.add(window);
+                            addPopulatedWindowInfo(
+                                    windowState, boundsInScreen, windows, addedWindows);
                             break;
                         }
                     }
@@ -1244,11 +1239,14 @@
                     (int) windowFrame.right, (int) windowFrame.bottom);
         }
 
-        private static WindowInfo obtainPopulatedWindowInfo(
-                WindowState windowState, Rect boundsInScreen) {
+        private static void addPopulatedWindowInfo(
+                WindowState windowState, Rect boundsInScreen,
+                List<WindowInfo> out, Set<IBinder> tokenOut) {
             final WindowInfo window = windowState.getWindowInfo();
             window.boundsInScreen.set(boundsInScreen);
-            return window;
+            window.layer = tokenOut.size();
+            out.add(window);
+            tokenOut.add(window.token);
         }
 
         private void cacheWindows(List<WindowInfo> windows) {
diff --git a/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
new file mode 100644
index 0000000..ae343da
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
@@ -0,0 +1,91 @@
+/*
+ * 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.server.wm;
+
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+
+/**
+ * Keeps track of all {@link AppWindowToken} that are animating and makes sure all animations are
+ * finished at the same time such that we don't run into issues with z-ordering: An activity A
+ * that has a shorter animation that is above another activity B with a longer animation in the same
+ * task, the animation layer would put the B on top of A, but from the hierarchy, A needs to be on
+ * top of B. Thus, we defer reparenting A to the original hierarchy such that it stays on top of B
+ * until B finishes animating.
+ */
+class AnimatingAppWindowTokenRegistry {
+
+    private ArraySet<AppWindowToken> mAnimatingTokens = new ArraySet<>();
+    private ArrayMap<AppWindowToken, Runnable> mFinishedTokens = new ArrayMap<>();
+
+    private ArrayList<Runnable> mTmpRunnableList = new ArrayList<>();
+
+    /**
+     * Notifies that an {@link AppWindowToken} has started animating.
+     */
+    void notifyStarting(AppWindowToken token) {
+        mAnimatingTokens.add(token);
+    }
+
+    /**
+     * Notifies that an {@link AppWindowToken} has finished animating.
+     */
+    void notifyFinished(AppWindowToken token) {
+        mAnimatingTokens.remove(token);
+        mFinishedTokens.remove(token);
+    }
+
+    /**
+     * Called when an {@link AppWindowToken} is about to finish animating.
+     *
+     * @param endDeferFinishCallback Callback to run when defer finish should be ended.
+     * @return {@code true} if finishing the animation should be deferred, {@code false} otherwise.
+     */
+    boolean notifyAboutToFinish(AppWindowToken token, Runnable endDeferFinishCallback) {
+        final boolean removed = mAnimatingTokens.remove(token);
+        if (!removed) {
+            return false;
+        }
+
+        if (mAnimatingTokens.isEmpty()) {
+
+            // If no animations are animating anymore, finish all others.
+            endDeferringFinished();
+            return false;
+        } else {
+
+            // Otherwise let's put it into the pending list of to be finished animations.
+            mFinishedTokens.put(token, endDeferFinishCallback);
+            return true;
+        }
+    }
+
+    private void endDeferringFinished() {
+        // Copy it into a separate temp list to avoid modifying the collection while iterating as
+        // calling the callback may call back into notifyFinished.
+        for (int i = mFinishedTokens.size() - 1; i >= 0; i--) {
+            mTmpRunnableList.add(mFinishedTokens.valueAt(i));
+        }
+        mFinishedTokens.clear();
+        for (int i = mTmpRunnableList.size() - 1; i >= 0; i--) {
+            mTmpRunnableList.get(i).run();
+        }
+        mTmpRunnableList.clear();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index d2ddf55..f8a4540 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -251,6 +251,7 @@
     private final Point mTmpPoint = new Point();
     private final Rect mTmpRect = new Rect();
     private RemoteAnimationDefinition mRemoteAnimationDefinition;
+    private AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry;
 
     AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
             DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
@@ -780,6 +781,16 @@
                 task.mStack.mExitingAppTokens.remove(this);
             }
         }
+        final TaskStack stack = getStack();
+
+        // If we reparent, make sure to remove ourselves from the old animation registry.
+        if (mAnimatingAppWindowTokenRegistry != null) {
+            mAnimatingAppWindowTokenRegistry.notifyFinished(this);
+        }
+        mAnimatingAppWindowTokenRegistry = stack != null
+                ? stack.getAnimatingAppWindowTokenRegistry()
+                : null;
+
         mLastParent = task;
     }
 
@@ -1784,6 +1795,21 @@
     }
 
     @Override
+    public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+        return mAnimatingAppWindowTokenRegistry != null
+                && mAnimatingAppWindowTokenRegistry.notifyAboutToFinish(
+                        this, endDeferFinishCallback);
+    }
+
+    @Override
+    public void onAnimationLeashDestroyed(Transaction t) {
+        super.onAnimationLeashDestroyed(t);
+        if (mAnimatingAppWindowTokenRegistry != null) {
+            mAnimatingAppWindowTokenRegistry.notifyFinished(this);
+        }
+    }
+
+    @Override
     protected void setLayer(Transaction t, int layer) {
         if (!mSurfaceAnimator.hasLeash()) {
             t.setLayer(mSurfaceControl, layer);
@@ -1825,6 +1851,9 @@
 
         final DisplayContent dc = getDisplayContent();
         dc.assignStackOrdering(t);
+        if (mAnimatingAppWindowTokenRegistry != null) {
+            mAnimatingAppWindowTokenRegistry.notifyStarting(this);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c35c05d..e2e1690 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -305,6 +305,11 @@
     int pendingLayoutChanges;
     // TODO(multi-display): remove some of the usages.
     boolean isDefaultDisplay;
+    /**
+     * Flag indicating whether WindowManager should override info for this display in
+     * DisplayManager.
+     */
+    boolean mShouldOverrideDisplayConfiguration = true;
 
     /** Window tokens that are in the process of exiting, but still on screen for animations. */
     final ArrayList<WindowToken> mExitingTokens = new ArrayList<>();
@@ -1177,8 +1182,14 @@
             mDisplayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
         }
 
+        // We usually set the override info in DisplayManager so that we get consistent display
+        // metrics values when displays are changing and don't send out new values until WM is aware
+        // of them. However, we don't do this for displays that serve as containers for ActivityView
+        // because we don't want letter-/pillar-boxing during resize.
+        final DisplayInfo overrideDisplayInfo = mShouldOverrideDisplayConfiguration
+                ? mDisplayInfo : null;
         mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
-                mDisplayInfo);
+                overrideDisplayInfo);
 
         mBaseDisplayRect.set(0, 0, dw, dh);
 
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index ad2fabb..235f63e 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
@@ -268,15 +269,23 @@
                     .build();
 
             // capture a screenshot into the surface we just created
-            Surface sur = new Surface();
-            sur.copyFrom(mSurfaceControl);
             // TODO(multidisplay): we should use the proper display
-            SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
-                            SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
-            t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
-            t.setAlpha(mSurfaceControl, 0);
-            t.show(mSurfaceControl);
-            sur.destroy();
+            final int displayId = SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN;
+            final IBinder displayHandle = SurfaceControl.getBuiltInDisplay(displayId);
+            // This null check below is to guard a race condition where WMS didn't have a chance to
+            // respond to display disconnection before handling rotation , that surfaceflinger may
+            // return a null handle here because it doesn't think that display is valid anymore.
+            if (displayHandle != null) {
+                Surface sur = new Surface();
+                sur.copyFrom(mSurfaceControl);
+                SurfaceControl.screenshot(displayHandle, sur);
+                t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
+                t.setAlpha(mSurfaceControl, 0);
+                t.show(mSurfaceControl);
+                sur.destroy();
+            } else {
+                Slog.w(TAG, "Built-in display " + displayId + " is null.");
+            }
         } catch (OutOfResourcesException e) {
             Slog.w(TAG, "Unable to allocate freeze surface", e);
         }
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index c06caaf..f10ff8c 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -81,9 +81,14 @@
                 if (anim != mAnimation) {
                     return;
                 }
-                reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);
-                if (animationFinishedCallback != null) {
-                    animationFinishedCallback.run();
+                final Runnable resetAndInvokeFinish = () -> {
+                    reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);
+                    if (animationFinishedCallback != null) {
+                        animationFinishedCallback.run();
+                    }
+                };
+                if (!mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)) {
+                    resetAndInvokeFinish.run();
                 }
             }
         };
@@ -407,5 +412,17 @@
          * @return The height of the surface to be animated.
          */
         int getSurfaceHeight();
+
+        /**
+         * Gets called when the animation is about to finish and gives the client the opportunity to
+         * defer finishing the animation, i.e. it keeps the leash around until the client calls
+         * {@link #cancelAnimation}.
+         *
+         * @param endDeferFinishCallback The callback to call when defer finishing should be ended.
+         * @return Whether the client would like to defer the animation finish.
+         */
+        default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+            return false;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b5d00a7..460edec 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -155,6 +155,9 @@
     final Rect mTmpDimBoundsRect = new Rect();
     private final Point mLastSurfaceSize = new Point();
 
+    private final AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry =
+            new AnimatingAppWindowTokenRegistry();
+
     TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
         super(service);
         mStackId = stackId;
@@ -1782,4 +1785,8 @@
         outPos.x -= outset;
         outPos.y -= outset;
     }
+
+    AnimatingAppWindowTokenRegistry getAnimatingAppWindowTokenRegistry() {
+        return mAnimatingAppWindowTokenRegistry;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5ae4dc5..27c0b3b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -285,7 +285,14 @@
         }
 
         if (mSurfaceControl != null) {
-            getPendingTransaction().destroy(mSurfaceControl);
+            mPendingTransaction.destroy(mSurfaceControl);
+
+            // Merge to parent transaction to ensure the transactions on this WindowContainer are
+            // applied in native even if WindowContainer is removed.
+            if (mParent != null) {
+                mParent.getPendingTransaction().merge(mPendingTransaction);
+            }
+
             mSurfaceControl = null;
             scheduleAnimation();
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9f8de58b..5f0769d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1117,17 +1117,7 @@
                 throw new IllegalStateException("Display has not been initialialized");
             }
 
-            DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-
-            // Adding a window is an exception where the WindowManagerService can create the
-            // display instead of waiting for the ActivityManagerService to drive creation.
-            if (displayContent == null) {
-                final Display display = mDisplayManager.getDisplay(displayId);
-
-                if (display != null) {
-                    displayContent = mRoot.createDisplayContent(display, null /* controller */);
-                }
-            }
+            final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
 
             if (displayContent == null) {
                 Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
@@ -1493,6 +1483,32 @@
         return res;
     }
 
+    /**
+     * Get existing {@link DisplayContent} or create a new one if the display is registered in
+     * DisplayManager.
+     *
+     * NOTE: This should only be used in cases when there is a chance that a {@link DisplayContent}
+     * that corresponds to a display just added to DisplayManager has not yet been created. This
+     * usually means that the call of this method was initiated from outside of Activity or Window
+     * Manager. In most cases the regular getter should be used.
+     * @see RootWindowContainer#getDisplayContent(int)
+     */
+    private DisplayContent getDisplayContentOrCreate(int displayId) {
+        DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+
+        // Create an instance if possible instead of waiting for the ActivityManagerService to drive
+        // the creation.
+        if (displayContent == null) {
+            final Display display = mDisplayManager.getDisplay(displayId);
+
+            if (display != null) {
+                displayContent = mRoot.createDisplayContent(display, null /* controller */);
+            }
+        }
+
+        return displayContent;
+    }
+
     private boolean doesAddToastWindowRequireToken(String packageName, int callingUid,
             WindowState attachedWindow) {
         // Try using the target SDK of the root window
@@ -6987,6 +7003,24 @@
     }
 
     @Override
+    public void dontOverrideDisplayInfo(int displayId) {
+        synchronized (mWindowMap) {
+            final DisplayContent dc = getDisplayContentOrCreate(displayId);
+            if (dc == null) {
+                throw new IllegalArgumentException(
+                        "Trying to register a non existent display.");
+            }
+            // We usually set the override info in DisplayManager so that we get consistent
+            // values when displays are changing. However, we don't do this for displays that
+            // serve as containers for ActivityViews because we don't want letter-/pillar-boxing
+            // during resize.
+            dc.mShouldOverrideDisplayConfiguration = false;
+            mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(displayId,
+                    null /* info */);
+        }
+    }
+
+    @Override
     public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
             throws RemoteException {
         if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, "registerShortcutKey")) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c100511..eebf2fd 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1032,15 +1032,21 @@
         if (mAttrs.type == TYPE_DOCK_DIVIDER) {
             // For the docked divider, we calculate the stable insets like a full-screen window
             // so it can use it to calculate the snap positions.
-            mStableInsets.set(Math.max(mStableFrame.left - mDisplayFrame.left, 0),
-                    Math.max(mStableFrame.top - mDisplayFrame.top, 0),
-                    Math.max(mDisplayFrame.right - mStableFrame.right, 0),
-                    Math.max(mDisplayFrame.bottom - mStableFrame.bottom, 0));
+            final WmDisplayCutout c = displayCutout.calculateRelativeTo(mDisplayFrame);
+            mTmpRect.set(mDisplayFrame);
+            mTmpRect.inset(c.getDisplayCutout().getSafeInsets());
+            mTmpRect.intersectUnchecked(mStableFrame);
+
+            mStableInsets.set(Math.max(mTmpRect.left - mDisplayFrame.left, 0),
+                    Math.max(mTmpRect.top - mDisplayFrame.top, 0),
+                    Math.max(mDisplayFrame.right - mTmpRect.right, 0),
+                    Math.max(mDisplayFrame.bottom - mTmpRect.bottom, 0));
 
             // The divider doesn't care about insets in any case, so set it to empty so we don't
             // trigger a relayout when moving it.
             mContentInsets.setEmpty();
             mVisibleInsets.setEmpty();
+            displayCutout = WmDisplayCutout.NO_CUTOUT;
         } else {
             getDisplayContent().getBounds(mTmpRect);
             // Override right and/or bottom insets in case if the frame doesn't fit the screen in
@@ -4525,8 +4531,7 @@
                 mAttrs.type == TYPE_NAVIGATION_BAR ||
                 // It's tempting to wonder: Have we forgotten the rounded corners overlay?
                 // worry not: it's a fake TYPE_NAVIGATION_BAR_PANEL
-                mAttrs.type == TYPE_NAVIGATION_BAR_PANEL ||
-                mAttrs.type == TYPE_STATUS_BAR) {
+                mAttrs.type == TYPE_NAVIGATION_BAR_PANEL) {
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 410eddf..0c2b075 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -225,6 +225,11 @@
     // case we need to give the client a new Surface if it lays back out to a visible state.
     boolean mChildrenDetached = false;
 
+    // Set to true after the first frame of the Pinned stack animation
+    // and reset after the last to ensure we only reset mForceScaleUntilResize
+    // once per animation.
+    boolean mPipAnimationStarted = false;
+
     WindowStateAnimator(final WindowState win) {
         final WindowManagerService service = win.mService;
 
@@ -512,7 +517,7 @@
             mDrawState = NO_SURFACE;
             return null;
         } catch (Exception e) {
-            Slog.e(TAG, "Exception creating surface", e);
+            Slog.e(TAG, "Exception creating surface (parent dead?)", e);
             mDrawState = NO_SURFACE;
             return null;
         }
@@ -983,8 +988,13 @@
             // As we are in SCALING_MODE_SCALE_TO_WINDOW, SurfaceFlinger will
             // then take over the scaling until the new buffer arrives, and things
             // will be seamless.
-            mForceScaleUntilResize = true;
+            if (mPipAnimationStarted == false) {
+                mForceScaleUntilResize = true;
+                mPipAnimationStarted = true;
+            }
         } else {
+            mPipAnimationStarted = false;
+
             if (!w.mSeamlesslyRotated) {
                 mSurfaceController.setPositionInTransaction(mXOffset, mYOffset, recoveringMemory);
             }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 70abf80..d165a45 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -196,6 +196,8 @@
             "com.android.server.search.SearchManagerService$Lifecycle";
     private static final String THERMAL_OBSERVER_CLASS =
             "com.google.android.clockwork.ThermalObserver";
+    private static final String WEAR_CONFIG_SERVICE_CLASS =
+            "com.google.android.clockwork.WearConfigManagerService";
     private static final String WEAR_CONNECTIVITY_SERVICE_CLASS =
             "com.android.clockwork.connectivity.WearConnectivityService";
     private static final String WEAR_SIDEKICK_SERVICE_CLASS =
@@ -1543,6 +1545,10 @@
         }
 
         if (isWatch) {
+            traceBeginAndSlog("StartWearConfigService");
+            mSystemServiceManager.startService(WEAR_CONFIG_SERVICE_CLASS);
+            traceEnd();
+
             traceBeginAndSlog("StartWearConnectivityService");
             mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS);
             traceEnd();
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index f603a09..d0398ad 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -148,16 +148,22 @@
         Looper backupLooper = startBackupThreadAndGetLooper();
         mShadowBackupLooper = shadowOf(backupLooper);
         mBackupHandler = new BackupHandler(mBackupManagerService, backupLooper);
+        Handler mainHandler = new Handler(Looper.getMainLooper());
 
         mBackupManager = spy(FakeIBackupManager.class);
 
+        BackupAgentTimeoutParameters agentTimeoutParameters =
+                new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver());
+        agentTimeoutParameters.start();
+
         setUpBackupManagerServiceBasics(
                 mBackupManagerService,
                 application,
                 mTransportManager,
                 packageManager,
                 mBackupHandler,
-                mWakeLock);
+                mWakeLock,
+                agentTimeoutParameters);
         when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir);
         when(mBackupManagerService.getDataDir()).thenReturn(dataDir);
         when(mBackupManagerService.getBackupManagerBinder()).thenReturn(mBackupManager);
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 03792b1..869c50b 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -41,12 +41,14 @@
 import android.app.backup.RestoreSet;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.internal.BackupHandler;
@@ -115,6 +117,15 @@
 
         Looper backupLooper = startBackupThreadAndGetLooper();
         mShadowBackupLooper = shadowOf(backupLooper);
+
+        Handler mainHandler = new Handler(Looper.getMainLooper());
+        BackupAgentTimeoutParameters agentTimeoutParameters =
+                new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver());
+        agentTimeoutParameters.start();
+
+        // We need to mock BMS timeout parameters before initializing the BackupHandler since
+        // the constructor of BackupHandler relies on the timeout parameters.
+        when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
         BackupHandler backupHandler = new BackupHandler(mBackupManagerService, backupLooper);
 
         mWakeLock = createBackupWakeLock(application);
@@ -125,7 +136,8 @@
                 mTransportManager,
                 application.getPackageManager(),
                 backupHandler,
-                mWakeLock);
+                mWakeLock,
+                agentTimeoutParameters);
         when(mBackupManagerService.getPendingRestores()).thenReturn(new ArrayDeque<>());
     }
 
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index c210fde..5a886e3 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -28,6 +28,7 @@
 import android.os.PowerManager;
 import android.util.SparseArray;
 
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.internal.BackupHandler;
@@ -43,7 +44,8 @@
             TransportManager transportManager,
             PackageManager packageManager,
             BackupHandler backupHandler,
-            PowerManager.WakeLock wakeLock) {
+            PowerManager.WakeLock wakeLock,
+            BackupAgentTimeoutParameters agentTimeoutParameters) {
         when(backupManagerService.getContext()).thenReturn(context);
         when(backupManagerService.getTransportManager()).thenReturn(transportManager);
         when(backupManagerService.getPackageManager()).thenReturn(packageManager);
@@ -53,6 +55,7 @@
         when(backupManagerService.getCurrentOperations()).thenReturn(new SparseArray<>());
         when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class));
         when(backupManagerService.getWakelock()).thenReturn(wakeLock);
+        when(backupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
     }
 
     public static PowerManager.WakeLock createBackupWakeLock(Application application) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 0462b14..e5c6c6e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.accessibility;
 
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -57,6 +60,7 @@
     static final int SERVICE_ID = 42;
 
     AccessibilityServiceConnection mConnection;
+
     @Mock AccessibilityManagerService.UserState mMockUserState;
     @Mock Context mMockContext;
     @Mock AccessibilityServiceInfo mMockServiceInfo;
@@ -66,7 +70,9 @@
     @Mock WindowManagerInternal mMockWindowManagerInternal;
     @Mock GlobalActionPerformer mMockGlobalActionPerformer;
     @Mock KeyEventDispatcher mMockKeyEventDispatcher;
+    @Mock MagnificationController mMockMagnificationController;
 
+    MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
 
     @BeforeClass
     public static void oneTimeInitialization() {
@@ -79,12 +85,15 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mMockSystemSupport.getKeyEventDispatcher()).thenReturn(mMockKeyEventDispatcher);
+        when(mMockSystemSupport.getMagnificationController())
+                .thenReturn(mMockMagnificationController);
+
         when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
         mMockResolveInfo.serviceInfo = mock(ServiceInfo.class);
         mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
 
         mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
-                COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, new Handler(), new Object(),
+                COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
                 mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
                 mMockGlobalActionPerformer);
     }
@@ -106,12 +115,30 @@
     @Test
     public void bindConnectUnbind_linksAndUnlinksToServiceDeath() throws RemoteException {
         IBinder mockBinder = mock(IBinder.class);
-        when(mMockUserState.getBindingServicesLocked())
-                .thenReturn(new HashSet<>(Arrays.asList(COMPONENT_NAME)));
+        setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
         mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
         verify(mockBinder).linkToDeath(eq(mConnection), anyInt());
         mConnection.unbindLocked();
         verify(mockBinder).unlinkToDeath(eq(mConnection), anyInt());
     }
+
+    @Test
+    public void connectedServiceCrashedAndRestarted_crashReportedInServiceInfo() {
+        IBinder mockBinder = mock(IBinder.class);
+        setServiceBinding(COMPONENT_NAME);
+        mConnection.bindLocked();
+        mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+        assertFalse(mConnection.getServiceInfo().crashed);
+        mConnection.binderDied();
+        assertTrue(mConnection.getServiceInfo().crashed);
+        mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+        mHandler.sendAllMessages();
+        assertFalse(mConnection.getServiceInfo().crashed);
+    }
+
+    private void setServiceBinding(ComponentName componentName) {
+        when(mMockUserState.getBindingServicesLocked())
+                .thenReturn(new HashSet<>(Arrays.asList(componentName)));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 2378e6d..8721d9c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -26,6 +26,12 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
 import android.app.WaitResult;
@@ -180,4 +186,56 @@
             assertEquals(deliverToTopWait.who, firstActivity.realActivity);
         }
     }
+
+    @Test
+    public void testApplySleepTokensLocked() throws Exception {
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final KeyguardController keyguard = mSupervisor.getKeyguardController();
+        final ActivityStack stack = mock(ActivityStack.class);
+        display.addChild(stack, 0 /* position */);
+
+        // Make sure we wake and resume in the case the display is turning on and the keyguard is
+        // not showing.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                true /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // showing.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                true /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // not showing as unfocused.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, false /* isFocusedStack */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Should not do anything if the display state hasn't changed.
+        verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                false /* keyguardShowing */, false /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+    }
+
+    private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
+            ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
+            boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
+            boolean expectResumeTopActivity) {
+        reset(stack);
+
+        doReturn(displayShouldSleep).when(display).shouldSleep();
+        doReturn(displaySleeping).when(display).isSleeping();
+        doReturn(keyguardShowing).when(keyguard).isKeyguardShowing(anyInt());
+
+        mSupervisor.mFocusedStack = isFocusedStack ? stack : null;
+        mSupervisor.applySleepTokensLocked(true);
+        verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+        verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
+                null /* target */, null /* targetOptions */);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 5906db3..8ff3e45 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -18,13 +18,19 @@
 
 import static android.app.ActivityManager.START_ABORTED;
 import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
 import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
+import static android.app.ActivityManager.START_PERMISSION_DENIED;
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_SWITCHES_CANCELED;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -45,6 +51,7 @@
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
+import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
 
 import static org.junit.Assert.assertEquals;
@@ -62,9 +69,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.times;
 
-import static android.app.ActivityManager.START_PERMISSION_DENIED;
-import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
-
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.am.ActivityStarter.Factory;
 import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
@@ -290,7 +294,7 @@
         }
     }
 
-    private ActivityStarter prepareStarter() {
+    private ActivityStarter prepareStarter(int launchFlags) {
         // always allow test to start activity.
         doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
                 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
@@ -325,8 +329,20 @@
         // ignore requests to create window container.
         doNothing().when(task).createWindowContainer(anyBoolean(), anyBoolean());
 
+
+        final Intent intent = new Intent();
+        intent.addFlags(launchFlags);
+        intent.setComponent(ActivityBuilder.getDefaultComponent());
+
+        final ActivityInfo info = new ActivityInfo();
+
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
+
         return new ActivityStarter(mController, mService,
-                mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
+                mService.mStackSupervisor, mock(ActivityStartInterceptor.class))
+                .setIntent(intent)
+                .setActivityInfo(info);
     }
 
     /**
@@ -342,9 +358,6 @@
         // add custom values to activity info to make unique.
         final ActivityInfo info = new ActivityInfo();
         final Rect launchBounds = new Rect(0, 0, 20, 30);
-        final Intent intent = new Intent();
-
-        intent.setComponent(ActivityBuilder.getDefaultComponent());
 
         final WindowLayout windowLayout =
                 new WindowLayout(10, .5f, 20, 1.0f, Gravity.NO_GRAVITY, 1, 1);
@@ -354,14 +367,13 @@
         info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
 
         // create starter.
-        final ActivityStarter optionStarter = prepareStarter();
+        final ActivityStarter optionStarter = prepareStarter(0 /* launchFlags */);
 
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchBounds(launchBounds);
 
         // run starter.
         optionStarter
-                .setIntent(intent)
                 .setReason("testCreateTaskLayout")
                 .setActivityInfo(info)
                 .setActivityOptions(new SafeActivityOptions(options))
@@ -371,4 +383,69 @@
         verify(modifier, times(1)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
                 any(), any());
     }
+
+    /**
+     * This test ensures that if the intent is being delivered to a
+     */
+    @Test
+    public void testSplitScreenDeliverToTop() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        final ActivityRecord focusActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        // Create reusable activity after entering split-screen so that it is the top secondary
+        // stack.
+        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+        // Set focus back to primary.
+        mService.mStackSupervisor.setFocusStackUnchecked("testSplitScreenDeliverToTop",
+                focusActivity.getStack());
+
+        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+
+        final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
+
+        // Ensure result is delivering intent to top.
+        assertEquals(result, START_DELIVERED_TO_TOP);
+    }
+
+    /**
+     * This test ensures that if the intent is being delivered to a split-screen unfocused task
+     * reports it is brought to front instead of delivering to top.
+     */
+    @Test
+    public void testSplitScreenTaskToFront() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        // Create reusable activity here first. Setting the windowing mode of the primary stack
+        // will move the existing standard full screen stack to secondary, putting this one on the
+        // bottom.
+        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+        final ActivityRecord focusActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        // Enter split-screen. Primary stack should have focus.
+        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+
+        final int result = starter.setReason("testSplitScreenMoveToFront").execute();
+
+        // Ensure result is moving task to front.
+        assertEquals(result, START_TASK_TO_FRONT);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index c130592..6fb1b2e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -353,22 +353,29 @@
      */
     protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
         private ActivityDisplay mDisplay;
+        private KeyguardController mKeyguardController;
 
         public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
             super(service, looper);
             mDisplayManager =
                     (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
             mWindowManager = prepareMockWindowManager();
+            mKeyguardController = mock(KeyguardController.class);
         }
 
         @Override
         public void initialize() {
             super.initialize();
-            mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
+            mDisplay = spy(new TestActivityDisplay(this, DEFAULT_DISPLAY));
             attachDisplay(mDisplay);
         }
 
         @Override
+        public KeyguardController getKeyguardController() {
+            return mKeyguardController;
+        }
+
+        @Override
         ActivityDisplay getDefaultDisplay() {
             return mDisplay;
         }
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index 613d0af..10a21fd 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -437,6 +437,11 @@
     }
 
     @Override
+    public ResolveInfo resolveServiceAsUser(Intent intent, int flags, int userId) {
+        return null;
+    }
+
+    @Override
     public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
         return null;
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e8170ee..d2fb1ca 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4738,7 +4738,11 @@
 
     public void testOverrideApnAPIsFailWithPO() throws Exception {
         setupProfileOwner();
-        ApnSetting apn = (new ApnSetting.Builder()).build();
+        ApnSetting apn = (new ApnSetting.Builder())
+            .setApnName("test")
+            .setEntryName("test")
+            .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT)
+            .build();
         assertExpectException(SecurityException.class, null, () ->
                 dpm.addOverrideApn(admin1, apn));
         assertExpectException(SecurityException.class, null, () ->
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 0ceb558..260bb0a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -40,6 +40,7 @@
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 import android.security.KeyStore;
+import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
@@ -68,6 +69,7 @@
 
 import java.io.File;
 import java.nio.charset.StandardCharsets;
+import java.security.UnrecoverableKeyException;
 import java.security.cert.CertPath;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
@@ -76,8 +78,10 @@
 import java.util.Map;
 import java.util.Random;
 
+import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
 @SmallTest
@@ -85,7 +89,7 @@
 public class RecoverableKeyStoreManagerTest {
     private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
 
-    private static final String ROOT_CERTIFICATE_ALIAS = "put_default_alias_here";
+    private static final String ROOT_CERTIFICATE_ALIAS = "";
     private static final String TEST_SESSION_ID = "karlin";
     private static final byte[] TEST_PUBLIC_KEY = new byte[] {
         (byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a,
@@ -139,12 +143,12 @@
     private static final String KEY_ALGORITHM = "AES";
     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
     private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey";
+    private static final String TEST_ROOT_CERT_ALIAS = "";
 
     @Mock private Context mMockContext;
     @Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
     @Mock private KeyguardManager mKeyguardManager;
     @Mock private PlatformKeyManager mPlatformKeyManager;
-    @Mock private KeyStore mKeyStore;
     @Mock private ApplicationKeyStorage mApplicationKeyStorage;
 
     private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
@@ -175,7 +179,7 @@
 
         mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
                 mMockContext,
-                mKeyStore,
+                KeyStore.getInstance(),
                 mRecoverableKeyStoreDb,
                 mRecoverySessionStorage,
                 Executors.newSingleThreadExecutor(),
@@ -219,6 +223,7 @@
 
         assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
         assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+        // TODO(76083050) Test the grant mechanism for the keys.
     }
 
     @Test
@@ -449,10 +454,13 @@
                         eq(Manifest.permission.RECOVER_KEYSTORE), any());
     }
 
+    // TODO: Add tests for non-existing cert alias
+
     @Test
     public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                 TEST_SESSION_ID,
+                TEST_ROOT_CERT_ALIAS,
                 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
@@ -474,6 +482,7 @@
     public void startRecoverySessionWithCertPath_checksPermissionFirst() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                 TEST_SESSION_ID,
+                TEST_ROOT_CERT_ALIAS,
                 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
@@ -591,6 +600,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
+                    TEST_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
@@ -609,6 +619,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
+                    TEST_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                     vaultParams,
                     TEST_VAULT_CHALLENGE,
@@ -631,6 +642,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
+                    TEST_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(emptyCertPath),
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
@@ -655,6 +667,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
+                    TEST_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(shortCertPath),
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
@@ -671,9 +684,9 @@
     }
 
     @Test
-    public void recoverKeys_throwsIfNoSessionIsPresent() throws Exception {
+    public void recoverKeyChainSnapshot_throwsIfNoSessionIsPresent() throws Exception {
         try {
-            mRecoverableKeyStoreManager.recoverKeys(
+            mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                     TEST_SESSION_ID,
                     /*recoveryKeyBlob=*/ randomBytes(32),
                     /*applicationKeys=*/ ImmutableList.of(
@@ -686,7 +699,7 @@
     }
 
     @Test
-    public void recoverKeys_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception {
+    public void recoverKeyChainSnapshot_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -699,7 +712,7 @@
                         TEST_SECRET)));
 
         try {
-            mRecoverableKeyStoreManager.recoverKeys(
+            mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                     TEST_SESSION_ID,
                     /*encryptedRecoveryKey=*/ randomBytes(60),
                     /*applicationKeys=*/ ImmutableList.of());
@@ -710,7 +723,7 @@
     }
 
     @Test
-    public void recoverKeys_throwsIfFailedToDecryptAllApplicationKeys() throws Exception {
+    public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -731,7 +744,7 @@
                 encryptedApplicationKey(randomRecoveryKey(), randomBytes(32)));
 
         try {
-            mRecoverableKeyStoreManager.recoverKeys(
+            mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                     TEST_SESSION_ID,
                     /*encryptedRecoveryKey=*/ encryptedClaimResponse,
                     /*applicationKeys=*/ ImmutableList.of(badApplicationKey));
@@ -742,7 +755,8 @@
     }
 
     @Test
-    public void recoverKeys_doesNotThrowIfNoApplicationKeysToBeDecrypted() throws Exception {
+    public void recoverKeyChainSnapshot_doesNotThrowIfNoApplicationKeysToBeDecrypted()
+            throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -759,14 +773,14 @@
         byte[] encryptedClaimResponse = encryptClaimResponse(
                 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
 
-        mRecoverableKeyStoreManager.recoverKeys(
+        mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                 TEST_SESSION_ID,
                 /*encryptedRecoveryKey=*/ encryptedClaimResponse,
                 /*applicationKeys=*/ ImmutableList.of());
     }
 
     @Test
-    public void recoverKeys_returnsDecryptedKeys() throws Exception {
+    public void recoverKeyChainSnapshot_returnsDecryptedKeys() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -787,17 +801,18 @@
                 TEST_ALIAS,
                 encryptedApplicationKey(recoveryKey, applicationKeyBytes));
 
-        Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys(
+        Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                 TEST_SESSION_ID,
                 encryptedClaimResponse,
                 ImmutableList.of(applicationKey));
 
         assertThat(recoveredKeys).hasSize(1);
-        assertThat(recoveredKeys.get(TEST_ALIAS)).isEqualTo(applicationKeyBytes);
+        assertThat(recoveredKeys).containsKey(TEST_ALIAS);
+        // TODO(76083050) Test the grant mechanism for the keys.
     }
 
     @Test
-    public void recoverKeys_worksOnOtherApplicationKeysIfOneDecryptionFails() throws Exception {
+    public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -825,13 +840,14 @@
                 TEST_ALIAS2,
                 encryptedApplicationKey(recoveryKey, applicationKeyBytes2));
 
-        Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys(
+        Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                 TEST_SESSION_ID,
                 encryptedClaimResponse,
                 ImmutableList.of(applicationKey1, applicationKey2));
 
         assertThat(recoveredKeys).hasSize(1);
-        assertThat(recoveredKeys.get(TEST_ALIAS2)).isEqualTo(applicationKeyBytes2);
+        assertThat(recoveredKeys).containsKey(TEST_ALIAS2);
+        // TODO(76083050) Test the grant mechanism for the keys.
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index edf1f74..552c915 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -17,6 +17,8 @@
 package com.android.server.usage;
 
 import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
+import static android.app.usage.UsageEvents.Event.SLICE_PINNED;
+import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;
 import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
@@ -406,6 +408,30 @@
     }
 
     @Test
+    public void testSlicePinnedEvent() throws Exception {
+        setChargingState(mController, false);
+
+        reportEvent(mController, USER_INTERACTION, 0);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+        mInjector.mElapsedRealtime = 1;
+        reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+
+        mController.forceIdleState(PACKAGE_1, USER_ID, true);
+        reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
+    }
+
+    @Test
+    public void testSlicePinnedPrivEvent() throws Exception {
+        setChargingState(mController, false);
+
+        mController.forceIdleState(PACKAGE_1, USER_ID, true);
+        reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+    }
+
+    @Test
     public void testPredictionTimedout() throws Exception {
         setChargingState(mController, false);
         // Set it to timeout or usage, so that prediction can override it
diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
new file mode 100644
index 0000000..8b78f10
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for the {@link TaskStack} class.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:com.android.server.wm.AnimatingAppWindowTokenRegistryTest
+ */
+@SmallTest
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class AnimatingAppWindowTokenRegistryTest extends WindowTestsBase {
+
+    @Mock
+    AnimationAdapter mAdapter;
+
+    @Mock
+    Runnable mMockEndDeferFinishCallback1;
+    @Mock
+    Runnable mMockEndDeferFinishCallback2;
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testDeferring() throws Exception {
+        final AppWindowToken window1 = createAppWindowToken(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD,
+                "window2").mAppToken;
+        final AnimatingAppWindowTokenRegistry registry =
+                window1.getStack().getAnimatingAppWindowTokenRegistry();
+
+        window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
+        window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
+        assertTrue(window1.isSelfAnimating());
+        assertTrue(window2.isSelfAnimating());
+
+        // Make sure that first animation finish is deferred, second one is not deferred, and first
+        // one gets cancelled.
+        assertTrue(registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1));
+        assertFalse(registry.notifyAboutToFinish(window2, mMockEndDeferFinishCallback2));
+        verify(mMockEndDeferFinishCallback1).run();
+        verifyZeroInteractions(mMockEndDeferFinishCallback2);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 4f49a4a..b645700 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -31,7 +31,10 @@
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -457,6 +460,18 @@
                 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
     }
 
+    @Test
+    public void testDisableDisplayInfoOverrideFromWindowManager() {
+        final DisplayContent dc = createNewDisplay();
+
+        assertTrue(dc.mShouldOverrideDisplayConfiguration);
+        sWm.dontOverrideDisplayInfo(dc.getDisplayId());
+
+        assertFalse(dc.mShouldOverrideDisplayConfiguration);
+        verify(sWm.mDisplayManagerInternal, times(1))
+                .setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null);
+    }
+
     private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
                              int expectedBaseHeight, int expectedBaseDensity) {
         assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth);
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index a120eba..6506872 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.SurfaceControl;
@@ -64,6 +65,7 @@
     private SurfaceSession mSession = new SurfaceSession();
     private MyAnimatable mAnimatable;
     private MyAnimatable mAnimatable2;
+    private DeferFinishAnimatable mDeferFinishAnimatable;
 
     @Before
     public void setUp() throws Exception {
@@ -71,6 +73,7 @@
         MockitoAnnotations.initMocks(this);
         mAnimatable = new MyAnimatable();
         mAnimatable2 = new MyAnimatable();
+        mDeferFinishAnimatable = new DeferFinishAnimatable();
     }
 
     @Test
@@ -166,6 +169,29 @@
         verify(mTransaction).destroy(eq(leash));
     }
 
+    @Test
+    @FlakyTest(detail = "Promote once confirmed non-flaky")
+    public void testDeferFinish() throws Exception {
+
+        // Start animation
+        mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec,
+                true /* hidden */);
+        final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
+                OnAnimationFinishedCallback.class);
+        assertAnimating(mDeferFinishAnimatable);
+        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+        // Finish the animation but then make sure we are deferring.
+        callbackCaptor.getValue().onAnimationFinished(mSpec);
+        assertAnimating(mDeferFinishAnimatable);
+
+        // Now end defer finishing.
+        mDeferFinishAnimatable.endDeferFinishCallback.run();
+        assertNotAnimating(mAnimatable2);
+        assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled);
+        verify(mTransaction).destroy(eq(mDeferFinishAnimatable.mLeash));
+    }
+
     private void assertAnimating(MyAnimatable animatable) {
         assertTrue(animatable.mSurfaceAnimator.isAnimating());
         assertNotNull(animatable.mSurfaceAnimator.getAnimation());
@@ -254,4 +280,15 @@
 
         private final Runnable mFinishedCallback = () -> mFinishedCallbackCalled = true;
     }
+
+    private class DeferFinishAnimatable extends MyAnimatable {
+
+        Runnable endDeferFinishCallback;
+
+        @Override
+        public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+            this.endDeferFinishCallback = endDeferFinishCallback;
+            return true;
+        }
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index f4313b8..181fceb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -70,6 +70,7 @@
             assertEquals(getSnoozeCriteria(key, i), ranking.getSnoozeCriteria());
             assertEquals(getShowBadge(i), ranking.canShowBadge());
             assertEquals(getUserSentiment(i), ranking.getUserSentiment());
+            assertEquals(getHidden(i), ranking.isSuspended());
         }
     }
 
@@ -85,6 +86,7 @@
         Bundle showBadge = new Bundle();
         int[] importance = new int[mKeys.length];
         Bundle userSentiment = new Bundle();
+        Bundle mHidden = new Bundle();
 
         for (int i = 0; i < mKeys.length; i++) {
             String key = mKeys[i];
@@ -101,11 +103,12 @@
             snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i));
             showBadge.putBoolean(key, getShowBadge(i));
             userSentiment.putInt(key, getUserSentiment(i));
+            mHidden.putBoolean(key, getHidden(i));
         }
         NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
                 interceptedKeys.toArray(new String[0]), visibilityOverrides,
                 suppressedVisualEffects, importance, explanation, overrideGroupKeys,
-                channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
+                channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden);
         return update;
     }
 
@@ -153,6 +156,10 @@
         return USER_SENTIMENT_NEUTRAL;
     }
 
+    private boolean getHidden(int index) {
+        return index % 2 == 0;
+    }
+
     private ArrayList<String> getPeople(String key, int index) {
         ArrayList<String> people = new ArrayList<>();
         for (int i = 0; i < index; i++) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4fe54b9..b82becd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -68,6 +68,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
+import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -264,7 +265,7 @@
                     mPackageManager, mPackageManagerClient, mockLightsManager,
                     mListeners, mAssistants, mConditionProviders,
                     mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
-                    mGroupHelper, mAm);
+                    mGroupHelper, mAm, mock(UsageStatsManagerInternal.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
@@ -2662,4 +2663,120 @@
 
         assertEquals(expected, actual);
     }
+
+    @Test
+    public void testVisualDifference_diffTitle() {
+        Notification.Builder nb1 = new Notification.Builder(mContext, "")
+                .setContentTitle("foo");
+        StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb1.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r1 =
+                new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+        Notification.Builder nb2 = new Notification.Builder(mContext, "")
+                .setContentTitle("bar");
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb2.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r2 =
+                new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+        assertTrue(mService.isVisuallyInterruptive(r1, r2));
+    }
+
+    @Test
+    public void testVisualDifference_diffText() {
+        Notification.Builder nb1 = new Notification.Builder(mContext, "")
+                .setContentText("foo");
+        StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb1.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r1 =
+                new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+        Notification.Builder nb2 = new Notification.Builder(mContext, "")
+                .setContentText("bar");
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb2.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r2 =
+                new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+        assertTrue(mService.isVisuallyInterruptive(r1, r2));
+    }
+
+    @Test
+    public void testVisualDifference_diffProgress() {
+        Notification.Builder nb1 = new Notification.Builder(mContext, "")
+                .setProgress(100, 90, false);
+        StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb1.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r1 =
+                new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+        Notification.Builder nb2 = new Notification.Builder(mContext, "")
+                .setProgress(100, 100, false);
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb2.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r2 =
+                new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+        assertTrue(mService.isVisuallyInterruptive(r1, r2));
+    }
+
+    @Test
+    public void testVisualDifference_diffProgressNotDone() {
+        Notification.Builder nb1 = new Notification.Builder(mContext, "")
+                .setProgress(100, 90, false);
+        StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb1.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r1 =
+                new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+        Notification.Builder nb2 = new Notification.Builder(mContext, "")
+                .setProgress(100, 91, false);
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb2.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r2 =
+                new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+        assertFalse(mService.isVisuallyInterruptive(r1, r2));
+    }
+
+    @Test
+    public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() {
+        // post 2 notification from this package
+        final NotificationRecord notif1 = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, true);
+        final NotificationRecord notif2 = generateNotificationRecord(
+                mTestNotificationChannel, 2, null, false);
+        mService.addNotification(notif1);
+        mService.addNotification(notif2);
+
+        // on broadcast, hide the 2 notifications
+        mService.simulatePackageSuspendBroadcast(true, PKG);
+        ArgumentCaptor<List> captorHide = ArgumentCaptor.forClass(List.class);
+        verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
+        assertEquals(2, captorHide.getValue().size());
+
+        // on broadcast, unhide the 2 notifications
+        mService.simulatePackageSuspendBroadcast(false, PKG);
+        ArgumentCaptor<List> captorUnhide = ArgumentCaptor.forClass(List.class);
+        verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
+        assertEquals(2, captorUnhide.getValue().size());
+    }
+
+    @Test
+    public void testNoNotificationsHiddenOnSuspendedPackageBroadcast() {
+        // post 2 notification from this package
+        final NotificationRecord notif1 = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, true);
+        final NotificationRecord notif2 = generateNotificationRecord(
+                mTestNotificationChannel, 2, null, false);
+        mService.addNotification(notif1);
+        mService.addNotification(notif2);
+
+        // on broadcast, nothing is hidden since no notifications are of package "test_package"
+        mService.simulatePackageSuspendBroadcast(true, "test_package");
+        ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+        verify(mListeners, times(1)).notifyHiddenLocked(captor.capture());
+        assertEquals(0, captor.getValue().size());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
index 4bfb236..c4a688b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
@@ -20,18 +20,27 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.app.Notification;
+import android.app.Notification.Person;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
 import android.os.Build;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.widget.RemoteViews;
 
 import com.android.server.UiServiceTestCase;
 
@@ -112,5 +121,272 @@
         assertEquals(Color.RED, new Notification.CarExtender(before).getColor());
         assertEquals("dismiss", new Notification.WearableExtender(before).getDismissalId());
     }
+
+    @Test
+    public void testStyleChangeVisiblyDifferent_noStyles() {
+        Notification.Builder n1 = new Notification.Builder(mContext, "test");
+        Notification.Builder n2 = new Notification.Builder(mContext, "test");
+
+        assertFalse(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testStyleChangeVisiblyDifferent_noStyleToStyle() {
+        Notification.Builder n1 = new Notification.Builder(mContext, "test");
+        Notification.Builder n2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle());
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testStyleChangeVisiblyDifferent_styleToNoStyle() {
+        Notification.Builder n2 = new Notification.Builder(mContext, "test");
+        Notification.Builder n1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle());
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testStyleChangeVisiblyDifferent_changeStyle() {
+        Notification.Builder n1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.InboxStyle());
+        Notification.Builder n2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle());
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testInboxTextChange() {
+        Notification.Builder nInbox1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.InboxStyle().addLine("a").addLine("b"));
+        Notification.Builder nInbox2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.InboxStyle().addLine("b").addLine("c"));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nInbox1, nInbox2));
+    }
+
+    @Test
+    public void testBigTextTextChange() {
+        Notification.Builder nBigText1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle().bigText("something"));
+        Notification.Builder nBigText2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle().bigText("else"));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nBigText1, nBigText2));
+    }
+
+    @Test
+    public void testBigPictureChange() {
+        Notification.Builder nBigPic1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigPictureStyle().bigPicture(mock(Bitmap.class)));
+        Notification.Builder nBigPic2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigPictureStyle().bigPicture(mock(Bitmap.class)));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nBigPic1, nBigPic2));
+    }
+
+    @Test
+    public void testMessagingChange_text() {
+        Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 100, mock(Notification.Person.class))));
+        Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 100, mock(Notification.Person.class)))
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "b", 100, mock(Notification.Person.class)))
+                );
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+    }
+
+    @Test
+    public void testMessagingChange_data() {
+        Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 100, mock(Person.class))
+                                .setData("text", mock(Uri.class))));
+        Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 100, mock(Person.class))));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+    }
+
+    @Test
+    public void testMessagingChange_sender() {
+        Person a = mock(Person.class);
+        when(a.getName()).thenReturn("A");
+        Person b = mock(Person.class);
+        when(b.getName()).thenReturn("b");
+        Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message("a", 100, b)));
+        Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message("a", 100, a)));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+    }
+
+    @Test
+    public void testMessagingChange_key() {
+        Person a = mock(Person.class);
+        when(a.getKey()).thenReturn("A");
+        Person b = mock(Person.class);
+        when(b.getKey()).thenReturn("b");
+        Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message("a", 100, a)));
+        Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message("a", 100, b)));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+    }
+
+    @Test
+    public void testMessagingChange_ignoreTimeChange() {
+        Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 100, mock(Notification.Person.class))));
+        Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 1000, mock(Notification.Person.class)))
+                );
+
+        assertFalse(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+    }
+
+    @Test
+    public void testRemoteViews_nullChange() {
+        Notification.Builder n1 = new Notification.Builder(mContext, "test")
+                .setContent(mock(RemoteViews.class));
+        Notification.Builder n2 = new Notification.Builder(mContext, "test");
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test");
+        n2 = new Notification.Builder(mContext, "test")
+                .setContent(mock(RemoteViews.class));
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test")
+                .setCustomBigContentView(mock(RemoteViews.class));
+        n2 = new Notification.Builder(mContext, "test");
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test");
+        n2 = new Notification.Builder(mContext, "test")
+                .setCustomBigContentView(mock(RemoteViews.class));
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test");
+        n2 = new Notification.Builder(mContext, "test");
+        assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferent_null() {
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .build();
+
+        assertFalse(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferentSame() {
+        PendingIntent intent = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+                .build();
+
+        assertFalse(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferentText() {
+        PendingIntent intent = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 2", intent).build())
+                .build();
+
+        assertTrue(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferentNumber() {
+        PendingIntent intent = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+                .addAction(new Notification.Action.Builder(icon, "TEXT 2", intent).build())
+                .build();
+
+        assertTrue(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferentIntent() {
+        PendingIntent intent1 = mock(PendingIntent.class);
+        PendingIntent intent2 = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent1).build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent2).build())
+                .build();
+
+        assertFalse(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferentRemoteInputs() {
+        PendingIntent intent = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent)
+                        .addRemoteInput(new RemoteInput.Builder("a")
+                                .setChoices(new CharSequence[] {"i", "m"})
+                                .build())
+                        .build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent)
+                        .addRemoteInput(new RemoteInput.Builder("a")
+                                .setChoices(new CharSequence[] {"t", "m"})
+                                .build())
+                        .build())
+                .build();
+
+        assertTrue(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
 }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 4f446a9..1073a80 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -30,6 +30,7 @@
 
 import android.app.AppOpsManager;
 import android.app.slice.SliceSpec;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.pm.PackageManagerInternal;
 import android.net.Uri;
 import android.os.Binder;
@@ -66,6 +67,8 @@
     @Before
     public void setup() {
         LocalServices.addService(PackageManagerInternal.class, mock(PackageManagerInternal.class));
+        LocalServices.addService(UsageStatsManagerInternal.class,
+                mock(UsageStatsManagerInternal.class));
         mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
         mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED);
 
@@ -77,6 +80,7 @@
     @After
     public void teardown() {
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
     }
 
     @Test
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index e836677..1af5f46 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -36,6 +36,7 @@
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+
 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
 
@@ -43,8 +44,8 @@
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.usage.AppStandbyInfo;
-import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
@@ -100,7 +101,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
@@ -690,7 +690,9 @@
                     || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
                     || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
                     || event.mEventType == UsageEvents.Event.USER_INTERACTION
-                    || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN)) {
+                    || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
+                    || event.mEventType == UsageEvents.Event.SLICE_PINNED
+                    || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV)) {
 
                 final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
                         event.mPackage, userId, elapsedRealtime);
@@ -699,7 +701,8 @@
                 final long nextCheckTime;
                 final int subReason = usageEventToSubReason(event.mEventType);
                 final int reason = REASON_MAIN_USAGE | subReason;
-                if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
+                if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
+                        || event.mEventType == UsageEvents.Event.SLICE_PINNED) {
                     // Mild usage elevates to WORKING_SET but doesn't change usage time.
                     mAppIdleHistory.reportUsage(appHistory, event.mPackage,
                             STANDBY_BUCKET_WORKING_SET, subReason,
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index d974282..c2e38f2 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -761,6 +761,10 @@
                 return "STANDBY_BUCKET_CHANGED";
             case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
                 return "NOTIFICATION_INTERRUPTION";
+            case UsageEvents.Event.SLICE_PINNED:
+                return "SLICE_PINNED";
+            case UsageEvents.Event.SLICE_PINNED_PRIV:
+                return "SLICE_PINNED_PRIV";
             default:
                 return "UNKNOWN";
         }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index cd3fdee..1160943 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -15,36 +15,68 @@
  */
 
 package com.android.server.soundtrigger;
+
+import static android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE;
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.GET_SERVICES;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
 import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK;
+import static android.provider.Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY;
+import static android.provider.Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
-import android.Manifest;
+import android.content.pm.ResolveInfo;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.SoundTrigger;
-import android.hardware.soundtrigger.SoundTrigger.SoundModel;
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.hardware.soundtrigger.SoundTrigger.SoundModel;
+import android.media.soundtrigger.ISoundTriggerDetectionService;
+import android.media.soundtrigger.ISoundTriggerDetectionServiceClient;
+import android.media.soundtrigger.SoundTriggerDetectionService;
 import android.media.soundtrigger.SoundTriggerManager;
+import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 
-import com.android.server.SystemService;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.ISoundTriggerService;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.TreeMap;
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A single SystemService to manage all sound/voice-based sound models on the DSP.
@@ -67,9 +99,13 @@
     private SoundTriggerHelper mSoundTriggerHelper;
     private final TreeMap<UUID, SoundModel> mLoadedModels;
     private Object mCallbacksLock;
-    private final TreeMap<UUID, LocalSoundTriggerRecognitionStatusCallback> mIntentCallbacks;
+    private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks;
     private PowerManager.WakeLock mWakelock;
 
+    /** Number of ops run by the {@link RemoteSoundTriggerDetectionService} per package name */
+    @GuardedBy("mLock")
+    private final ArrayMap<String, NumOps> mNumOpsPerPackage = new ArrayMap<>();
+
     public SoundTriggerService(Context context) {
         super(context);
         mContext = context;
@@ -77,7 +113,7 @@
         mLocalSoundTriggerService = new LocalSoundTriggerService(context);
         mLoadedModels = new TreeMap<UUID, SoundModel>();
         mCallbacksLock = new Object();
-        mIntentCallbacks = new TreeMap<UUID, LocalSoundTriggerRecognitionStatusCallback>();
+        mCallbacks = new TreeMap<>();
         mLock = new Object();
     }
 
@@ -214,7 +250,7 @@
                 if (oldModel != null && !oldModel.equals(soundModel)) {
                     mSoundTriggerHelper.unloadGenericSoundModel(soundModel.uuid);
                     synchronized (mCallbacksLock) {
-                        mIntentCallbacks.remove(soundModel.uuid);
+                        mCallbacks.remove(soundModel.uuid);
                     }
                 }
                 mLoadedModels.put(soundModel.uuid, soundModel);
@@ -245,7 +281,7 @@
                 if (oldModel != null && !oldModel.equals(soundModel)) {
                     mSoundTriggerHelper.unloadKeyphraseSoundModel(soundModel.keyphrases[0].id);
                     synchronized (mCallbacksLock) {
-                        mIntentCallbacks.remove(soundModel.uuid);
+                        mCallbacks.remove(soundModel.uuid);
                     }
                 }
                 mLoadedModels.put(soundModel.uuid, soundModel);
@@ -254,8 +290,28 @@
         }
 
         @Override
+        public int startRecognitionForService(ParcelUuid soundModelId, Bundle params,
+            ComponentName detectionService, SoundTrigger.RecognitionConfig config) {
+            Preconditions.checkNotNull(soundModelId);
+            Preconditions.checkNotNull(detectionService);
+            Preconditions.checkNotNull(config);
+
+            return startRecognitionForInt(soundModelId,
+                new RemoteSoundTriggerDetectionService(soundModelId.getUuid(),
+                    params, detectionService, Binder.getCallingUserHandle(), config), config);
+
+        }
+
+        @Override
         public int startRecognitionForIntent(ParcelUuid soundModelId, PendingIntent callbackIntent,
                 SoundTrigger.RecognitionConfig config) {
+            return startRecognitionForInt(soundModelId,
+                new LocalSoundTriggerRecognitionStatusIntentCallback(soundModelId.getUuid(),
+                    callbackIntent, config), config);
+        }
+
+        private int startRecognitionForInt(ParcelUuid soundModelId,
+            IRecognitionStatusCallback callback, SoundTrigger.RecognitionConfig config) {
             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
             if (!isInitialized()) return STATUS_ERROR;
             if (DEBUG) {
@@ -268,27 +324,25 @@
                     Slog.e(TAG, soundModelId + " is not loaded");
                     return STATUS_ERROR;
                 }
-                LocalSoundTriggerRecognitionStatusCallback callback = null;
+                IRecognitionStatusCallback existingCallback = null;
                 synchronized (mCallbacksLock) {
-                    callback = mIntentCallbacks.get(soundModelId.getUuid());
+                    existingCallback = mCallbacks.get(soundModelId.getUuid());
                 }
-                if (callback != null) {
+                if (existingCallback != null) {
                     Slog.e(TAG, soundModelId + " is already running");
                     return STATUS_ERROR;
                 }
-                callback = new LocalSoundTriggerRecognitionStatusCallback(soundModelId.getUuid(),
-                        callbackIntent, config);
                 int ret;
                 switch (soundModel.type) {
                     case SoundModel.TYPE_KEYPHRASE: {
                         KeyphraseSoundModel keyphraseSoundModel = (KeyphraseSoundModel) soundModel;
                         ret = mSoundTriggerHelper.startKeyphraseRecognition(
-                                keyphraseSoundModel.keyphrases[0].id, keyphraseSoundModel, callback,
-                                config);
+                            keyphraseSoundModel.keyphrases[0].id, keyphraseSoundModel, callback,
+                            config);
                     } break;
                     case SoundModel.TYPE_GENERIC_SOUND:
                         ret = mSoundTriggerHelper.startGenericRecognition(soundModel.uuid,
-                                (GenericSoundModel) soundModel, callback, config);
+                            (GenericSoundModel) soundModel, callback, config);
                         break;
                     default:
                         Slog.e(TAG, "Unknown model type");
@@ -300,7 +354,7 @@
                     return ret;
                 }
                 synchronized (mCallbacksLock) {
-                    mIntentCallbacks.put(soundModelId.getUuid(), callback);
+                    mCallbacks.put(soundModelId.getUuid(), callback);
                 }
             }
             return STATUS_OK;
@@ -320,9 +374,9 @@
                     Slog.e(TAG, soundModelId + " is not loaded");
                     return STATUS_ERROR;
                 }
-                LocalSoundTriggerRecognitionStatusCallback callback = null;
+                IRecognitionStatusCallback callback = null;
                 synchronized (mCallbacksLock) {
-                     callback = mIntentCallbacks.get(soundModelId.getUuid());
+                     callback = mCallbacks.get(soundModelId.getUuid());
                 }
                 if (callback == null) {
                     Slog.e(TAG, soundModelId + " is not running");
@@ -347,7 +401,7 @@
                     return ret;
                 }
                 synchronized (mCallbacksLock) {
-                    mIntentCallbacks.remove(soundModelId.getUuid());
+                    mCallbacks.remove(soundModelId.getUuid());
                 }
             }
             return STATUS_OK;
@@ -394,8 +448,7 @@
             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
             if (!isInitialized()) return false;
             synchronized (mCallbacksLock) {
-                LocalSoundTriggerRecognitionStatusCallback callback =
-                        mIntentCallbacks.get(parcelUuid.getUuid());
+                IRecognitionStatusCallback callback = mCallbacks.get(parcelUuid.getUuid());
                 if (callback == null) {
                     return false;
                 }
@@ -404,13 +457,13 @@
         }
     }
 
-    private final class LocalSoundTriggerRecognitionStatusCallback
+    private final class LocalSoundTriggerRecognitionStatusIntentCallback
             extends IRecognitionStatusCallback.Stub {
         private UUID mUuid;
         private PendingIntent mCallbackIntent;
         private RecognitionConfig mRecognitionConfig;
 
-        public LocalSoundTriggerRecognitionStatusCallback(UUID modelUuid,
+        public LocalSoundTriggerRecognitionStatusIntentCallback(UUID modelUuid,
                 PendingIntent callbackIntent,
                 RecognitionConfig config) {
             mUuid = modelUuid;
@@ -528,7 +581,7 @@
         private void removeCallback(boolean releaseWakeLock) {
             mCallbackIntent = null;
             synchronized (mCallbacksLock) {
-                mIntentCallbacks.remove(mUuid);
+                mCallbacks.remove(mUuid);
                 if (releaseWakeLock) {
                     mWakelock.release();
                 }
@@ -536,6 +589,478 @@
         }
     }
 
+    /**
+     * Counts the number of operations added in the last 24 hours.
+     */
+    private static class NumOps {
+        private final Object mLock = new Object();
+
+        @GuardedBy("mLock")
+        private int[] mNumOps = new int[24];
+        @GuardedBy("mLock")
+        private long mLastOpsHourSinceBoot;
+
+        /**
+         * Clear buckets of new hours that have elapsed since last operation.
+         *
+         * <p>I.e. when the last operation was triggered at 1:40 and the current operation was
+         * triggered at 4:03, the buckets "2, 3, and 4" are cleared.
+         *
+         * @param currentTime Current elapsed time since boot in ns
+         */
+        void clearOldOps(long currentTime) {
+            synchronized (mLock) {
+                long numHoursSinceBoot = TimeUnit.HOURS.convert(currentTime, TimeUnit.NANOSECONDS);
+
+                // Clear buckets of new hours that have elapsed since last operation
+                // I.e. when the last operation was triggered at 1:40 and the current
+                // operation was triggered at 4:03, the bucket "2, 3, and 4" is cleared
+                if (mLastOpsHourSinceBoot != 0) {
+                    for (long hour = mLastOpsHourSinceBoot + 1; hour <= numHoursSinceBoot; hour++) {
+                        mNumOps[(int) (hour % 24)] = 0;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Add a new operation.
+         *
+         * @param currentTime Current elapsed time since boot in ns
+         */
+        void addOp(long currentTime) {
+            synchronized (mLock) {
+                long numHoursSinceBoot = TimeUnit.HOURS.convert(currentTime, TimeUnit.NANOSECONDS);
+
+                mNumOps[(int) (numHoursSinceBoot % 24)]++;
+                mLastOpsHourSinceBoot = numHoursSinceBoot;
+            }
+        }
+
+        /**
+         * Get the total operations added in the last 24 hours.
+         *
+         * @return The total number of operations added in the last 24 hours
+         */
+        int getOpsAdded() {
+            synchronized (mLock) {
+                int totalOperationsInLastDay = 0;
+                for (int i = 0; i < 24; i++) {
+                    totalOperationsInLastDay += mNumOps[i];
+                }
+
+                return totalOperationsInLastDay;
+            }
+        }
+    }
+
+    private interface Operation {
+        void run(int opId, ISoundTriggerDetectionService service) throws RemoteException;
+    }
+
+    /**
+     * Local end for a {@link SoundTriggerDetectionService}. Operations are queued up and executed
+     * when the service connects.
+     *
+     * <p>If operations take too long they are forcefully aborted.
+     *
+     * <p>This also limits the amount of operations in 24 hours.
+     */
+    private class RemoteSoundTriggerDetectionService
+        extends IRecognitionStatusCallback.Stub implements ServiceConnection {
+        private static final int MSG_STOP_ALL_PENDING_OPERATIONS = 1;
+
+        private final Object mRemoteServiceLock = new Object();
+
+        /** UUID of the model the service is started for */
+        private final @NonNull ParcelUuid mPuuid;
+        /** Params passed into the start method for the service */
+        private final @Nullable Bundle mParams;
+        /** Component name passed when starting the service */
+        private final @NonNull ComponentName mServiceName;
+        /** User that started the service */
+        private final @NonNull UserHandle mUser;
+        /** Configuration of the recognition the service is handling */
+        private final @NonNull RecognitionConfig mRecognitionConfig;
+        /** Wake lock keeping the remote service alive */
+        private final @NonNull PowerManager.WakeLock mRemoteServiceWakeLock;
+
+        private final @NonNull Handler mHandler;
+
+        /** Callbacks that are called by the service */
+        private final @NonNull ISoundTriggerDetectionServiceClient mClient;
+
+        /** Operations that are pending because the service is not yet connected */
+        @GuardedBy("mRemoteServiceLock")
+        private final ArrayList<Operation> mPendingOps = new ArrayList<>();
+        /** Operations that have been send to the service but have no yet finished */
+        @GuardedBy("mRemoteServiceLock")
+        private final ArraySet<Integer> mRunningOpIds = new ArraySet<>();
+        /** The number of operations executed in each of the last 24 hours */
+        private final NumOps mNumOps;
+
+        /** The service binder if connected */
+        @GuardedBy("mRemoteServiceLock")
+        private @Nullable ISoundTriggerDetectionService mService;
+        /** Whether the service has been bound */
+        @GuardedBy("mRemoteServiceLock")
+        private boolean mIsBound;
+        /** Whether the service has been destroyed */
+        @GuardedBy("mRemoteServiceLock")
+        private boolean mIsDestroyed;
+        /**
+         * Set once a final op is scheduled. No further ops can be added and the service is
+         * destroyed once the op finishes.
+         */
+        @GuardedBy("mRemoteServiceLock")
+        private boolean mDestroyOnceRunningOpsDone;
+
+        /** Total number of operations performed by this service */
+        @GuardedBy("mRemoteServiceLock")
+        private int mNumTotalOpsPerformed;
+
+        /**
+         * Create a new remote sound trigger detection service. This only binds to the service when
+         * operations are in flight. Each operation has a certain time it can run. Once no
+         * operations are allowed to run anymore, {@link #stopAllPendingOperations() all operations
+         * are aborted and stopped} and the service is disconnected.
+         *
+         * @param modelUuid The UUID of the model the recognition is for
+         * @param params The params passed to each method of the service
+         * @param serviceName The component name of the service
+         * @param user The user of the service
+         * @param config The configuration of the recognition
+         */
+        public RemoteSoundTriggerDetectionService(@NonNull UUID modelUuid,
+            @Nullable Bundle params, @NonNull ComponentName serviceName, @NonNull UserHandle user,
+            @NonNull RecognitionConfig config) {
+            mPuuid = new ParcelUuid(modelUuid);
+            mParams = params;
+            mServiceName = serviceName;
+            mUser = user;
+            mRecognitionConfig = config;
+            mHandler = new Handler(Looper.getMainLooper());
+
+            PowerManager pm = ((PowerManager) mContext.getSystemService(Context.POWER_SERVICE));
+            mRemoteServiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    "RemoteSoundTriggerDetectionService " + mServiceName.getPackageName() + ":"
+                            + mServiceName.getClassName());
+
+            synchronized (mLock) {
+                NumOps numOps = mNumOpsPerPackage.get(mServiceName.getPackageName());
+                if (numOps == null) {
+                    numOps = new NumOps();
+                    mNumOpsPerPackage.put(mServiceName.getPackageName(), numOps);
+                }
+                mNumOps = numOps;
+            }
+
+            mClient = new ISoundTriggerDetectionServiceClient.Stub() {
+                @Override
+                public void onOpFinished(int opId) {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        synchronized (mRemoteServiceLock) {
+                            mRunningOpIds.remove(opId);
+
+                            if (mRunningOpIds.isEmpty() && mPendingOps.isEmpty()) {
+                                if (mDestroyOnceRunningOpsDone) {
+                                    destroy();
+                                } else {
+                                    disconnectLocked();
+                                }
+                            }
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            };
+        }
+
+        @Override
+        public boolean pingBinder() {
+            return !(mIsDestroyed || mDestroyOnceRunningOpsDone);
+        }
+
+        /**
+         * Disconnect from the service, but allow to re-connect when new operations are triggered.
+         */
+        private void disconnectLocked() {
+            if (mService != null) {
+                try {
+                    mService.removeClient(mPuuid);
+                } catch (Exception e) {
+                    Slog.e(TAG, mPuuid + ": Cannot remove client", e);
+                }
+
+                mService = null;
+            }
+
+            if (mIsBound) {
+                mContext.unbindService(RemoteSoundTriggerDetectionService.this);
+                mIsBound = false;
+
+                synchronized (mCallbacksLock) {
+                    mRemoteServiceWakeLock.release();
+                }
+            }
+        }
+
+        /**
+         * Disconnect, do not allow to reconnect to the service. All further operations will be
+         * dropped.
+         */
+        private void destroy() {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": destroy");
+
+            synchronized (mRemoteServiceLock) {
+                disconnectLocked();
+
+                mIsDestroyed = true;
+            }
+
+            // The callback is removed before the flag is set
+            if (!mDestroyOnceRunningOpsDone) {
+                synchronized (mCallbacksLock) {
+                    mCallbacks.remove(mPuuid.getUuid());
+                }
+            }
+        }
+
+        /**
+         * Stop all pending operations and then disconnect for the service.
+         */
+        private void stopAllPendingOperations() {
+            synchronized (mRemoteServiceLock) {
+                if (mIsDestroyed) {
+                    return;
+                }
+
+                if (mService != null) {
+                    int numOps = mRunningOpIds.size();
+                    for (int i = 0; i < numOps; i++) {
+                        try {
+                            mService.onStopOperation(mPuuid, mRunningOpIds.valueAt(i));
+                        } catch (Exception e) {
+                            Slog.e(TAG, mPuuid + ": Could not stop operation "
+                                    + mRunningOpIds.valueAt(i), e);
+                        }
+                    }
+
+                    mRunningOpIds.clear();
+                }
+
+                disconnectLocked();
+            }
+        }
+
+        /**
+         * Verify that the service has the expected properties and then bind to the service
+         */
+        private void bind() {
+            long token = Binder.clearCallingIdentity();
+            try {
+                Intent i = new Intent();
+                i.setComponent(mServiceName);
+
+                ResolveInfo ri = mContext.getPackageManager().resolveServiceAsUser(i,
+                        GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING,
+                        mUser.getIdentifier());
+
+                if (ri == null) {
+                    Slog.w(TAG, mPuuid + ": " + mServiceName + " not found");
+                    return;
+                }
+
+                if (!BIND_SOUND_TRIGGER_DETECTION_SERVICE
+                        .equals(ri.serviceInfo.permission)) {
+                    Slog.w(TAG, mPuuid + ": " + mServiceName + " does not require "
+                            + BIND_SOUND_TRIGGER_DETECTION_SERVICE);
+                    return;
+                }
+
+                mIsBound = mContext.bindServiceAsUser(i, this,
+                        BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE, mUser);
+
+                if (mIsBound) {
+                    mRemoteServiceWakeLock.acquire();
+                } else {
+                    Slog.w(TAG, mPuuid + ": Could not bind to " + mServiceName);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * Run an operation (i.e. send it do the service). If the service is not connected, this
+         * binds the service and then runs the operation once connected.
+         *
+         * @param op The operation to run
+         */
+        private void runOrAddOperation(Operation op) {
+            synchronized (mRemoteServiceLock) {
+                if (mIsDestroyed || mDestroyOnceRunningOpsDone) {
+                    return;
+                }
+
+                if (mService == null) {
+                    mPendingOps.add(op);
+
+                    if (!mIsBound) {
+                        bind();
+                    }
+                } else {
+                    long currentTime = System.nanoTime();
+                    mNumOps.clearOldOps(currentTime);
+
+                    // Drop operation if too many were executed in the last 24 hours.
+                    int opsAllowed = Settings.Global.getInt(mContext.getContentResolver(),
+                            MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
+                            Integer.MAX_VALUE);
+
+                    int opsAdded = mNumOps.getOpsAdded();
+                    if (mNumOps.getOpsAdded() >= opsAllowed) {
+                        if (DEBUG || opsAllowed + 10 > opsAdded) {
+                            Slog.w(TAG, mPuuid + ": Dropped operation as too many operations were "
+                                    + "run in last 24 hours");
+                        }
+                        return;
+                    }
+
+                    mNumOps.addOp(currentTime);
+
+                    // Find a free opID
+                    int opId = mNumTotalOpsPerformed;
+                    do {
+                        mNumTotalOpsPerformed++;
+                    } while (mRunningOpIds.contains(opId));
+
+                    // Run OP
+                    try {
+                        if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId);
+
+                        op.run(opId, mService);
+                        mRunningOpIds.add(opId);
+                    } catch (Exception e) {
+                        Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e);
+                    }
+
+                    // Unbind from service if no operations are left (i.e. if the operation failed)
+                    if (mPendingOps.isEmpty() && mRunningOpIds.isEmpty()) {
+                        if (mDestroyOnceRunningOpsDone) {
+                            destroy();
+                        } else {
+                            disconnectLocked();
+                        }
+                    } else {
+                        mHandler.removeMessages(MSG_STOP_ALL_PENDING_OPERATIONS);
+                        mHandler.sendMessageDelayed(obtainMessage(
+                                RemoteSoundTriggerDetectionService::stopAllPendingOperations, this)
+                                        .setWhat(MSG_STOP_ALL_PENDING_OPERATIONS),
+                                Settings.Global.getLong(mContext.getContentResolver(),
+                                        SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
+                                        Long.MAX_VALUE));
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onKeyphraseDetected(SoundTrigger.KeyphraseRecognitionEvent event) {
+            Slog.w(TAG, mPuuid + "->" + mServiceName + ": IGNORED onKeyphraseDetected(" + event
+                    + ")");
+        }
+
+        @Override
+        public void onGenericSoundTriggerDetected(SoundTrigger.GenericRecognitionEvent event) {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": Generic sound trigger event: " + event);
+
+            runOrAddOperation((opId, service) -> {
+                if (!mRecognitionConfig.allowMultipleTriggers) {
+                    synchronized (mCallbacksLock) {
+                        mCallbacks.remove(mPuuid.getUuid());
+                    }
+                    mDestroyOnceRunningOpsDone = true;
+                }
+
+                service.onGenericRecognitionEvent(mPuuid, opId, event);
+            });
+        }
+
+        @Override
+        public void onError(int status) {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": onError: " + status);
+
+            runOrAddOperation((opId, service) -> {
+                synchronized (mCallbacksLock) {
+                    mCallbacks.remove(mPuuid.getUuid());
+                }
+                mDestroyOnceRunningOpsDone = true;
+
+                service.onError(mPuuid, opId, status);
+            });
+        }
+
+        @Override
+        public void onRecognitionPaused() {
+            Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionPaused");
+        }
+
+        @Override
+        public void onRecognitionResumed() {
+            Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionResumed");
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceConnected(" + service + ")");
+
+            synchronized (mRemoteServiceLock) {
+                mService = ISoundTriggerDetectionService.Stub.asInterface(service);
+
+                try {
+                    mService.setClient(mPuuid, mParams, mClient);
+                } catch (Exception e) {
+                    Slog.e(TAG, mPuuid + ": Could not init " + mServiceName, e);
+                    return;
+                }
+
+                while (!mPendingOps.isEmpty()) {
+                    runOrAddOperation(mPendingOps.remove(0));
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceDisconnected");
+
+            synchronized (mRemoteServiceLock) {
+                mService = null;
+            }
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": onBindingDied");
+
+            synchronized (mRemoteServiceLock) {
+                destroy();
+            }
+        }
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            Slog.w(TAG, name + " for model " + mPuuid + " returned a null binding");
+
+            synchronized (mRemoteServiceLock) {
+                disconnectLocked();
+            }
+        }
+    }
+
     private void grabWakeLock() {
         synchronized (mCallbacksLock) {
             if (mWakelock == null) {
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 105ddb0..713ac00 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -181,6 +182,7 @@
      * @return The long alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string). May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaLong() {
         return mAlphaLong;
     }
@@ -189,6 +191,7 @@
      * @return The short alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string).  May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaShort() {
         return mAlphaShort;
     }
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 52944a8a..aae7929 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -191,6 +192,7 @@
      * @return The long alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string). May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaLong() {
         return mAlphaLong;
     }
@@ -199,6 +201,7 @@
      * @return The short alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string).  May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaShort() {
         return mAlphaShort;
     }
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 37fb075..9b3ef56 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -201,6 +202,7 @@
      * @return The long alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string). May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaLong() {
         return mAlphaLong;
     }
@@ -209,6 +211,7 @@
      * @return The short alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string).  May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaShort() {
         return mAlphaShort;
     }
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 992545d..7475c74 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -34,6 +35,10 @@
     private final int mCid;
     // 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
     private final int mCpid;
+    // long alpha Operator Name String or Enhanced Operator Name String
+    private final String mAlphaLong;
+    // short alpha Operator Name String or Enhanced Operator Name String
+    private final String mAlphaShort;
 
     /**
      * @hide
@@ -43,6 +48,8 @@
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mCpid = Integer.MAX_VALUE;
+        mAlphaLong = null;
+        mAlphaShort = null;
     }
 
     /**
@@ -55,7 +62,7 @@
      * @hide
      */
     public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid) {
-        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid);
+        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, null, null);
     }
 
     /**
@@ -65,6 +72,7 @@
      * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
      * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
      *
+     * FIXME: This is a temporary constructor to facilitate migration.
      * @hide
      */
     public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
@@ -72,10 +80,34 @@
         mLac = lac;
         mCid = cid;
         mCpid = cpid;
+        mAlphaLong = null;
+        mAlphaShort = null;
+    }
+
+    /**
+     * @param mcc 3-digit Mobile Country Code in string format
+     * @param mnc 2 or 3-digit Mobile Network Code in string format
+     * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String
+     *
+     * @hide
+     */
+    public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid,
+            String alphal, String alphas) {
+        super(TAG, TYPE_TDSCDMA, mcc, mnc);
+        mLac = lac;
+        mCid = cid;
+        mCpid = cpid;
+        mAlphaLong = alphal;
+        mAlphaShort = alphas;
     }
 
     private CellIdentityTdscdma(CellIdentityTdscdma cid) {
-        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid, cid.mCpid);
+        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid,
+                cid.mCpid, cid.mAlphaLong, cid.mAlphaShort);
     }
 
     CellIdentityTdscdma copy() {
@@ -119,9 +151,31 @@
         return mCpid;
     }
 
+    /**
+     * @return The long alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string). May be null if unknown.
+     *
+     * @hide
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaLong() {
+        return mAlphaLong;
+    }
+
+    /**
+     * @return The short alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string).  May be null if unknown.
+     *
+     * @hide
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaShort() {
+        return mAlphaShort;
+    }
+
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid);
+        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid, mAlphaLong, mAlphaShort);
     }
 
     @Override
@@ -139,7 +193,9 @@
                 && TextUtils.equals(mMncStr, o.mMncStr)
                 && mLac == o.mLac
                 && mCid == o.mCid
-                && mCpid == o.mCpid;
+                && mCpid == o.mCpid
+                && mAlphaLong == o.mAlphaLong
+                && mAlphaShort == o.mAlphaShort;
     }
 
     @Override
@@ -150,6 +206,8 @@
         .append(" mLac=").append(mLac)
         .append(" mCid=").append(mCid)
         .append(" mCpid=").append(mCpid)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
         .append("}").toString();
     }
 
@@ -161,6 +219,8 @@
         dest.writeInt(mLac);
         dest.writeInt(mCid);
         dest.writeInt(mCpid);
+        dest.writeString(mAlphaLong);
+        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -169,6 +229,8 @@
         mLac = in.readInt();
         mCid = in.readInt();
         mCpid = in.readInt();
+        mAlphaLong = in.readString();
+        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index affa0c1..52fa54f 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -182,6 +183,7 @@
      * @return The long alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string). May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaLong() {
         return mAlphaLong;
     }
@@ -190,6 +192,7 @@
      * @return The short alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string).  May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaShort() {
         return mAlphaShort;
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7dff667..0b15191 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1455,7 +1455,6 @@
      * {@hide}
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public int getCurrentPhoneType() {
         return getCurrentPhoneType(getSubId());
     }
@@ -1471,17 +1470,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public int getCurrentPhoneType(int subId) {
-        return getCurrentPhoneType(subId, false);
-    }
-
-    /**
-     * getCurrentPhoneType() with optional check if device is voice capable.
-     *
-     * @hide
-     */
-    public int getCurrentPhoneType(int subId, boolean checkIsVoiceCapable) {
         int phoneId;
         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
             // if we don't have any sims, we don't have subscriptions, but we
@@ -1490,7 +1479,8 @@
         } else {
             phoneId = SubscriptionManager.getPhoneId(subId);
         }
-        return getCurrentPhoneTypeForSlot(phoneId, checkIsVoiceCapable);
+
+        return getCurrentPhoneTypeForSlot(phoneId);
     }
 
     /**
@@ -1498,15 +1488,11 @@
      *
      * @hide
      */
-    public int getCurrentPhoneTypeForSlot(int slotIndex, boolean checkIsVoiceCapable) {
+    public int getCurrentPhoneTypeForSlot(int slotIndex) {
         try{
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                if (checkIsVoiceCapable) {
-                    return telephony.getVoiceCapableActivePhoneTypeForSlot(slotIndex);
-                } else {
-                    return telephony.getActivePhoneTypeForSlot(slotIndex);
-                }
+                return telephony.getActivePhoneTypeForSlot(slotIndex);
             } else {
                 // This can happen when the ITelephony interface is not up yet.
                 return getPhoneTypeFromProperty(slotIndex);
@@ -1532,7 +1518,10 @@
      * @see #PHONE_TYPE_SIP
      */
     public int getPhoneType() {
-        return getCurrentPhoneType(getSubId(), true);
+        if (!isVoiceCapable()) {
+            return PHONE_TYPE_NONE;
+        }
+        return getCurrentPhoneType();
     }
 
     private int getPhoneTypeFromProperty() {
@@ -5469,7 +5458,10 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param request Contains all the RAT with bands/channels that need to be scanned.
-     * @param executor The executor through which the callback should be invoked.
+     * @param executor The executor through which the callback should be invoked. Since the scan
+     *        request may trigger multiple callbacks and they must be invoked in the same order as
+     *        they are received by the platform, the user should provide an executor which executes
+     *        tasks one at a time in serial order. For example AsyncTask.SERIAL_EXECUTOR.
      * @param callback Returns network scan results or errors.
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
      */
@@ -5495,7 +5487,7 @@
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public NetworkScan requestNetworkScan(
         NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
-        return requestNetworkScan(request, AsyncTask.THREAD_POOL_EXECUTOR, callback);
+        return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
     }
 
     /**
@@ -5842,14 +5834,12 @@
 
     /** @hide */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public List<String> getCarrierPackageNamesForIntent(Intent intent) {
         return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
     }
 
     /** @hide */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) {
         try {
             ITelephony telephony = getITelephony();
@@ -5959,11 +5949,7 @@
         }
     }
 
-    /**
-     * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
-     * @hide
-     */
-    @Deprecated
+    /** @hide */
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -5980,11 +5966,7 @@
         return false;
     }
 
-    /**
-     * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead
-     * @hide
-     */
-    @Deprecated
+    /** @hide */
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -6001,11 +5983,7 @@
         return false;
     }
 
-    /**
-     * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
-     * @hide
-     */
-    @Deprecated
+    /** @hide */
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -6022,11 +6000,7 @@
         return true;
     }
 
-    /**
-     * @deprecated Use {@link android.telephony.TelephonyManager#getServiceState} instead
-     * @hide
-     */
-    @Deprecated
+    /** @hide */
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -6317,7 +6291,6 @@
 
     /** @hide */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isDataConnectivityPossible() {
         try {
             ITelephony telephony = getITelephony();
@@ -6332,7 +6305,6 @@
 
     /** @hide */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean needsOtaServiceProvisioning() {
         try {
             ITelephony telephony = getITelephony();
@@ -6435,7 +6407,10 @@
 
     /** @hide */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE
+    })
     public boolean isVideoCallingEnabled() {
         try {
             ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 99e2db8..96ff332 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -137,8 +137,10 @@
                             for (int i = 0; i < parcelables.length; i++) {
                                 ci[i] = (CellInfo) parcelables[i];
                             }
-                            executor.execute(() ->
-                                    callback.onResults((List<CellInfo>) Arrays.asList(ci)));
+                            executor.execute(() ->{
+                                Rlog.d(TAG, "onResults: " + ci.toString());
+                                callback.onResults((List<CellInfo>) Arrays.asList(ci));
+                            });
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onResults", e);
                         }
@@ -146,14 +148,20 @@
                     case CALLBACK_SCAN_ERROR:
                         try {
                             final int errorCode = message.arg1;
-                            executor.execute(() -> callback.onError(errorCode));
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onError: " + errorCode);
+                                callback.onError(errorCode);
+                            });
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onError", e);
                         }
                         break;
                     case CALLBACK_SCAN_COMPLETE:
                         try {
-                            executor.execute(() -> callback.onComplete());
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onComplete");
+                                callback.onComplete();
+                            });
                             mScanInfo.remove(message.arg2);
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 73a05af..145ed7e 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -17,10 +17,10 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.StringDef;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.hardware.radio.V1_0.ApnTypes;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Telephony;
@@ -28,17 +28,15 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -46,25 +44,184 @@
  */
 public class ApnSetting implements Parcelable {
 
-    static final String LOG_TAG = "ApnSetting";
+    private static final String LOG_TAG = "ApnSetting";
     private static final boolean VDBG = false;
 
+    private static final Map<String, Integer> APN_TYPE_STRING_MAP;
+    private static final Map<Integer, String> APN_TYPE_INT_MAP;
+    private static final Map<String, Integer> PROTOCOL_STRING_MAP;
+    private static final Map<Integer, String> PROTOCOL_INT_MAP;
+    private static final Map<String, Integer> MVNO_TYPE_STRING_MAP;
+    private static final Map<Integer, String> MVNO_TYPE_INT_MAP;
+    private static final int NOT_IN_MAP_INT = -1;
+    private static final int NO_PORT_SPECIFIED = -1;
+
+    /** All APN types except IA. */
+    private static final int TYPE_ALL_BUT_IA = ApnTypes.ALL & (~ApnTypes.IA);
+
+    /** APN type for default data traffic and HiPri traffic. */
+    public static final int TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI;
+    /** APN type for MMS traffic. */
+    public static final int TYPE_MMS = ApnTypes.MMS;
+    /** APN type for SUPL assisted GPS. */
+    public static final int TYPE_SUPL = ApnTypes.SUPL;
+    /** APN type for DUN traffic. */
+    public static final int TYPE_DUN = ApnTypes.DUN;
+    /** APN type for HiPri traffic. */
+    public static final int TYPE_HIPRI = ApnTypes.HIPRI;
+    /** APN type for accessing the carrier's FOTA portal, used for over the air updates. */
+    public static final int TYPE_FOTA = ApnTypes.FOTA;
+    /** APN type for IMS. */
+    public static final int TYPE_IMS = ApnTypes.IMS;
+    /** APN type for CBS. */
+    public static final int TYPE_CBS = ApnTypes.CBS;
+    /** APN type for IA Initial Attach APN. */
+    public static final int TYPE_IA = ApnTypes.IA;
+    /**
+     * APN type for Emergency PDN. This is not an IA apn, but is used
+     * for access to carrier services in an emergency call situation.
+     */
+    public static final int TYPE_EMERGENCY = ApnTypes.EMERGENCY;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+        TYPE_DEFAULT,
+        TYPE_MMS,
+        TYPE_SUPL,
+        TYPE_DUN,
+        TYPE_HIPRI,
+        TYPE_FOTA,
+        TYPE_IMS,
+        TYPE_CBS,
+        TYPE_IA,
+        TYPE_EMERGENCY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApnType {}
+
+    // Possible values for authentication types.
+    /** No authentication type. */
+    public static final int AUTH_TYPE_NONE = 0;
+    /** Authentication type for PAP. */
+    public static final int AUTH_TYPE_PAP = 1;
+    /** Authentication type for CHAP. */
+    public static final int AUTH_TYPE_CHAP = 2;
+    /** Authentication type for PAP or CHAP. */
+    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "AUTH_TYPE_" }, value = {
+        AUTH_TYPE_NONE,
+        AUTH_TYPE_PAP,
+        AUTH_TYPE_CHAP,
+        AUTH_TYPE_PAP_OR_CHAP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AuthType {}
+
+    // Possible values for protocol.
+    /** Protocol type for IP. */
+    public static final int PROTOCOL_IP = 0;
+    /** Protocol type for IPV6. */
+    public static final int PROTOCOL_IPV6 = 1;
+    /** Protocol type for IPV4V6. */
+    public static final int PROTOCOL_IPV4V6 = 2;
+    /** Protocol type for PPP. */
+    public static final int PROTOCOL_PPP = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "PROTOCOL_" }, value = {
+        PROTOCOL_IP,
+        PROTOCOL_IPV6,
+        PROTOCOL_IPV4V6,
+        PROTOCOL_PPP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtocolType {}
+
+    // Possible values for MVNO type.
+    /** MVNO type for service provider name. */
+    public static final int MVNO_TYPE_SPN = 0;
+    /** MVNO type for IMSI. */
+    public static final int MVNO_TYPE_IMSI = 1;
+    /** MVNO type for group identifier level 1. */
+    public static final int MVNO_TYPE_GID = 2;
+    /** MVNO type for ICCID. */
+    public static final int MVNO_TYPE_ICCID = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "MVNO_TYPE_" }, value = {
+        MVNO_TYPE_SPN,
+        MVNO_TYPE_IMSI,
+        MVNO_TYPE_GID,
+        MVNO_TYPE_ICCID,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MvnoType {}
+
+    static {
+        APN_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        APN_TYPE_STRING_MAP.put("*", TYPE_ALL_BUT_IA);
+        APN_TYPE_STRING_MAP.put("default", TYPE_DEFAULT);
+        APN_TYPE_STRING_MAP.put("mms", TYPE_MMS);
+        APN_TYPE_STRING_MAP.put("supl", TYPE_SUPL);
+        APN_TYPE_STRING_MAP.put("dun", TYPE_DUN);
+        APN_TYPE_STRING_MAP.put("hipri", TYPE_HIPRI);
+        APN_TYPE_STRING_MAP.put("fota", TYPE_FOTA);
+        APN_TYPE_STRING_MAP.put("ims", TYPE_IMS);
+        APN_TYPE_STRING_MAP.put("cbs", TYPE_CBS);
+        APN_TYPE_STRING_MAP.put("ia", TYPE_IA);
+        APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY);
+        APN_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+        APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default");
+        APN_TYPE_INT_MAP.put(TYPE_MMS, "mms");
+        APN_TYPE_INT_MAP.put(TYPE_SUPL, "supl");
+        APN_TYPE_INT_MAP.put(TYPE_DUN, "dun");
+        APN_TYPE_INT_MAP.put(TYPE_HIPRI, "hipri");
+        APN_TYPE_INT_MAP.put(TYPE_FOTA, "fota");
+        APN_TYPE_INT_MAP.put(TYPE_IMS, "ims");
+        APN_TYPE_INT_MAP.put(TYPE_CBS, "cbs");
+        APN_TYPE_INT_MAP.put(TYPE_IA, "ia");
+        APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency");
+
+        PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>();
+        PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
+        PROTOCOL_STRING_MAP.put("IPV6", PROTOCOL_IPV6);
+        PROTOCOL_STRING_MAP.put("IPV4V6", PROTOCOL_IPV4V6);
+        PROTOCOL_STRING_MAP.put("PPP", PROTOCOL_PPP);
+        PROTOCOL_INT_MAP = new ArrayMap<Integer, String>();
+        PROTOCOL_INT_MAP.put(PROTOCOL_IP, "IP");
+        PROTOCOL_INT_MAP.put(PROTOCOL_IPV6, "IPV6");
+        PROTOCOL_INT_MAP.put(PROTOCOL_IPV4V6, "IPV4V6");
+        PROTOCOL_INT_MAP.put(PROTOCOL_PPP, "PPP");
+
+        MVNO_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        MVNO_TYPE_STRING_MAP.put("spn", MVNO_TYPE_SPN);
+        MVNO_TYPE_STRING_MAP.put("imsi", MVNO_TYPE_IMSI);
+        MVNO_TYPE_STRING_MAP.put("gid", MVNO_TYPE_GID);
+        MVNO_TYPE_STRING_MAP.put("iccid", MVNO_TYPE_ICCID);
+        MVNO_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_SPN, "spn");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_IMSI, "imsi");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_GID, "gid");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_ICCID, "iccid");
+    }
+
     private final String mEntryName;
     private final String mApnName;
-    private final InetAddress mProxy;
-    private final int mPort;
-    private final URL mMmsc;
-    private final InetAddress mMmsProxy;
-    private final int mMmsPort;
+    private final InetAddress mProxyAddress;
+    private final int mProxyPort;
+    private final Uri mMmsc;
+    private final InetAddress mMmsProxyAddress;
+    private final int mMmsProxyPort;
     private final String mUser;
     private final String mPassword;
     private final int mAuthType;
-    private final List<String> mTypes;
-    private final int mTypesBitmap;
+    private final int mApnTypeBitmask;
     private final int mId;
     private final String mOperatorNumeric;
-    private final String mProtocol;
-    private final String mRoamingProtocol;
+    private final int mProtocol;
+    private final int mRoamingProtocol;
     private final int mMtu;
 
     private final boolean mCarrierEnabled;
@@ -78,22 +235,12 @@
     private final int mWaitTime;
     private final int mMaxConnsTime;
 
-    private final String mMvnoType;
+    private final int mMvnoType;
     private final String mMvnoMatchData;
 
     private boolean mPermanentFailed = false;
 
     /**
-     * Returns the types bitmap of the APN.
-     *
-     * @return types bitmap of the APN
-     * @hide
-     */
-    public int getTypesBitmap() {
-        return mTypesBitmap;
-    }
-
-    /**
      * Returns the MTU size of the mobile interface to which the APN connected.
      *
      * @return the MTU size of the APN
@@ -211,8 +358,8 @@
      *
      * @return proxy address.
      */
-    public InetAddress getProxy() {
-        return mProxy;
+    public InetAddress getProxyAddress() {
+        return mProxyAddress;
     }
 
     /**
@@ -220,15 +367,15 @@
      *
      * @return proxy port
      */
-    public int getPort() {
-        return mPort;
+    public int getProxyPort() {
+        return mProxyPort;
     }
     /**
-     * Returns the MMSC URL of the APN.
+     * Returns the MMSC Uri of the APN.
      *
-     * @return MMSC URL.
+     * @return MMSC Uri.
      */
-    public URL getMmsc() {
+    public Uri getMmsc() {
         return mMmsc;
     }
 
@@ -237,8 +384,8 @@
      *
      * @return MMS proxy address.
      */
-    public InetAddress getMmsProxy() {
-        return mMmsProxy;
+    public InetAddress getMmsProxyAddress() {
+        return mMmsProxyAddress;
     }
 
     /**
@@ -246,8 +393,8 @@
      *
      * @return MMS proxy port
      */
-    public int getMmsPort() {
-        return mMmsPort;
+    public int getMmsProxyPort() {
+        return mMmsProxyPort;
     }
 
     /**
@@ -268,21 +415,9 @@
         return mPassword;
     }
 
-    /** @hide */
-    @IntDef({
-            AUTH_TYPE_NONE,
-            AUTH_TYPE_PAP,
-            AUTH_TYPE_CHAP,
-            AUTH_TYPE_PAP_OR_CHAP,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AuthType {}
-
     /**
      * Returns the authentication type of the APN.
      *
-     * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
-     *
      * @return authentication type
      */
     @AuthType
@@ -290,32 +425,20 @@
         return mAuthType;
     }
 
-    /** @hide */
-    @StringDef({
-            TYPE_DEFAULT,
-            TYPE_MMS,
-            TYPE_SUPL,
-            TYPE_DUN,
-            TYPE_HIPRI,
-            TYPE_FOTA,
-            TYPE_IMS,
-            TYPE_CBS,
-            TYPE_IA,
-            TYPE_EMERGENCY
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ApnType {}
-
     /**
-     * Returns the list of APN types of the APN.
+     * Returns the bitmask of APN types.
      *
-     * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+     * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+     * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+     * MMS-specific connections.
      *
-     * @return the list of APN types
+     * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+     *
+     * @see Builder#setApnTypeBitmask(int)
+     * @return a bitmask describing the types of the APN
      */
-    @ApnType
-    public List<String> getTypes() {
-        return mTypes;
+    public @ApnType int getApnTypeBitmask() {
+        return mApnTypeBitmask;
     }
 
     /**
@@ -328,7 +451,7 @@
     }
 
     /**
-     * Returns the numeric operator ID for the APN. Usually
+     * Returns the numeric operator ID for the APN. Numeric operator ID is defined as
      * {@link android.provider.Telephony.Carriers#MCC} +
      * {@link android.provider.Telephony.Carriers#MNC}.
      *
@@ -338,37 +461,29 @@
         return mOperatorNumeric;
     }
 
-    /** @hide */
-    @StringDef({
-            PROTOCOL_IP,
-            PROTOCOL_IPV6,
-            PROTOCOL_IPV4V6,
-            PROTOCOL_PPP,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ProtocolType {}
-
     /**
      * Returns the protocol to use to connect to this APN.
      *
-     * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-     * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+     * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
      *
+     * @see Builder#setProtocol(int)
      * @return the protocol
      */
     @ProtocolType
-    public String getProtocol() {
+    public int getProtocol() {
         return mProtocol;
     }
 
     /**
-     * Returns the protocol to use to connect to this APN when roaming.
+     * Returns the protocol to use to connect to this APN while the device is roaming.
      *
-     * The syntax is the same as {@link android.provider.Telephony.Carriers#PROTOCOL}.
+     * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
      *
+     * @see Builder#setRoamingProtocol(int)
      * @return the roaming protocol
      */
-    public String getRoamingProtocol() {
+    @ProtocolType
+    public int getRoamingProtocol() {
         return mRoamingProtocol;
     }
 
@@ -398,41 +513,29 @@
         return mNetworkTypeBitmask;
     }
 
-    /** @hide */
-    @StringDef({
-            MVNO_TYPE_SPN,
-            MVNO_TYPE_IMSI,
-            MVNO_TYPE_GID,
-            MVNO_TYPE_ICCID,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface MvnoType {}
-
     /**
      * Returns the MVNO match type for this APN.
      *
-     * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
-     *
+     * @see Builder#setMvnoType(int)
      * @return the MVNO match type
      */
     @MvnoType
-    public String getMvnoType() {
+    public int getMvnoType() {
         return mMvnoType;
     }
 
     private ApnSetting(Builder builder) {
         this.mEntryName = builder.mEntryName;
         this.mApnName = builder.mApnName;
-        this.mProxy = builder.mProxy;
-        this.mPort = builder.mPort;
+        this.mProxyAddress = builder.mProxyAddress;
+        this.mProxyPort = builder.mProxyPort;
         this.mMmsc = builder.mMmsc;
-        this.mMmsProxy = builder.mMmsProxy;
-        this.mMmsPort = builder.mMmsPort;
+        this.mMmsProxyAddress = builder.mMmsProxyAddress;
+        this.mMmsProxyPort = builder.mMmsProxyPort;
         this.mUser = builder.mUser;
         this.mPassword = builder.mPassword;
         this.mAuthType = builder.mAuthType;
-        this.mTypes = (builder.mTypes == null ? new ArrayList<String>() : builder.mTypes);
-        this.mTypesBitmap = builder.mTypesBitmap;
+        this.mApnTypeBitmask = builder.mApnTypeBitmask;
         this.mId = builder.mId;
         this.mOperatorNumeric = builder.mOperatorNumeric;
         this.mProtocol = builder.mProtocol;
@@ -451,25 +554,25 @@
 
     /** @hide */
     public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
-            String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
-            int mmsPort, String user, String password, int authType, List<String> types,
-            String protocol, String roamingProtocol, boolean carrierEnabled,
+            String apnName, InetAddress proxy, int port, Uri mmsc, InetAddress mmsProxy,
+            int mmsPort, String user, String password, int authType, int mApnTypeBitmask,
+            int protocol, int roamingProtocol, boolean carrierEnabled,
             int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
-            int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
+            int waitTime, int maxConnsTime, int mtu, int mvnoType, String mvnoMatchData) {
         return new Builder()
                 .setId(id)
                 .setOperatorNumeric(operatorNumeric)
                 .setEntryName(entryName)
                 .setApnName(apnName)
-                .setProxy(proxy)
-                .setPort(port)
+                .setProxyAddress(proxy)
+                .setProxyPort(port)
                 .setMmsc(mmsc)
-                .setMmsProxy(mmsProxy)
-                .setMmsPort(mmsPort)
+                .setMmsProxyAddress(mmsProxy)
+                .setMmsProxyPort(mmsPort)
                 .setUser(user)
                 .setPassword(password)
                 .setAuthType(authType)
-                .setTypes(types)
+                .setApnTypeBitmask(mApnTypeBitmask)
                 .setProtocol(protocol)
                 .setRoamingProtocol(roamingProtocol)
                 .setCarrierEnabled(carrierEnabled)
@@ -487,7 +590,7 @@
 
     /** @hide */
     public static ApnSetting makeApnSetting(Cursor cursor) {
-        String[] types = parseTypes(
+        final int apnTypesBitmask = parseTypes(
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
         int networkTypeBitmask = cursor.getInt(
                 cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
@@ -507,7 +610,7 @@
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
                 portFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))),
-                URLFromString(cursor.getString(
+                UriFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
                 inetAddressFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
@@ -516,10 +619,12 @@
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
-                Arrays.asList(types),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.ROAMING_PROTOCOL)),
+                apnTypesBitmask,
+                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)))),
+                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.ROAMING_PROTOCOL)))),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
                 networkTypeBitmask,
@@ -531,8 +636,9 @@
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MAX_CONNS_TIME)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.MVNO_TYPE)),
+                nullToNotInMapInt(MVNO_TYPE_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MVNO_TYPE)))),
                 cursor.getString(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MVNO_MATCH_DATA)));
     }
@@ -540,8 +646,8 @@
     /** @hide */
     public static ApnSetting makeApnSetting(ApnSetting apn) {
         return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
-                apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
-                apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
+                apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress, apn.mMmsProxyPort, apn.mUser,
+                apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask, apn.mProtocol, apn.mRoamingProtocol,
                 apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
                 apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
                 apn.mMvnoType, apn.mMvnoMatchData);
@@ -555,18 +661,14 @@
                 .append(", ").append(mId)
                 .append(", ").append(mOperatorNumeric)
                 .append(", ").append(mApnName)
-                .append(", ").append(inetAddressToString(mProxy))
-                .append(", ").append(URLToString(mMmsc))
-                .append(", ").append(inetAddressToString(mMmsProxy))
-                .append(", ").append(portToString(mMmsPort))
-                .append(", ").append(portToString(mPort))
+                .append(", ").append(inetAddressToString(mProxyAddress))
+                .append(", ").append(UriToString(mMmsc))
+                .append(", ").append(inetAddressToString(mMmsProxyAddress))
+                .append(", ").append(portToString(mMmsProxyPort))
+                .append(", ").append(portToString(mProxyPort))
                 .append(", ").append(mAuthType).append(", ");
-        for (int i = 0; i < mTypes.size(); i++) {
-            sb.append(mTypes.get(i));
-            if (i < mTypes.size() - 1) {
-                sb.append(" | ");
-            }
-        }
+        final String[] types = deParseTypes(mApnTypeBitmask).split(",");
+        sb.append(TextUtils.join(" | ", types)).append(", ");
         sb.append(", ").append(mProtocol);
         sb.append(", ").append(mRoamingProtocol);
         sb.append(", ").append(mCarrierEnabled);
@@ -588,56 +690,37 @@
      * @hide
      */
     public boolean hasMvnoParams() {
-        return !TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData);
+        return (mMvnoType != NOT_IN_MAP_INT) && !TextUtils.isEmpty(mMvnoMatchData);
     }
 
     /** @hide */
-    public boolean canHandleType(String type) {
-        if (!mCarrierEnabled) return false;
-        boolean wildcardable = true;
-        if (TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
-        for (String t : mTypes) {
-            // DEFAULT handles all, and HIPRI is handled by DEFAULT
-            if (t.equalsIgnoreCase(type)
-                    || (wildcardable && t.equalsIgnoreCase(TYPE_ALL))
-                    || (t.equalsIgnoreCase(TYPE_DEFAULT)
-                    && type.equalsIgnoreCase(TYPE_HIPRI))) {
-                return true;
-            }
-        }
-        return false;
+    public boolean canHandleType(@ApnType int type) {
+        return mCarrierEnabled && ((mApnTypeBitmask & type) == type);
     }
 
     // check whether the types of two APN same (even only one type of each APN is same)
     private boolean typeSameAny(ApnSetting first, ApnSetting second) {
         if (VDBG) {
             StringBuilder apnType1 = new StringBuilder(first.mApnName + ": ");
-            for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
-                apnType1.append(first.mTypes.get(index1));
-                apnType1.append(",");
-            }
+            apnType1.append(deParseTypes(first.mApnTypeBitmask));
 
             StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
-            for (int index1 = 0; index1 < second.mTypes.size(); index1++) {
-                apnType2.append(second.mTypes.get(index1));
-                apnType2.append(",");
-            }
+            apnType2.append(deParseTypes(second.mApnTypeBitmask));
+
             Rlog.d(LOG_TAG, "APN1: is " + apnType1);
             Rlog.d(LOG_TAG, "APN2: is " + apnType2);
         }
 
-        for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
-            for (int index2 = 0; index2 < second.mTypes.size(); index2++) {
-                if (first.mTypes.get(index1).equals(ApnSetting.TYPE_ALL)
-                        || second.mTypes.get(index2).equals(ApnSetting.TYPE_ALL)
-                        || first.mTypes.get(index1).equals(second.mTypes.get(index2))) {
-                    if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
-                    return true;
-                }
+        if ((first.mApnTypeBitmask & second.mApnTypeBitmask) != 0) {
+            if (VDBG) {
+                Rlog.d(LOG_TAG, "typeSameAny: return true");
             }
+            return true;
         }
 
-        if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
+        if (VDBG) {
+            Rlog.d(LOG_TAG, "typeSameAny: return false");
+        }
         return false;
     }
 
@@ -655,16 +738,15 @@
                 && Objects.equals(mId, other.mId)
                 && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
                 && Objects.equals(mApnName, other.mApnName)
-                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
                 && Objects.equals(mMmsc, other.mMmsc)
-                && Objects.equals(mMmsProxy, other.mMmsProxy)
-                && Objects.equals(mMmsPort, other.mMmsPort)
-                && Objects.equals(mPort,other.mPort)
+                && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+                && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+                && Objects.equals(mProxyPort,other.mProxyPort)
                 && Objects.equals(mUser, other.mUser)
                 && Objects.equals(mPassword, other.mPassword)
                 && Objects.equals(mAuthType, other.mAuthType)
-                && Objects.equals(mTypes, other.mTypes)
-                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
                 && Objects.equals(mProtocol, other.mProtocol)
                 && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -701,16 +783,15 @@
         return mEntryName.equals(other.mEntryName)
                 && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
                 && Objects.equals(mApnName, other.mApnName)
-                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
                 && Objects.equals(mMmsc, other.mMmsc)
-                && Objects.equals(mMmsProxy, other.mMmsProxy)
-                && Objects.equals(mMmsPort, other.mMmsPort)
-                && Objects.equals(mPort, other.mPort)
+                && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+                && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+                && Objects.equals(mProxyPort, other.mProxyPort)
                 && Objects.equals(mUser, other.mUser)
                 && Objects.equals(mPassword, other.mPassword)
                 && Objects.equals(mAuthType, other.mAuthType)
-                && Objects.equals(mTypes, other.mTypes)
-                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
                 && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
                 && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -736,17 +817,17 @@
                 && !other.canHandleType(TYPE_DUN)
                 && Objects.equals(this.mApnName, other.mApnName)
                 && !typeSameAny(this, other)
-                && xorEqualsInetAddress(this.mProxy, other.mProxy)
-                && xorEqualsPort(this.mPort, other.mPort)
+                && xorEquals(this.mProxyAddress, other.mProxyAddress)
+                && xorEqualsPort(this.mProxyPort, other.mProxyPort)
                 && xorEquals(this.mProtocol, other.mProtocol)
                 && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
                 && Objects.equals(this.mProfileId, other.mProfileId)
                 && Objects.equals(this.mMvnoType, other.mMvnoType)
                 && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
-                && xorEqualsURL(this.mMmsc, other.mMmsc)
-                && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
-                && xorEqualsPort(this.mMmsPort, other.mMmsPort))
+                && xorEquals(this.mMmsc, other.mMmsc)
+                && xorEquals(this.mMmsProxyAddress, other.mMmsProxyAddress)
+                && xorEqualsPort(this.mMmsProxyPort, other.mMmsProxyPort))
                 && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
@@ -757,42 +838,23 @@
                 || TextUtils.isEmpty(second));
     }
 
-    // Equal or one is not specified.
-    private boolean xorEqualsInetAddress(InetAddress first, InetAddress second) {
-        return first == null || second == null || first.equals(second);
-    }
-
-    // Equal or one is not specified.
-    private boolean xorEqualsURL(URL first, URL second) {
+    // Equal or one is not null.
+    private boolean xorEquals(Object first, Object second) {
         return first == null || second == null || first.equals(second);
     }
 
     // Equal or one is not specified.
     private boolean xorEqualsPort(int first, int second) {
-        return first == -1 || second == -1 || Objects.equals(first, second);
+        return first == NO_PORT_SPECIFIED || second == NO_PORT_SPECIFIED
+            || Objects.equals(first, second);
     }
 
-    // Helper function to convert APN string into a 32-bit bitmask.
-    private static int getApnBitmask(String apn) {
-        switch (apn) {
-            case TYPE_DEFAULT: return ApnTypes.DEFAULT;
-            case TYPE_MMS: return ApnTypes.MMS;
-            case TYPE_SUPL: return ApnTypes.SUPL;
-            case TYPE_DUN: return ApnTypes.DUN;
-            case TYPE_HIPRI: return ApnTypes.HIPRI;
-            case TYPE_FOTA: return ApnTypes.FOTA;
-            case TYPE_IMS: return ApnTypes.IMS;
-            case TYPE_CBS: return ApnTypes.CBS;
-            case TYPE_IA: return ApnTypes.IA;
-            case TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
-            case TYPE_ALL: return ApnTypes.ALL;
-            default: return ApnTypes.NONE;
-        }
-    }
-
-    private String deParseTypes(List<String> types) {
-        if (types == null) {
-            return null;
+    private String deParseTypes(int apnTypeBitmask) {
+        List<String> types = new ArrayList<>();
+        for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+            if ((apnTypeBitmask & type) == type) {
+                types.add(APN_TYPE_INT_MAP.get(type));
+            }
         }
         return TextUtils.join(",", types);
     }
@@ -808,21 +870,25 @@
         apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
         apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
         apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
-        apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy));
-        apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
-        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
-        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
-        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
-                ? "" : inetAddressToString(mMmsProxy));
+        apnValue.put(Telephony.Carriers.PROXY, mProxyAddress == null ? ""
+            : inetAddressToString(mProxyAddress));
+        apnValue.put(Telephony.Carriers.PORT, portToString(mProxyPort));
+        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : UriToString(mMmsc));
+        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsProxyPort));
+        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxyAddress == null
+                ? "" : inetAddressToString(mMmsProxyAddress));
         apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
         apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
         apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
-        String apnType = deParseTypes(mTypes);
+        String apnType = deParseTypes(mApnTypeBitmask);
         apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
-        apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol));
-        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol));
+        apnValue.put(Telephony.Carriers.PROTOCOL,
+            nullToEmpty(PROTOCOL_INT_MAP.get(mProtocol)));
+        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL,
+            nullToEmpty(PROTOCOL_INT_MAP.get(mRoamingProtocol)));
         apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
-        apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+        apnValue.put(Telephony.Carriers.MVNO_TYPE,
+            nullToEmpty(MVNO_TYPE_INT_MAP.get(mMvnoType)));
         apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
 
         return apnValue;
@@ -830,32 +896,31 @@
 
     /**
      * @param types comma delimited list of APN types
-     * @return array of APN types
+     * @return bitmask of APN types
      * @hide
      */
-    public static String[] parseTypes(String types) {
-        String[] result;
-        // If unset, set to DEFAULT.
+    public static int parseTypes(String types) {
+        // If unset, set to ALL.
         if (TextUtils.isEmpty(types)) {
-            result = new String[1];
-            result[0] = TYPE_ALL;
+            return TYPE_ALL_BUT_IA;
         } else {
-            result = types.split(",");
-        }
-        return result;
-    }
-
-    private static URL URLFromString(String url) {
-        try {
-            return TextUtils.isEmpty(url) ? null : new URL(url);
-        } catch (MalformedURLException e) {
-            Log.e(LOG_TAG, "Can't parse URL from string.");
-            return null;
+            int result = 0;
+            for (String str : types.split(",")) {
+                Integer type = APN_TYPE_STRING_MAP.get(str);
+                if (type != null) {
+                    result |= type;
+                }
+            }
+            return result;
         }
     }
 
-    private static String URLToString(URL url) {
-        return url == null ? "" : url.toString();
+    private static Uri UriFromString(String uri) {
+        return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
+    }
+
+    private static String UriToString(Uri uri) {
+        return uri == null ? "" : uri.toString();
     }
 
     private static InetAddress inetAddressFromString(String inetAddress) {
@@ -887,7 +952,7 @@
     }
 
     private static int portFromString(String strPort) {
-        int port = -1;
+        int port = NO_PORT_SPECIFIED;
         if (!TextUtils.isEmpty(strPort)) {
             try {
                 port = Integer.parseInt(strPort);
@@ -899,7 +964,7 @@
     }
 
     private static String portToString(int port) {
-        return port == -1 ? "" : Integer.toString(port);
+        return port == NO_PORT_SPECIFIED ? "" : Integer.toString(port);
     }
 
     // Implement Parcelable.
@@ -916,19 +981,19 @@
         dest.writeString(mOperatorNumeric);
         dest.writeString(mEntryName);
         dest.writeString(mApnName);
-        dest.writeValue(mProxy);
-        dest.writeInt(mPort);
+        dest.writeValue(mProxyAddress);
+        dest.writeInt(mProxyPort);
         dest.writeValue(mMmsc);
-        dest.writeValue(mMmsProxy);
-        dest.writeInt(mMmsPort);
+        dest.writeValue(mMmsProxyAddress);
+        dest.writeInt(mMmsProxyPort);
         dest.writeString(mUser);
         dest.writeString(mPassword);
         dest.writeInt(mAuthType);
-        dest.writeStringArray(mTypes.toArray(new String[0]));
-        dest.writeString(mProtocol);
-        dest.writeString(mRoamingProtocol);
+        dest.writeInt(mApnTypeBitmask);
+        dest.writeInt(mProtocol);
+        dest.writeInt(mRoamingProtocol);
         dest.writeInt(mCarrierEnabled ? 1: 0);
-        dest.writeString(mMvnoType);
+        dest.writeInt(mMvnoType);
         dest.writeInt(mNetworkTypeBitmask);
     }
 
@@ -939,23 +1004,23 @@
         final String apnName = in.readString();
         final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
         final int port = in.readInt();
-        final URL mmsc = (URL)in.readValue(URL.class.getClassLoader());
+        final Uri mmsc = (Uri)in.readValue(Uri.class.getClassLoader());
         final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
         final int mmsPort = in.readInt();
         final String user = in.readString();
         final String password = in.readString();
         final int authType = in.readInt();
-        final List<String> types = Arrays.asList(in.readStringArray());
-        final String protocol = in.readString();
-        final String roamingProtocol = in.readString();
+        final int apnTypesBitmask = in.readInt();
+        final int protocol = in.readInt();
+        final int roamingProtocol = in.readInt();
         final boolean carrierEnabled = in.readInt() > 0;
-        final String mvnoType = in.readString();
+        final int mvnoType = in.readInt();
         final int networkTypeBitmask = in.readInt();
 
         return makeApnSetting(id, operatorNumeric, entryName, apnName,
-                proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol,
-                roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
-                0, 0, 0, 0, mvnoType, null);
+            proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, apnTypesBitmask,
+            protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+            0, 0, 0, 0, mvnoType, null);
     }
 
     public static final Parcelable.Creator<ApnSetting> CREATOR =
@@ -971,89 +1036,26 @@
                 }
             };
 
-    /**
-     * APN types for data connections.  These are usage categories for an APN
-     * entry.  One APN entry may support multiple APN types, eg, a single APN
-     * may service regular internet traffic ("default") as well as MMS-specific
-     * connections.<br/>
-     * ALL is a special type to indicate that this APN entry can
-     * service all data connections.
-     */
-    public static final String TYPE_ALL = "*";
-    /** APN type for default data traffic */
-    public static final String TYPE_DEFAULT = "default";
-    /** APN type for MMS traffic */
-    public static final String TYPE_MMS = "mms";
-    /** APN type for SUPL assisted GPS */
-    public static final String TYPE_SUPL = "supl";
-    /** APN type for DUN traffic */
-    public static final String TYPE_DUN = "dun";
-    /** APN type for HiPri traffic */
-    public static final String TYPE_HIPRI = "hipri";
-    /** APN type for FOTA */
-    public static final String TYPE_FOTA = "fota";
-    /** APN type for IMS */
-    public static final String TYPE_IMS = "ims";
-    /** APN type for CBS */
-    public static final String TYPE_CBS = "cbs";
-    /** APN type for IA Initial Attach APN */
-    public static final String TYPE_IA = "ia";
-    /** APN type for Emergency PDN. This is not an IA apn, but is used
-     * for access to carrier services in an emergency call situation. */
-    public static final String TYPE_EMERGENCY = "emergency";
-    /**
-     * Array of all APN types
-     *
-     * @hide
-     */
-    public static final String[] ALL_TYPES = {
-            TYPE_DEFAULT,
-            TYPE_MMS,
-            TYPE_SUPL,
-            TYPE_DUN,
-            TYPE_HIPRI,
-            TYPE_FOTA,
-            TYPE_IMS,
-            TYPE_CBS,
-            TYPE_IA,
-            TYPE_EMERGENCY
-    };
-
-    // Possible values for authentication types.
-    public static final int AUTH_TYPE_NONE = 0;
-    public static final int AUTH_TYPE_PAP = 1;
-    public static final int AUTH_TYPE_CHAP = 2;
-    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
-
-    // Possible values for protocol.
-    public static final String PROTOCOL_IP = "IP";
-    public static final String PROTOCOL_IPV6 = "IPV6";
-    public static final String PROTOCOL_IPV4V6 = "IPV4V6";
-    public static final String PROTOCOL_PPP = "PPP";
-
-    // Possible values for MVNO type.
-    public static final String MVNO_TYPE_SPN = "spn";
-    public static final String MVNO_TYPE_IMSI = "imsi";
-    public static final String MVNO_TYPE_GID = "gid";
-    public static final String MVNO_TYPE_ICCID = "iccid";
+    private static int nullToNotInMapInt(Integer value) {
+        return value == null ? NOT_IN_MAP_INT : value;
+    }
 
     public static class Builder{
         private String mEntryName;
         private String mApnName;
-        private InetAddress mProxy;
-        private int mPort = -1;
-        private URL mMmsc;
-        private InetAddress mMmsProxy;
-        private int mMmsPort = -1;
+        private InetAddress mProxyAddress;
+        private int mProxyPort = NO_PORT_SPECIFIED;
+        private Uri mMmsc;
+        private InetAddress mMmsProxyAddress;
+        private int mMmsProxyPort = NO_PORT_SPECIFIED;
         private String mUser;
         private String mPassword;
         private int mAuthType;
-        private List<String> mTypes;
-        private int mTypesBitmap;
+        private int mApnTypeBitmask;
         private int mId;
         private String mOperatorNumeric;
-        private String mProtocol;
-        private String mRoamingProtocol;
+        private int mProtocol = NOT_IN_MAP_INT;
+        private int mRoamingProtocol = NOT_IN_MAP_INT;
         private int mMtu;
         private int mNetworkTypeBitmask;
         private boolean mCarrierEnabled;
@@ -1062,7 +1064,7 @@
         private int mMaxConns;
         private int mWaitTime;
         private int mMaxConnsTime;
-        private String mMvnoType;
+        private int mMvnoType = NOT_IN_MAP_INT;
         private String mMvnoMatchData;
 
         /**
@@ -1182,8 +1184,8 @@
          *
          * @param proxy the proxy address to set for the APN
          */
-        public Builder setProxy(InetAddress proxy) {
-            this.mProxy = proxy;
+        public Builder setProxyAddress(InetAddress proxy) {
+            this.mProxyAddress = proxy;
             return this;
         }
 
@@ -1192,17 +1194,17 @@
          *
          * @param port the proxy port to set for the APN
          */
-        public Builder setPort(int port) {
-            this.mPort = port;
+        public Builder setProxyPort(int port) {
+            this.mProxyPort = port;
             return this;
         }
 
         /**
-         * Sets the MMSC URL of the APN.
+         * Sets the MMSC Uri of the APN.
          *
-         * @param mmsc the MMSC URL to set for the APN
+         * @param mmsc the MMSC Uri to set for the APN
          */
-        public Builder setMmsc(URL mmsc) {
+        public Builder setMmsc(Uri mmsc) {
             this.mMmsc = mmsc;
             return this;
         }
@@ -1212,8 +1214,8 @@
          *
          * @param mmsProxy the MMS proxy address to set for the APN
          */
-        public Builder setMmsProxy(InetAddress mmsProxy) {
-            this.mMmsProxy = mmsProxy;
+        public Builder setMmsProxyAddress(InetAddress mmsProxy) {
+            this.mMmsProxyAddress = mmsProxy;
             return this;
         }
 
@@ -1222,8 +1224,8 @@
          *
          * @param mmsPort the MMS proxy port to set for the APN
          */
-        public Builder setMmsPort(int mmsPort) {
-            this.mMmsPort = mmsPort;
+        public Builder setMmsProxyPort(int mmsPort) {
+            this.mMmsProxyPort = mmsPort;
             return this;
         }
 
@@ -1251,8 +1253,6 @@
         /**
          * Sets the authentication type of the APN.
          *
-         * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
-         *
          * @param authType the authentication type to set for the APN
          */
         public Builder setAuthType(@AuthType int authType) {
@@ -1261,25 +1261,25 @@
         }
 
         /**
-         * Sets the list of APN types of the APN.
+         * Sets the bitmask of APN types.
          *
-         * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+         * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+         * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+         * MMS-specific connections.
          *
-         * @param types the list of APN types to set for the APN
+         * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+         *
+         * @param apnTypeBitmask a bitmask describing the types of the APN
          */
-        public Builder setTypes(@ApnType List<String> types) {
-            this.mTypes = types;
-            int apnBitmap = 0;
-            for (int i = 0; i < mTypes.size(); i++) {
-                mTypes.set(i, mTypes.get(i).toLowerCase());
-                apnBitmap |= getApnBitmask(mTypes.get(i));
-            }
-            this.mTypesBitmap = apnBitmap;
+        public Builder setApnTypeBitmask(@ApnType int apnTypeBitmask) {
+            this.mApnTypeBitmask = apnTypeBitmask;
             return this;
         }
 
         /**
-         * Set the numeric operator ID for the APN.
+         * Sets the numeric operator ID for the APN. Numeric operator ID is defined as
+         * {@link android.provider.Telephony.Carriers#MCC} +
+         * {@link android.provider.Telephony.Carriers#MNC}.
          *
          * @param operatorNumeric the numeric operator ID to set for this entry
          */
@@ -1291,22 +1291,23 @@
         /**
          * Sets the protocol to use to connect to this APN.
          *
-         * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-         * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+         * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
          *
          * @param protocol the protocol to set to use to connect to this APN
          */
-        public Builder setProtocol(@ProtocolType String protocol) {
+        public Builder setProtocol(@ProtocolType int protocol) {
             this.mProtocol = protocol;
             return this;
         }
 
         /**
-         * Sets the protocol to use to connect to this APN when roaming.
+         * Sets the protocol to use to connect to this APN when the device is roaming.
+         *
+         * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
          *
          * @param roamingProtocol the protocol to set to use to connect to this APN when roaming
          */
-        public Builder setRoamingProtocol(String roamingProtocol) {
+        public Builder setRoamingProtocol(@ProtocolType  int roamingProtocol) {
             this.mRoamingProtocol = roamingProtocol;
             return this;
         }
@@ -1334,16 +1335,25 @@
         /**
          * Sets the MVNO match type for this APN.
          *
-         * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
-         *
          * @param mvnoType the MVNO match type to set for this APN
          */
-        public Builder setMvnoType(@MvnoType String mvnoType) {
+        public Builder setMvnoType(@MvnoType int mvnoType) {
             this.mMvnoType = mvnoType;
             return this;
         }
 
+        /**
+         * Builds {@link ApnSetting} from this builder.
+         *
+         * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
+         * is empty, or {@link #setApnTypeBitmask(int)} doesn't contain a valid bit,
+         * {@link ApnSetting} built from this builder otherwise.
+         */
         public ApnSetting build() {
+            if ((mApnTypeBitmask & ApnTypes.ALL) == 0 || TextUtils.isEmpty(mApnName)
+                || TextUtils.isEmpty(mEntryName)) {
+                return null;
+            }
             return new ApnSetting(this);
         }
     }
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 1637c55..dff1c6f 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -15,11 +15,12 @@
  */
 package android.telephony.euicc;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -73,6 +74,7 @@
      */
     @SystemApi
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public static final String ACTION_OTA_STATUS_CHANGED =
             "android.telephony.euicc.action.OTA_STATUS_CHANGED";
 
@@ -301,6 +303,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public int getOtaStatus() {
         if (!isEnabled()) {
             return EUICC_OTA_STATUS_UNAVAILABLE;
@@ -325,6 +328,7 @@
      * @param switchAfterDownload if true, the profile will be activated upon successful download.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void downloadSubscription(DownloadableSubscription subscription,
             boolean switchAfterDownload, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -387,6 +391,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
         if (!isEnabled()) {
             PendingIntent callbackIntent =
@@ -422,6 +427,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDownloadableSubscriptionMetadata(
             DownloadableSubscription subscription, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -452,6 +458,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -496,6 +503,7 @@
      * @param subscriptionId the ID of the subscription to delete.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -523,6 +531,7 @@
      *     current profile without activating another profile to replace it.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -548,6 +557,7 @@
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void updateSubscriptionNickname(
             int subscriptionId, String nickname, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -566,12 +576,13 @@
      * Erase all subscriptions and reset the eUICC.
      *
      * <p>Requires that the calling app has the
-     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
-     * internal system use only.
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void eraseSubscriptions(PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 23b4dc0..fbb69ad 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -375,8 +375,6 @@
 
     /**
      * Report whether data connectivity is possible.
-     *
-     * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
      */
     boolean isDataConnectivityPossible(int subId);
 
@@ -415,25 +413,11 @@
      * Returns the current active phone type as integer for particular slot.
      * Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE
      * and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE
-     *
-     * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
-     *
      * @param slotIndex - slot to query.
      */
     int getActivePhoneTypeForSlot(int slotIndex);
 
     /**
-     * Returns the current active phone type as integer for particular slot.
-     * Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE
-     * and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE
-     *
-     * If the device is not voice-capable, return PHONE_TYPE_NONE
-     *
-     * @param slotIndex - slot to query.
-     */
-    int getVoiceCapableActivePhoneTypeForSlot(int slotIndex);
-
-    /**
      * Returns the CDMA ERI icon index to display
      * @param callingPackage package making the call.
      */
@@ -480,8 +464,6 @@
      * Returns true if OTA service provisioning needs to run.
      * Only relevant on some technologies, others will always
      * return false.
-     *
-     * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
      */
     boolean needsOtaServiceProvisioning();
 
@@ -994,8 +976,6 @@
      * Returns list of the package names of the carrier apps that should handle the input intent
      * and have carrier privileges for the given phoneId.
      *
-     * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
-     *
      * @param intent Intent that will be sent.
      * @param phoneId The phoneId on which the carrier app has carrier privileges.
      * @return list of carrier app package names that can handle the intent on phoneId.
@@ -1120,8 +1100,6 @@
     /**
      * Whether video calling has been enabled by the user.
      *
-     * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
-     *
      * @param callingPackage The package making the call.
      * @return {@code true} if the user has enabled video calling, {@code false} otherwise.
      */
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 14c5f4b..964a313 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -470,6 +470,9 @@
         int bearerDataLength;
         SmsEnvelope env = new SmsEnvelope();
         CdmaSmsAddress addr = new CdmaSmsAddress();
+        // We currently do not parse subaddress in PDU, but it is required when determining
+        // fingerprint (see getIncomingSmsFingerprint()).
+        CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress();
 
         try {
             env.messageType = dis.readInt();
@@ -520,6 +523,7 @@
         // link the filled objects to this SMS
         mOriginatingAddress = addr;
         env.origAddress = addr;
+        env.origSubaddress = subaddr;
         mEnvelope = env;
         mPdu = pdu;
 
@@ -1009,8 +1013,11 @@
         output.write(mEnvelope.teleService);
         output.write(mEnvelope.origAddress.origBytes, 0, mEnvelope.origAddress.origBytes.length);
         output.write(mEnvelope.bearerData, 0, mEnvelope.bearerData.length);
-        output.write(mEnvelope.origSubaddress.origBytes, 0,
-                mEnvelope.origSubaddress.origBytes.length);
+        // subaddress is not set when parsing some MT SMS.
+        if (mEnvelope.origSubaddress != null && mEnvelope.origSubaddress.origBytes != null) {
+            output.write(mEnvelope.origSubaddress.origBytes, 0,
+                    mEnvelope.origSubaddress.origBytes.length);
+        }
 
         return output.toByteArray();
     }
diff --git a/test-mock/api/android-test-mock-current.txt b/test-mock/api/android-test-mock-current.txt
index 07acfef..f3b253c 100644
--- a/test-mock/api/android-test-mock-current.txt
+++ b/test-mock/api/android-test-mock-current.txt
@@ -278,6 +278,7 @@
     method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
     method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
     method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
+    method public android.content.pm.ResolveInfo resolveServiceAsUser(android.content.Intent, int, int);
     method public void setApplicationCategoryHint(java.lang.String, int);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 1af7c3a..8ebb77d 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -464,6 +465,11 @@
     }
 
     @Override
+    public ResolveInfo resolveServiceAsUser(Intent intent, int flags, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
         throw new UnsupportedOperationException();
     }
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index cc3366f..0ca20de 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -50,13 +50,18 @@
 
     private static final int TEST_UDP_ENCAP_PORT = 34567;
     private static final int DROID_SPI = 0xD1201D;
+    private static final int DUMMY_RESOURCE_ID = 0x1234;
 
     private static final InetAddress GOOGLE_DNS_4;
+    private static final String VTI_INTF_NAME = "ipsec_test";
+    private static final InetAddress VTI_LOCAL_ADDRESS;
+    private static final LinkAddress VTI_INNER_ADDRESS = new LinkAddress("10.0.1.1/24");
 
     static {
         try {
             // Google Public DNS Addresses;
             GOOGLE_DNS_4 = InetAddress.getByName("8.8.8.8");
+            VTI_LOCAL_ADDRESS = InetAddress.getByName("8.8.4.4");
         } catch (UnknownHostException e) {
             throw new RuntimeException("Could not resolve DNS Addresses", e);
         }
@@ -77,9 +82,8 @@
      */
     @Test
     public void testAllocSpi() throws Exception {
-        int resourceId = 1;
         IpSecSpiResponse spiResp =
-                new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
+                new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI);
         when(mMockIpSecService.allocateSecurityParameterIndex(
                         eq(GOOGLE_DNS_4.getHostAddress()),
                         eq(DROID_SPI),
@@ -92,14 +96,13 @@
 
         droidSpi.close();
 
-        verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId);
+        verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID);
     }
 
     @Test
     public void testAllocRandomSpi() throws Exception {
-        int resourceId = 1;
         IpSecSpiResponse spiResp =
-                new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
+                new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI);
         when(mMockIpSecService.allocateSecurityParameterIndex(
                         eq(GOOGLE_DNS_4.getHostAddress()),
                         eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
@@ -113,7 +116,7 @@
 
         randomSpi.close();
 
-        verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId);
+        verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID);
     }
 
     /*
@@ -165,11 +168,10 @@
 
     @Test
     public void testOpenEncapsulationSocket() throws Exception {
-        int resourceId = 1;
         IpSecUdpEncapResponse udpEncapResp =
                 new IpSecUdpEncapResponse(
                         IpSecManager.Status.OK,
-                        resourceId,
+                        DUMMY_RESOURCE_ID,
                         TEST_UDP_ENCAP_PORT,
                         Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
         when(mMockIpSecService.openUdpEncapsulationSocket(eq(TEST_UDP_ENCAP_PORT), anyObject()))
@@ -182,16 +184,15 @@
 
         encapSocket.close();
 
-        verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId);
+        verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID);
     }
 
     @Test
     public void testOpenEncapsulationSocketOnRandomPort() throws Exception {
-        int resourceId = 1;
         IpSecUdpEncapResponse udpEncapResp =
                 new IpSecUdpEncapResponse(
                         IpSecManager.Status.OK,
-                        resourceId,
+                        DUMMY_RESOURCE_ID,
                         TEST_UDP_ENCAP_PORT,
                         Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
 
@@ -206,7 +207,7 @@
 
         encapSocket.close();
 
-        verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId);
+        verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID);
     }
 
     @Test
@@ -219,4 +220,45 @@
     }
 
     // TODO: add test when applicable transform builder interface is available
-}
+
+    private IpSecManager.IpSecTunnelInterface createAndValidateVti(int resourceId, String intfName)
+            throws Exception {
+        IpSecTunnelInterfaceResponse dummyResponse =
+                new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
+        when(mMockIpSecService.createTunnelInterface(
+                eq(VTI_LOCAL_ADDRESS.getHostAddress()), eq(GOOGLE_DNS_4.getHostAddress()),
+                anyObject(), anyObject()))
+                        .thenReturn(dummyResponse);
+
+        IpSecManager.IpSecTunnelInterface tunnelIntf = mIpSecManager.createIpSecTunnelInterface(
+                VTI_LOCAL_ADDRESS, GOOGLE_DNS_4, mock(Network.class));
+
+        assertNotNull(tunnelIntf);
+        return tunnelIntf;
+    }
+
+    @Test
+    public void testCreateVti() throws Exception {
+        IpSecManager.IpSecTunnelInterface tunnelIntf =
+                createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME);
+
+        assertEquals(VTI_INTF_NAME, tunnelIntf.getInterfaceName());
+
+        tunnelIntf.close();
+        verify(mMockIpSecService).deleteTunnelInterface(eq(DUMMY_RESOURCE_ID));
+    }
+
+    @Test
+    public void testAddRemoveAddressesFromVti() throws Exception {
+        IpSecManager.IpSecTunnelInterface tunnelIntf =
+                createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME);
+
+        tunnelIntf.addAddress(VTI_INNER_ADDRESS);
+        verify(mMockIpSecService)
+                .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS));
+
+        tunnelIntf.removeAddress(VTI_INNER_ADDRESS);
+        verify(mMockIpSecService)
+                .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS));
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 3e1ff6d..a5c55e8 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
@@ -32,6 +33,9 @@
 import android.net.IpSecManager;
 import android.net.IpSecSpiResponse;
 import android.net.IpSecTransformResponse;
+import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
+import android.net.Network;
 import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
@@ -56,10 +60,15 @@
 
     private final String mDestinationAddr;
     private final String mSourceAddr;
+    private final LinkAddress mLocalInnerAddress;
 
     @Parameterized.Parameters
     public static Collection ipSecConfigs() {
-        return Arrays.asList(new Object[][] {{"1.2.3.4", "8.8.4.4"}, {"2601::2", "2601::10"}});
+        return Arrays.asList(
+                new Object[][] {
+                {"1.2.3.4", "8.8.4.4", "10.0.1.1/24"},
+                {"2601::2", "2601::10", "2001:db8::1/64"}
+        });
     }
 
     private static final byte[] AEAD_KEY = {
@@ -86,6 +95,7 @@
     INetd mMockNetd;
     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
     IpSecService mIpSecService;
+    Network fakeNetwork = new Network(0xAB);
 
     private static final IpSecAlgorithm AUTH_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
@@ -94,9 +104,11 @@
     private static final IpSecAlgorithm AEAD_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
 
-    public IpSecServiceParameterizedTest(String sourceAddr, String destAddr) {
+    public IpSecServiceParameterizedTest(
+            String sourceAddr, String destAddr, String localInnerAddr) {
         mSourceAddr = sourceAddr;
         mDestinationAddr = destAddr;
+        mLocalInnerAddress = new LinkAddress(localInnerAddr);
     }
 
     @Before
@@ -406,4 +418,103 @@
 
         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
     }
+
+    private IpSecTunnelInterfaceResponse createAndValidateTunnel(
+            String localAddr, String remoteAddr) {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                mIpSecService.createTunnelInterface(
+                        mSourceAddr, mDestinationAddr, fakeNetwork, new Binder());
+
+        assertNotNull(createTunnelResp);
+        assertEquals(IpSecManager.Status.OK, createTunnelResp.status);
+        return createTunnelResp;
+    }
+
+    @Test
+    public void testCreateTunnelInterface() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        // Check that we have stored the tracking object, and retrieve it
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.RefcountedResource refcountedRecord =
+                userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                        createTunnelResp.resourceId);
+
+        assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
+        verify(mMockNetd)
+                .addVirtualTunnelInterface(
+                        eq(createTunnelResp.interfaceName),
+                        eq(mSourceAddr),
+                        eq(mDestinationAddr),
+                        anyInt(),
+                        anyInt());
+    }
+
+    @Test
+    public void testDeleteTunnelInterface() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+
+        mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId);
+
+        // Verify quota and RefcountedResource objects cleaned up
+        assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
+        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        try {
+            userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                    createTunnelResp.resourceId);
+            fail("Expected IllegalArgumentException on attempt to access deleted resource");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testTunnelInterfaceBinderDeath() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.RefcountedResource refcountedRecord =
+                userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                        createTunnelResp.resourceId);
+
+        refcountedRecord.binderDied();
+
+        // Verify quota and RefcountedResource objects cleaned up
+        assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
+        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        try {
+            userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                    createTunnelResp.resourceId);
+            fail("Expected IllegalArgumentException on attempt to access deleted resource");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testAddRemoveAddressFromTunnelInterface() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        mIpSecService.addAddressToTunnelInterface(createTunnelResp.resourceId, mLocalInnerAddress);
+        verify(mMockNetd)
+                .interfaceAddAddress(
+                        eq(createTunnelResp.interfaceName),
+                        eq(mLocalInnerAddress.getAddress().getHostAddress()),
+                        eq(mLocalInnerAddress.getPrefixLength()));
+
+        mIpSecService.removeAddressFromTunnelInterface(
+                createTunnelResp.resourceId, mLocalInnerAddress);
+        verify(mMockNetd)
+                .interfaceDelAddress(
+                        eq(createTunnelResp.interfaceName),
+                        eq(mLocalInnerAddress.getAddress().getHostAddress()),
+                        eq(mLocalInnerAddress.getPrefixLength()));
+    }
 }
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index ab106d7..ebdcdfd 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -46,7 +46,8 @@
       message(that.message),
       fields(that.fields),
       primaryFields(that.primaryFields),
-      exclusiveField(that.exclusiveField) {}
+      exclusiveField(that.exclusiveField),
+      uidField(that.uidField) {}
 
 AtomDecl::AtomDecl(int c, const string& n, const string& m)
     :code(c),
@@ -262,6 +263,18 @@
             errorCount++;
         }
     }
+
+    if (field->options().GetExtension(os::statsd::is_uid) == true) {
+        if (javaType != JAVA_TYPE_INT) {
+            errorCount++;
+        }
+
+        if (atomDecl->uidField == 0) {
+            atomDecl->uidField = it->first;
+        } else {
+            errorCount++;
+        }
+    }
   }
 
   return errorCount;
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index edba3e2..5d2c302 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -84,6 +84,8 @@
     vector<int> primaryFields;
     int exclusiveField = 0;
 
+    int uidField = 0;
+
     AtomDecl();
     AtomDecl(const AtomDecl& that);
     AtomDecl(int code, const string& name, const string& message);
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index d58c223..300c701 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -113,6 +113,98 @@
     fprintf(out, "// the single event tag id for all stats logs\n");
     fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
 
+    std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
+                                             "audio_state_changed",
+                                             "call_state_changed",
+                                             "phone_signal_strength_changed",
+                                             "mobile_bytes_transfer_by_fg_bg",
+                                             "mobile_bytes_transfer"};
+    fprintf(out,
+            "const std::set<int> "
+            "AtomsInfo::kNotTruncatingTimestampAtomWhiteList = {\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+         atom != atoms.decls.end(); atom++) {
+        if (kTruncatingAtomNames.find(atom->name) ==
+            kTruncatingAtomNames.end()) {
+            string constant = make_constant_name(atom->name);
+            fprintf(out, " %s,\n", constant.c_str());
+        }
+    }
+    fprintf(out, "};\n");
+    fprintf(out, "\n");
+
+    fprintf(out,
+            "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+         atom != atoms.decls.end(); atom++) {
+        for (vector<AtomField>::const_iterator field = atom->fields.begin();
+             field != atom->fields.end(); field++) {
+            if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                string constant = make_constant_name(atom->name);
+                fprintf(out, " %s,\n", constant.c_str());
+                break;
+            }
+        }
+    }
+    fprintf(out, "};\n");
+    fprintf(out, "\n");
+
+    fprintf(out,
+            "static std::map<int, int> "
+            "getAtomUidField() {\n");
+    fprintf(out, "  std::map<int, int> uidField;\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+         atom != atoms.decls.end(); atom++) {
+        if (atom->uidField == 0) {
+            continue;
+        }
+        fprintf(out,
+                "\n    // Adding uid field for atom "
+                "(%d)%s\n",
+                atom->code, atom->name.c_str());
+        fprintf(out, "    uidField[static_cast<int>(%s)] = %d;\n",
+                make_constant_name(atom->name).c_str(), atom->uidField);
+    }
+
+    fprintf(out, "    return uidField;\n");
+    fprintf(out, "};\n");
+
+    fprintf(out,
+            "const std::map<int, int> AtomsInfo::kAtomsWithUidField = "
+            "getAtomUidField();\n");
+
+    fprintf(out,
+            "static std::map<int, StateAtomFieldOptions> "
+            "getStateAtomFieldOptions() {\n");
+    fprintf(out, "    std::map<int, StateAtomFieldOptions> options;\n");
+    fprintf(out, "    StateAtomFieldOptions opt;\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+         atom != atoms.decls.end(); atom++) {
+        if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
+            continue;
+        }
+        fprintf(out,
+                "\n    // Adding primary and exclusive fields for atom "
+                "(%d)%s\n",
+                atom->code, atom->name.c_str());
+        fprintf(out, "    opt.primaryFields.clear();\n");
+        for (const auto& field : atom->primaryFields) {
+            fprintf(out, "    opt.primaryFields.push_back(%d);\n", field);
+        }
+
+        fprintf(out, "    opt.exclusiveField = %d;\n", atom->exclusiveField);
+        fprintf(out, "    options[static_cast<int>(%s)] = opt;\n",
+                make_constant_name(atom->name).c_str());
+    }
+
+    fprintf(out, "    return options;\n");
+    fprintf(out, "  }\n");
+
+    fprintf(out,
+            "const std::map<int, StateAtomFieldOptions> "
+            "AtomsInfo::kStateAtomsFieldOptions = "
+            "getStateAtomFieldOptions();\n");
+
     // Print write methods
     fprintf(out, "\n");
     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
@@ -366,89 +458,26 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
-    std::set<string> kTruncatingAtomNames =
-        { "mobile_radio_power_state_changed", "audio_state_changed", "call_state_changed",
-          "phone_signal_strength_changed", "mobile_bytes_transfer_by_fg_bg",
-          "mobile_bytes_transfer"};
-    fprintf(out, "const static std::set<int> kNotTruncatingTimestampAtomWhiteList = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
-        if (kTruncatingAtomNames.find(atom->name) == kTruncatingAtomNames.end()) {
-            string constant = make_constant_name(atom->name);
-            fprintf(out, " %s,\n", constant.c_str());
-        }
-    }
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-
-    fprintf(out, "const static std::set<int> kAtomsWithUidField = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
-        for (vector<AtomField>::const_iterator field = atom->fields.begin();
-                field != atom->fields.end(); field++) {
-            if (field->name == "uid") {
-                string constant = make_constant_name(atom->name);
-                fprintf(out, " %s,\n", constant.c_str());
-                break;
-            }
-        }
-    }
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-
-    fprintf(out, "const static std::set<int> kAtomsWithAttributionChain = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
-        for (vector<AtomField>::const_iterator field = atom->fields.begin();
-                field != atom->fields.end(); field++) {
-            if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                string constant = make_constant_name(atom->name);
-                fprintf(out, " %s,\n", constant.c_str());
-                break;
-            }
-        }
-    }
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-
-    fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", maxPushedAtomId);
-
     fprintf(out, "struct StateAtomFieldOptions {\n");
     fprintf(out, "  std::vector<int> primaryFields;\n");
     fprintf(out, "  int exclusiveField;\n");
+    fprintf(out, "};\n");
     fprintf(out, "\n");
+
+    fprintf(out, "struct AtomsInfo {\n");
     fprintf(out,
-            "  static std::map<int, StateAtomFieldOptions> "
-            "getStateAtomFieldOptions() {\n");
-    fprintf(out, "    std::map<int, StateAtomFieldOptions> options;\n");
-    fprintf(out, "    StateAtomFieldOptions opt;\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
-        if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
-            continue;
-        }
-        fprintf(out,
-                "\n    // Adding primary and exclusive fields for atom "
-                "(%d)%s\n",
-                atom->code, atom->name.c_str());
-        fprintf(out, "    opt.primaryFields.clear();\n");
-        for (const auto& field : atom->primaryFields) {
-            fprintf(out, "    opt.primaryFields.push_back(%d);\n", field);
-        }
-
-        fprintf(out, "    opt.exclusiveField = %d;\n", atom->exclusiveField);
-        fprintf(out, "    options[static_cast<int>(%s)] = opt;\n",
-                make_constant_name(atom->name).c_str());
-    }
-
-    fprintf(out, "    return options;\n");
-    fprintf(out, "  }\n");
+            "  const static std::set<int> "
+            "kNotTruncatingTimestampAtomWhiteList;\n");
+    fprintf(out, "  const static std::map<int, int> kAtomsWithUidField;\n");
+    fprintf(out,
+            "  const static std::set<int> kAtomsWithAttributionChain;\n");
+    fprintf(out,
+            "  const static std::map<int, StateAtomFieldOptions> "
+            "kStateAtomsFieldOptions;\n");
     fprintf(out, "};\n");
 
-    fprintf(out,
-            "const static std::map<int, StateAtomFieldOptions> "
-            "kStateAtomsFieldOptions = "
-            "StateAtomFieldOptions::getStateAtomFieldOptions();\n");
+    fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
+            maxPushedAtomId);
 
     // Print write methods
     fprintf(out, "//\n");
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 309bc80..2f7b50d 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -28,7 +28,6 @@
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.PasspointManagementObjectDefinition;
 import android.net.wifi.ScanResult;
-import android.net.wifi.ScanSettings;
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
@@ -86,7 +85,7 @@
 
     boolean disableNetwork(int netId, String packageName);
 
-    void startScan(in ScanSettings requested, in WorkSource ws, String packageName);
+    void startScan(String packageName);
 
     List<ScanResult> getScanResults(String callingPackage);
 
diff --git a/wifi/java/android/net/wifi/ScanSettings.java b/wifi/java/android/net/wifi/ScanSettings.java
deleted file mode 100644
index 094ce34..0000000
--- a/wifi/java/android/net/wifi/ScanSettings.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Bundle of customized scan settings
- *
- * @see WifiManager#startCustomizedScan
- *
- * @hide
- */
-public class ScanSettings implements Parcelable {
-
-    /** channel set to scan. this can be null or empty, indicating a full scan */
-    public Collection<WifiChannel> channelSet;
-
-    /** public constructor */
-    public ScanSettings() { }
-
-    /** copy constructor */
-    public ScanSettings(ScanSettings source) {
-        if (source.channelSet != null)
-            channelSet = new ArrayList<WifiChannel>(source.channelSet);
-    }
-
-    /** check for validity */
-    public boolean isValid() {
-        for (WifiChannel channel : channelSet)
-            if (!channel.isValid()) return false;
-        return true;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(channelSet == null ? 0 : channelSet.size());
-        if (channelSet != null)
-            for (WifiChannel channel : channelSet) channel.writeToParcel(out, flags);
-    }
-
-    /** implement Parcelable interface */
-    public static final Parcelable.Creator<ScanSettings> CREATOR =
-            new Parcelable.Creator<ScanSettings>() {
-        @Override
-        public ScanSettings createFromParcel(Parcel in) {
-            ScanSettings settings = new ScanSettings();
-            int size = in.readInt();
-            if (size > 0) {
-                settings.channelSet = new ArrayList<WifiChannel>(size);
-                while (size-- > 0)
-                    settings.channelSet.add(WifiChannel.CREATOR.createFromParcel(in));
-            }
-            return settings;
-        }
-
-        @Override
-        public ScanSettings[] newArray(int size) {
-            return new ScanSettings[size];
-        }
-    };
-}
diff --git a/wifi/java/android/net/wifi/WifiChannel.java b/wifi/java/android/net/wifi/WifiChannel.java
deleted file mode 100644
index 640481e..0000000
--- a/wifi/java/android/net/wifi/WifiChannel.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Wifi Channel
- *
- * @see ScanSettings
- *
- * @hide
- */
-public class WifiChannel implements Parcelable {
-
-    private static final int MIN_FREQ_MHZ = 2412;
-    private static final int MAX_FREQ_MHZ = 5825;
-
-    private static final int MIN_CHANNEL_NUM = 1;
-    private static final int MAX_CHANNEL_NUM = 196;
-
-    /** frequency */
-    public int freqMHz;
-
-    /** channel number */
-    public int channelNum;
-
-    /** is it a DFS channel? */
-    public boolean isDFS;
-
-    /** public constructor */
-    public WifiChannel() { }
-
-    /** check for validity */
-    public boolean isValid() {
-        if (freqMHz < MIN_FREQ_MHZ || freqMHz > MAX_FREQ_MHZ) return false;
-        if (channelNum < MIN_CHANNEL_NUM || channelNum > MAX_CHANNEL_NUM) return false;
-        return true;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(freqMHz);
-        out.writeInt(channelNum);
-        out.writeInt(isDFS ? 1 : 0);
-    }
-
-    /** implement Parcelable interface */
-    public static final Parcelable.Creator<WifiChannel> CREATOR =
-            new Parcelable.Creator<WifiChannel>() {
-        @Override
-        public WifiChannel createFromParcel(Parcel in) {
-            WifiChannel channel = new WifiChannel();
-            channel.freqMHz = in.readInt();
-            channel.channelNum = in.readInt();
-            channel.isDFS = in.readInt() != 0;
-            return channel;
-        }
-
-        @Override
-        public WifiChannel[] newArray(int size) {
-            return new WifiChannel[size];
-        }
-    };
-}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index d2cdf8d..c8df087 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1634,7 +1634,7 @@
     public boolean startScan(WorkSource workSource) {
         try {
             String packageName = mContext.getOpPackageName();
-            mService.startScan(null, workSource, packageName);
+            mService.startScan(packageName);
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();