Merge "Remove Activity Recognition from the Android Code."
diff --git a/Android.mk b/Android.mk
index e3cc275..9f7bf99 100644
--- a/Android.mk
+++ b/Android.mk
@@ -80,6 +80,8 @@
 # ==== hiddenapi lists =======================================
 .KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
 $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
+    PRIVATE_FLAGS_INPUTS := $(PRIVATE_FLAGS_INPUTS) $(SOONG_HIDDENAPI_FLAGS)
+$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
     frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
     frameworks/base/config/hiddenapi-greylist.txt \
     frameworks/base/config/hiddenapi-greylist-max-p.txt \
@@ -87,7 +89,8 @@
     frameworks/base/config/hiddenapi-force-blacklist.txt \
     $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
     $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
-    $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
+    $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
+    $(SOONG_HIDDENAPI_FLAGS)
 	frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
 	    --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
 	    --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
diff --git a/api/current.txt b/api/current.txt
index 02d703f..e7c9b24 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24027,6 +24027,7 @@
     method public android.view.Surface getSurface();
     method public int getWidth();
     method public static android.media.ImageReader newInstance(int, int, int, int);
+    method public static android.media.ImageReader newInstance(int, int, int, int, long);
     method public void setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler);
   }
 
@@ -24040,6 +24041,7 @@
     method public int getFormat();
     method public int getMaxImages();
     method public static android.media.ImageWriter newInstance(android.view.Surface, int);
+    method public static android.media.ImageWriter newInstance(android.view.Surface, int, int);
     method public void queueInputImage(android.media.Image);
     method public void setOnImageReleasedListener(android.media.ImageWriter.OnImageReleasedListener, android.os.Handler);
   }
@@ -52681,7 +52683,6 @@
     method public final void notifyViewDisappeared(android.view.autofill.AutofillId);
     method public final void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
     method public final void notifyViewsDisappeared(android.view.autofill.AutofillId, int[]);
-    field public static final int FLAG_USER_INPUT = 1; // 0x1
   }
 
   public final class ContentCaptureSessionId implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index d179bde..e9743f7 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1336,7 +1336,7 @@
   }
 
   public class CrossProfileApps {
-    method public void startAnyActivity(android.content.ComponentName, android.os.UserHandle);
+    method public void startActivity(android.content.ComponentName, android.os.UserHandle);
   }
 
   public final class InstantAppInfo implements android.os.Parcelable {
@@ -2901,6 +2901,7 @@
     method public int getConstellationType();
     method public float getExcessPathLengthMeters();
     method public float getExcessPathLengthUncertaintyMeters();
+    method public float getProbSatIsLos();
     method public android.location.GnssReflectingPlane getReflectingPlane();
     method public int getSatId();
     method public int getSingleSatCorrectionFlags();
@@ -2908,13 +2909,12 @@
     method public boolean hasExcessPathLengthUncertainty();
     method public boolean hasReflectingPlane();
     method public boolean hasSatelliteLineOfSight();
-    method public boolean isSatelliteLineOfSight();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssSingleSatCorrection> CREATOR;
     field public static final int HAS_EXCESS_PATH_LENGTH_MASK = 2; // 0x2
     field public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 4; // 0x4
+    field public static final int HAS_PROB_SAT_IS_LOS_MASK = 1; // 0x1
     field public static final int HAS_REFLECTING_PLANE_MASK = 8; // 0x8
-    field public static final int HAS_SAT_IS_LOS_MASK = 1; // 0x1
   }
 
   public static class GnssSingleSatCorrection.Builder {
@@ -2924,9 +2924,9 @@
     method public android.location.GnssSingleSatCorrection.Builder setConstellationType(int);
     method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(float);
     method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthUncertaintyMeters(float);
+    method public android.location.GnssSingleSatCorrection.Builder setProbSatIsLos(float);
     method public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(android.location.GnssReflectingPlane);
     method public android.location.GnssSingleSatCorrection.Builder setSatId(int);
-    method public android.location.GnssSingleSatCorrection.Builder setSatIsLos(boolean);
     method public android.location.GnssSingleSatCorrection.Builder setSingleSatCorrectionFlags(int);
   }
 
@@ -3933,10 +3933,10 @@
     method public abstract void onEnrolleeSuccess(int);
     method public abstract void onFailure(int);
     method public abstract void onProgress(int);
-    field public static final int EASY_CONNECT_EVENT_FAILURE = -7; // 0xfffffff9
     field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
     field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
     field public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
+    field public static final int EASY_CONNECT_EVENT_FAILURE_GENERIC = -7; // 0xfffffff9
     field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
     field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
     field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
@@ -5106,6 +5106,8 @@
     method public static void removeOnPropertyChangedListener(android.provider.DeviceConfig.OnPropertyChangedListener);
     method public static void resetToDefaults(int, java.lang.String);
     method public static boolean setProperty(java.lang.String, java.lang.String, java.lang.String, boolean);
+    field public static final java.lang.String NAMESPACE_AUTOFILL = "autofill";
+    field public static final java.lang.String NAMESPACE_CONTENT_CAPTURE = "content_capture";
     field public static final java.lang.String NAMESPACE_GAME_DRIVER = "game_driver";
     field public static final java.lang.String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
   }
@@ -5744,6 +5746,7 @@
     field public static final java.lang.String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
     field public static final java.lang.String EXTRA_RESOLUTION_ALLOW_POLICY_RULES = "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES";
     field public static final java.lang.String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
+    field public static final java.lang.String EXTRA_RESOLUTION_CARD_ID = "android.service.euicc.extra.RESOLUTION_CARD_ID";
     field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
     field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
     field public static final java.lang.String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT";
@@ -8335,7 +8338,6 @@
   public final class ContentCaptureEvent implements android.os.Parcelable {
     method public int describeContents();
     method public long getEventTime();
-    method public int getFlags();
     method public android.view.autofill.AutofillId getId();
     method public java.util.List<android.view.autofill.AutofillId> getIds();
     method public java.lang.CharSequence getText();
diff --git a/api/test-current.txt b/api/test-current.txt
index fbba73c..0853182 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2124,6 +2124,33 @@
 
 }
 
+package android.view.inspector {
+
+  public abstract class InspectableNodeName implements java.lang.annotation.Annotation {
+  }
+
+  public abstract class InspectableProperty implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class InspectableProperty.EnumMap implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class InspectableProperty.FlagMap implements java.lang.annotation.Annotation {
+  }
+
+  public static final class InspectableProperty.ValueType extends java.lang.Enum {
+    method public static android.view.inspector.InspectableProperty.ValueType valueOf(java.lang.String);
+    method public static final android.view.inspector.InspectableProperty.ValueType[] values();
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType COLOR;
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType GRAVITY;
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType INFERRED;
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType INT_ENUM;
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType INT_FLAG;
+    enum_constant public static final android.view.inspector.InspectableProperty.ValueType NONE;
+  }
+
+}
+
 package android.widget {
 
   public abstract class AbsListView extends android.widget.AdapterView implements android.widget.Filter.FilterListener android.text.TextWatcher android.view.ViewTreeObserver.OnGlobalLayoutListener android.view.ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 72819cb..b8d748d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -384,9 +384,7 @@
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging);
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
-        STANDARD_LAYOUTS.add(R.layout.notification_template_ambient_header);
         STANDARD_LAYOUTS.add(R.layout.notification_template_header);
-        STANDARD_LAYOUTS.add(R.layout.notification_template_material_ambient);
     }
 
     /**
@@ -4570,9 +4568,7 @@
             if (p.title != null) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
                 contentView.setTextViewText(R.id.title, processTextSpans(p.title));
-                if (!p.ambient) {
-                    setTextViewColorPrimary(contentView, R.id.title, p);
-                }
+                setTextViewColorPrimary(contentView, R.id.title, p);
                 contentView.setViewLayoutWidth(R.id.title, showProgress
                         ? ViewGroup.LayoutParams.WRAP_CONTENT
                         : ViewGroup.LayoutParams.MATCH_PARENT);
@@ -4581,9 +4577,7 @@
                 int textId = showProgress ? com.android.internal.R.id.text_line_1
                         : com.android.internal.R.id.text;
                 contentView.setTextViewText(textId, processTextSpans(p.text));
-                if (!p.ambient) {
-                    setTextViewColorSecondary(contentView, textId, p);
-                }
+                setTextViewColorSecondary(contentView, textId, p);
                 contentView.setViewVisibility(textId, View.VISIBLE);
             }
 
@@ -4842,7 +4836,7 @@
             if (mN.mLargeIcon == null && mN.largeIcon != null) {
                 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
             }
-            boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon && !p.ambient;
+            boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon;
             if (showLargeIcon) {
                 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
                 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
@@ -4856,7 +4850,7 @@
          * @return if the reply icon is visible
          */
         private boolean bindReplyIcon(RemoteViews contentView, StandardTemplateParams p) {
-            boolean actionVisible = !p.hideReplyIcon && !p.ambient;
+            boolean actionVisible = !p.hideReplyIcon;
             Action action = null;
             if (actionVisible) {
                 action = findReplyAction();
@@ -4896,21 +4890,18 @@
         private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) {
             bindSmallIcon(contentView, p);
             bindHeaderAppName(contentView, p);
-            if (!p.ambient) {
-                // Ambient view does not have these
-                bindHeaderText(contentView, p);
-                bindHeaderTextSecondary(contentView, p);
-                bindHeaderChronometerAndTime(contentView, p);
-                bindProfileBadge(contentView, p);
-                bindAlertedIcon(contentView, p);
-            }
+            bindHeaderText(contentView, p);
+            bindHeaderTextSecondary(contentView, p);
+            bindHeaderChronometerAndTime(contentView, p);
+            bindProfileBadge(contentView, p);
+            bindAlertedIcon(contentView, p);
             bindActivePermissions(contentView, p);
             bindExpandButton(contentView, p);
             mN.mUsesStandardHeader = true;
         }
 
         private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) {
-            int color = p.ambient ? resolveAmbientColor(p) : getNeutralColor(p);
+            int color = getNeutralColor(p);
             contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
             contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
             contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
@@ -5021,13 +5012,12 @@
             if (isColorized(p)) {
                 setTextViewColorPrimary(contentView, R.id.app_name_text, p);
             } else {
-                contentView.setTextColor(R.id.app_name_text,
-                        p.ambient ? resolveAmbientColor(p) : getSecondaryTextColor(p));
+                contentView.setTextColor(R.id.app_name_text, getSecondaryTextColor(p));
             }
         }
 
         private boolean isColorized(StandardTemplateParams p) {
-            return p.allowColorization && !p.ambient && mN.isColorized();
+            return p.allowColorization && mN.isColorized();
         }
 
         private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
@@ -5097,7 +5087,7 @@
             List<Notification.Action> nonContextualActions = filterOutContextualActions(mActions);
 
             int N = nonContextualActions.size();
-            boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
+            boolean emphazisedMode = mN.fullScreenIntent != null;
             big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
             if (N > 0) {
                 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
@@ -5122,7 +5112,7 @@
             }
 
             CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
-            if (!p.ambient && validRemoteInput && replyText != null
+            if (validRemoteInput && replyText != null
                     && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])
                     && p.maxRemoteInputHistory > 0) {
                 boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER);
@@ -5239,11 +5229,10 @@
          * Construct a RemoteViews for the final notification header only. This will not be
          * colorized.
          *
-         * @param ambient if true, generate the header for the ambient display layout.
          * @hide
          */
-        public RemoteViews makeNotificationHeader(boolean ambient) {
-            return makeNotificationHeader(mParams.reset().ambient(ambient).fillTextsFrom(this));
+        public RemoteViews makeNotificationHeader() {
+            return makeNotificationHeader(mParams.reset().fillTextsFrom(this));
         }
 
         /**
@@ -5256,8 +5245,7 @@
             // Headers on their own are never colorized
             p.disallowColorization();
             RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
-                    p.ambient ? R.layout.notification_template_ambient_header
-                            : R.layout.notification_template_header);
+                    R.layout.notification_template_header);
             resetNotificationHeader(header);
             bindNotificationHeader(header, p);
             return header;
@@ -5269,11 +5257,7 @@
          * @hide
          */
         public RemoteViews makeAmbientNotification() {
-            RemoteViews ambient = applyStandardTemplateWithActions(
-                    R.layout.notification_template_material_ambient,
-                    mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false),
-                    null /* result */);
-            return ambient;
+            return createHeadsUpContentView(false /* increasedHeight */);
         }
 
         private void hideLine1Text(RemoteViews result) {
@@ -5377,14 +5361,8 @@
             }
             mN.extras = publicExtras;
             RemoteViews view;
-            if (ambient) {
-                publicExtras.putCharSequence(EXTRA_TITLE,
-                        mContext.getString(com.android.internal.R.string.notification_hidden_text));
-                view = makeAmbientNotification();
-            } else{
-                view = makeNotificationHeader(false /* ambient */);
-                view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
-            }
+            view = makeNotificationHeader();
+            view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
             mN.extras = savedBundle;
             mN.mLargeIcon = largeIcon;
             mN.largeIcon = largeIconLegacy;
@@ -5404,7 +5382,6 @@
         public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
             StandardTemplateParams p = mParams.reset()
                     .forceDefaultColor()
-                    .ambient(false)
                     .fillTextsFrom(this);
             if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
                 p.summaryText(createSummaryText());
@@ -5495,8 +5472,7 @@
                 if (isColorized(p)) {
                     setTextViewColorPrimary(button, R.id.action0, p);
                 } else if (getRawColor(p) != COLOR_DEFAULT && mTintActionButtons) {
-                    button.setTextColor(R.id.action0,
-                            p.ambient ? resolveAmbientColor(p) : resolveContrastColor(p));
+                    button.setTextColor(R.id.action0, resolveContrastColor(p));
                 }
             }
             button.setIntTag(R.id.action0, R.id.notification_action_index_tag,
@@ -5589,13 +5565,8 @@
         }
 
         private CharSequence processLegacyText(CharSequence charSequence) {
-            return processLegacyText(charSequence, false /* ambient */);
-        }
-
-        private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
             boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
-            boolean wantLightText = ambient;
-            if (isAlreadyLightText != wantLightText) {
+            if (isAlreadyLightText) {
                 return getColorUtil().invertCharSequenceColors(charSequence);
             } else {
                 return charSequence;
@@ -5609,9 +5580,7 @@
                 StandardTemplateParams p) {
             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
             int color;
-            if (p.ambient) {
-                color = resolveAmbientColor(p);
-            } else if (isColorized(p)) {
+            if (isColorized(p)) {
                 color = getPrimaryTextColor(p);
             } else {
                 color = resolveContrastColor(p);
@@ -5698,17 +5667,6 @@
             return mNeutralColor;
         }
 
-        int resolveAmbientColor(StandardTemplateParams p) {
-            int rawColor = getRawColor(p);
-            if (mCachedAmbientColorIsFor == rawColor && mCachedAmbientColorIsFor != COLOR_INVALID) {
-                return mCachedAmbientColor;
-            }
-            final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, rawColor);
-
-            mCachedAmbientColorIsFor = rawColor;
-            return mCachedAmbientColor = contrasted;
-        }
-
         /**
          * Apply the unstyled operations and return a new {@link Notification} object.
          * @hide
@@ -10144,7 +10102,6 @@
 
     private static class StandardTemplateParams {
         boolean hasProgress = true;
-        boolean ambient = false;
         CharSequence title;
         CharSequence text;
         CharSequence headerTextSecondary;
@@ -10157,7 +10114,6 @@
 
         final StandardTemplateParams reset() {
             hasProgress = true;
-            ambient = false;
             title = null;
             text = null;
             summaryText = null;
@@ -10213,22 +10169,15 @@
             return this;
         }
 
-        final StandardTemplateParams ambient(boolean ambient) {
-            Preconditions.checkState(title == null && text == null, "must set ambient before text");
-            this.ambient = ambient;
-            return this;
-        }
-
         final StandardTemplateParams fillTextsFrom(Builder b) {
             Bundle extras = b.mN.extras;
-            this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
+            this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE));
 
-            // Big text notifications should contain their content when viewed in ambient mode.
             CharSequence text = extras.getCharSequence(EXTRA_BIG_TEXT);
-            if (!ambient || TextUtils.isEmpty(text)) {
+            if (TextUtils.isEmpty(text)) {
                 text = extras.getCharSequence(EXTRA_TEXT);
             }
-            this.text = b.processLegacyText(text, ambient);
+            this.text = b.processLegacyText(text);
             this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT);
             return this;
         }
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 740fd7f..b7366f1 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
@@ -76,9 +77,23 @@
     }
 
     /**
-     * Starts the specified activity of the caller package in the specified profile if the caller
-     * has {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} permission and
-     * both the caller and target user profiles are in the same user group.
+     * @deprecated use {@link #startActivity(ComponentName, UserHandle)} instead.
+     *
+     * @removed
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public void startAnyActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
+        startActivity(component, targetUser);
+    }
+
+    /**
+     * Starts the specified activity of the caller package in the specified profile. Unlike
+     * {@link #startMainActivity}, this can start any activity of the caller package, not just
+     * the main activity.
+     * The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
+     * permission and both the caller and target user profiles must be in the same profile group.
      *
      * @param component The ComponentName of the activity to launch. It must be exported.
      * @param targetUser The UserHandle of the profile, must be one of the users returned by
@@ -88,7 +103,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)
-    public void startAnyActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
+    public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
                     mContext.getPackageName(), component, targetUser.getIdentifier(), false);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 766c566..983ea9f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -136,17 +136,6 @@
     public static final String EXTRA_PIN_ITEM_REQUEST =
             "android.content.pm.extra.PIN_ITEM_REQUEST";
 
-    /**
-     * Metadata key that specifies vouched certs, so any apps signed by a cert in vouched certs
-     * will not show hidden icon in launcher even it does not have a launcher visible activity.
-     *
-     * If an app has this metadata in manifest, it won't be eligible to hide its icon even if its
-     * cert is in vouched certs list.
-     *
-     * @hide
-     */
-    public static final String VOUCHED_CERTS_KEY = "vouched_certs";
-
     private final Context mContext;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final ILauncherApps mService;
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 77796d9..d8564d5 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -855,13 +855,9 @@
             try {
                 if (file.endsWith(".xml")) {
                     if (file.startsWith("res/color/")) {
-                        ColorStateList csl = loadColorStateList(wrapper, value, id, null);
-                        dr = (csl != null ? new ColorStateListDrawable(csl) : null);
+                        dr = loadColorOrXmlDrawable(wrapper, value, id, density, file);
                     } else {
-                        final XmlResourceParser rp = loadXmlResourceParser(
-                                file, id, value.assetCookie, "drawable");
-                        dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
-                        rp.close();
+                        dr = loadXmlDrawable(wrapper, value, id, density, file);
                     }
                 } else {
                     final InputStream is = mAssets.openNonAsset(
@@ -915,6 +911,33 @@
         return dr;
     }
 
+    private Drawable loadColorOrXmlDrawable(@NonNull Resources wrapper, @NonNull TypedValue value,
+            int id, int density, String file) {
+        try {
+            ColorStateList csl = loadColorStateList(wrapper, value, id, null);
+            return new ColorStateListDrawable(csl);
+        } catch (NotFoundException originalException) {
+            // If we fail to load as color, try as normal XML drawable
+            try {
+                return loadXmlDrawable(wrapper, value, id, density, file);
+            } catch (Exception ignored) {
+                // If fallback also fails, throw the original exception
+                throw originalException;
+            }
+        }
+    }
+
+    private Drawable loadXmlDrawable(@NonNull Resources wrapper, @NonNull TypedValue value,
+            int id, int density, String file)
+            throws IOException, XmlPullParserException {
+        try (
+                XmlResourceParser rp =
+                        loadXmlResourceParser(file, id, value.assetCookie, "drawable")
+        ) {
+            return Drawable.createFromXmlForDensity(wrapper, rp, density, null);
+        }
+    }
+
     /**
      * Loads a font from XML or resources stream.
      */
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index aefcf8c..70a9f08 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -104,6 +104,16 @@
         return context.getResources().getBoolean(R.bool.config_displayWhiteBalanceAvailable);
     }
 
+    /**
+     * Check if the color transforms are color accelerated. Some transforms are experimental only
+     * on non-accelerated platforms due to the performance implications.
+     *
+     * @hide
+     */
+    public static boolean isColorTransformAccelerated(Context context) {
+        return context.getResources().getBoolean(R.bool.config_setColorTransformAccelerated);
+    }
+
     private static class ColorDisplayManagerInternal {
 
         private static ColorDisplayManagerInternal sInstance;
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index e84a518..ca39051 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -846,7 +846,16 @@
         return contains(dir.getAbsolutePath(), file.getAbsolutePath());
     }
 
-    /** {@hide} */
+    /**
+     * Test if a file lives under the given directory, either as a direct child
+     * or a distant grandchild.
+     * <p>
+     * Both files <em>must</em> have been resolved using
+     * {@link File#getCanonicalFile()} to avoid symlink or path traversal
+     * attacks.
+     *
+     * @hide
+     */
     public static boolean contains(String dirPath, String filePath) {
         if (dirPath.equals(filePath)) {
             return true;
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index af8146e..9015703 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -60,6 +60,24 @@
     public static final String NAMESPACE_GAME_DRIVER = "game_driver";
 
     /**
+     * Namespace for autofill feature that provides suggestions across all apps when
+     * the user interacts with input fields.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_AUTOFILL = "autofill";
+
+    /**
+     * Namespace for content capture feature used by on-device machine intelligence
+     * to provide suggestions in a privacy-safe manner.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
+
+    /**
      * Namespace for all input-related features that are used at the native level.
      * These features are applied at reboot.
      *
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fb511e3..d5de13c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10958,6 +10958,21 @@
         public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
 
         /**
+         * Whether to try cellular data recovery when a bad network is reported.
+         *
+         * @hide
+         */
+        public static final String DATA_STALL_RECOVERY_ON_BAD_NETWORK =
+                "data_stall_recovery_on_bad_network";
+
+        /**
+         * Minumim duration in millisecodns between cellular data recovery attempts
+         *
+         * @hide
+         */
+        public static final String MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS =
+                "min_duration_between_recovery_steps";
+        /**
          * Whether network service discovery is enabled.
          *
          * @hide
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 4be1f9c..4dc10cd 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -184,6 +184,12 @@
     public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED =
             "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
 
+    /**
+     * Intent extra set for resolution requests containing an int indicating the current card Id.
+     */
+    public static final String EXTRA_RESOLUTION_CARD_ID =
+            "android.service.euicc.extra.RESOLUTION_CARD_ID";
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "RESULT_" }, value = {
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index 5718d99..70d049a 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -162,6 +162,7 @@
     private static final int FP32_EXPONENT_MASK     = 0xff;
     private static final int FP32_SIGNIFICAND_MASK  = 0x7fffff;
     private static final int FP32_EXPONENT_BIAS     = 127;
+    private static final int FP32_QNAN_MASK         = 0x400000;
 
     private static final int FP32_DENORMAL_MAGIC = 126 << 23;
     private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC);
@@ -903,6 +904,9 @@
             outM = m << 13;
             if (e == 0x1f) { // Infinite or NaN
                 outE = 0xff;
+                if (outM != 0) { // SNaNs are quieted
+                    outM |= FP32_QNAN_MASK;
+                }
             } else {
                 outE = e - FP16_EXPONENT_BIAS + FP32_EXPONENT_BIAS;
             }
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 0b39c3a..63c21f3 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -88,9 +88,8 @@
     }
 
     @Override
-    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
-            int flags) {
-        getMainCaptureSession().notifyViewTextChanged(mId, id, text, flags);
+    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
+        getMainCaptureSession().notifyViewTextChanged(mId, id, text);
     }
 
     @Override
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index a3b285d..43963c3 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -81,7 +81,6 @@
     private final @NonNull String mSessionId;
     private final int mType;
     private final long mEventTime;
-    private final int mFlags;
     private @Nullable AutofillId mId;
     private @Nullable ArrayList<AutofillId> mIds;
     private @Nullable ViewNode mNode;
@@ -90,21 +89,15 @@
     private @Nullable ContentCaptureContext mClientContext;
 
     /** @hide */
-    public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime, int flags) {
+    public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime) {
         mSessionId = sessionId;
         mType = type;
         mEventTime = eventTime;
-        mFlags = flags;
-    }
-
-    /** @hide */
-    public ContentCaptureEvent(@NonNull String sessionId, int type, int flags) {
-        this(sessionId, type, System.currentTimeMillis(), flags);
     }
 
     /** @hide */
     public ContentCaptureEvent(@NonNull String sessionId, int type) {
-        this(sessionId, type, /* flags= */ 0);
+        this(sessionId, type, System.currentTimeMillis());
     }
 
     /** @hide */
@@ -212,16 +205,6 @@
     }
 
     /**
-     * Gets optional flags associated with the event.
-     *
-     * @return either {@code 0} or
-     * {@link android.view.contentcapture.ContentCaptureSession#FLAG_USER_INPUT}.
-     */
-    public int getFlags() {
-        return mFlags;
-    }
-
-    /**
      * Gets the whole metadata of the node associated with the event.
      *
      * <p>Only set on {@link #TYPE_VIEW_APPEARED} events.
@@ -268,9 +251,6 @@
     public void dump(@NonNull PrintWriter pw) {
         pw.print("type="); pw.print(getTypeAsString(mType));
         pw.print(", time="); pw.print(mEventTime);
-        if (mFlags > 0) {
-            pw.print(", flags="); pw.print(mFlags);
-        }
         if (mId != null) {
             pw.print(", id="); pw.print(mId);
         }
@@ -300,9 +280,6 @@
         if (mType == TYPE_SESSION_STARTED && mParentSessionId != null) {
             string.append(", parent=").append(mParentSessionId);
         }
-        if (mFlags > 0) {
-            string.append(", flags=").append(mFlags);
-        }
         if (mId != null) {
             string.append(", id=").append(mId);
         }
@@ -329,7 +306,6 @@
         parcel.writeString(mSessionId);
         parcel.writeInt(mType);
         parcel.writeLong(mEventTime);
-        parcel.writeInt(mFlags);
         parcel.writeParcelable(mId, flags);
         parcel.writeTypedList(mIds);
         ViewNode.writeToParcel(parcel, mNode, flags);
@@ -350,9 +326,7 @@
             final String sessionId = parcel.readString();
             final int type = parcel.readInt();
             final long eventTime  = parcel.readLong();
-            final int flags = parcel.readInt();
-            final ContentCaptureEvent event =
-                    new ContentCaptureEvent(sessionId, type, eventTime, flags);
+            final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type, eventTime);
             final AutofillId id = parcel.readParcelable(null);
             if (id != null) {
                 event.setAutofillId(id);
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 21914e2..b620ab1 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -51,12 +51,6 @@
     private static final String TAG = ContentCaptureSession.class.getSimpleName();
 
     /**
-     * Used on {@link #notifyViewTextChanged(AutofillId, CharSequence, int)} to indicate that the
-     * text change was caused by user input (for example, through IME).
-     */
-    public static final int FLAG_USER_INPUT = 0x1;
-
-    /**
      * Initial state, when there is no session.
      *
      * @hide
@@ -375,8 +369,7 @@
      *
      * @param id of the node.
      * @param text new text.
-     * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
-     * changed by the user (for example, through the keyboard).
+     * @param flags currently ignored.
      */
     public final void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
             int flags) {
@@ -384,11 +377,11 @@
 
         if (!isContentCaptureEnabled()) return;
 
-        internalNotifyViewTextChanged(id, text, flags);
+        internalNotifyViewTextChanged(id, text);
     }
 
-    abstract void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
-            int flags);
+    abstract void internalNotifyViewTextChanged(@NonNull AutofillId id,
+            @Nullable CharSequence text);
 
     /**
      * Creates a {@link ViewStructure} for a "standard" view.
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index af56af3..103d7e6 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -490,9 +490,8 @@
     }
 
     @Override
-    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
-            int flags) {
-        notifyViewTextChanged(mId, id, text, flags);
+    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
+        notifyViewTextChanged(mId, id, text);
     }
 
     @Override
@@ -533,9 +532,9 @@
     }
 
     void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id,
-            @Nullable CharSequence text, int flags) {
+            @Nullable CharSequence text) {
         mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
-                new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+                new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED).setAutofillId(id)
                         .setText(text), /* forceFlush= */ false));
     }
 
diff --git a/core/java/android/view/inspector/InspectableNodeName.java b/core/java/android/view/inspector/InspectableNodeName.java
index ea94ad4..7b9a507 100644
--- a/core/java/android/view/inspector/InspectableNodeName.java
+++ b/core/java/android/view/inspector/InspectableNodeName.java
@@ -19,6 +19,8 @@
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.TestApi;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
@@ -39,6 +41,7 @@
  */
 @Target({TYPE})
 @Retention(SOURCE)
+@TestApi
 public @interface InspectableNodeName {
     /**
      * The display name for nodes of this type.
diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java
index a57470c..355ff1d 100644
--- a/core/java/android/view/inspector/InspectableProperty.java
+++ b/core/java/android/view/inspector/InspectableProperty.java
@@ -20,6 +20,7 @@
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.TestApi;
 import android.content.res.Resources;
 
 import java.lang.annotation.Retention;
@@ -40,6 +41,7 @@
  */
 @Target({METHOD})
 @Retention(SOURCE)
+@TestApi
 public @interface InspectableProperty {
     /**
      * The name of the property.
@@ -86,7 +88,6 @@
      *
      * @return An array of {@link EnumMap}, empty if not applicable
      * @see android.annotation.IntDef
-     * @see IntEnumMapping
      */
     EnumMap[] enumMapping() default {};
 
@@ -109,6 +110,7 @@
      */
     @Target({TYPE})
     @Retention(SOURCE)
+    @TestApi
     @interface EnumMap {
         /**
          * The string name of this enumeration value.
@@ -133,6 +135,7 @@
      */
     @Target({TYPE})
     @Retention(SOURCE)
+    @TestApi
     @interface FlagMap {
         /**
          * The string name of this flag.
@@ -167,15 +170,22 @@
      *
      * @hide
      */
+    @TestApi
     enum ValueType {
         /**
          * No special handling, property is considered to be a numeric value.
+         *
+         * @hide
          */
+        @TestApi
         NONE,
 
         /**
          * The default the annotation processor infers the value type from context.
+         *
+         * @hide
          */
+        @TestApi
         INFERRED,
 
         /**
@@ -184,7 +194,9 @@
          * This is inferred if {@link #enumMapping()} is specified.
          *
          * @see EnumMap
+         * @hide
          */
+        @TestApi
         INT_ENUM,
 
         /**
@@ -193,7 +205,9 @@
          * This is inferred if {@link #flagMapping()} is specified.
          *
          * @see FlagMap
+         * @hide
          */
+        @TestApi
         INT_FLAG,
 
         /**
@@ -203,7 +217,9 @@
          * {@link android.annotation.ColorLong} on the getter method.
          *
          * @see android.graphics.Color
+         * @hide
          */
+        @TestApi
         COLOR,
 
         /**
@@ -212,7 +228,9 @@
          * This type is not inferred, and is non-trivial to represent using {@link FlagMap}.
          *
          * @see android.view.Gravity
+         * @hide
          */
+        @TestApi
         GRAVITY
     }
 }
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 798825f..26a474c 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -22,12 +22,12 @@
 #include <utils/Log.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
-#include <cutils/sched_policy.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
 #include <processgroup/processgroup.h>
+#include <processgroup/sched_policy.h>
 
 #include <string>
 #include <vector>
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 057e02a..7aee833 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -62,13 +62,13 @@
 #include <android-base/stringprintf.h>
 #include <cutils/fs.h>
 #include <cutils/multiuser.h>
-#include <cutils/sched_policy.h>
 #include <private/android_filesystem_config.h>
 #include <utils/String8.h>
 #include <selinux/android.h>
 #include <seccomp_policy.h>
 #include <stats_event_list.h>
 #include <processgroup/processgroup.h>
+#include <processgroup/sched_policy.h>
 
 #include "core_jni_helpers.h"
 #include <nativehelper/JNIHelp.h>
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 248c57b..ea0c8e2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2210,8 +2210,9 @@
     <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
         android:protectionLevel="signature|installer" />
 
-    <!-- @SystemApi Allows an application to start an activity within its managed profile from
-         the personal profile.
+    <!-- @SystemApi Allows an application to start its own activities, but on a different profile
+         associated with the user. For example, an application running on the main profile of a user
+         can start an activity on a managed profile of that user.
          This permission is not available to third party applications.
          @hide -->
     <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
@@ -4764,6 +4765,11 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.ZramWriteback"
+                 android:exported="false"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
         <service android:name="com.android.server.backup.FullBackupJob"
                  android:exported="true"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
diff --git a/core/res/res/layout/notification_template_ambient_header.xml b/core/res/res/layout/notification_template_ambient_header.xml
deleted file mode 100644
index be5d9b4..0000000
--- a/core/res/res/layout/notification_template_ambient_header.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-
-<!-- hack to work around <include /> not being supported at the top level -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:paddingStart="@dimen/notification_extra_margin_ambient"
-    android:paddingEnd="@dimen/notification_extra_margin_ambient"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
-    <include
-        layout="@layout/notification_template_header"
-        android:theme="@style/Theme.DeviceDefault.Notification.Ambient"/>
-</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
deleted file mode 100644
index 2c6064e..0000000
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:tag="ambient"
-    android:paddingStart="@dimen/notification_extra_margin_ambient"
-    android:paddingEnd="@dimen/notification_extra_margin_ambient"
-    >
-    <include layout="@layout/notification_template_ambient_header"
-             android:theme="@style/Theme.DeviceDefault.Notification.Ambient" />
-
-    <LinearLayout
-            android:id="@+id/notification_action_list_margin_target"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_gravity="top"
-            android:layout_marginTop="@dimen/notification_content_margin_top"
-            android:layout_marginBottom="@dimen/notification_action_list_height"
-            android:paddingTop="4dp"
-            android:paddingBottom="6dp"
-            android:clipToPadding="false"
-            android:orientation="vertical">
-
-        <LinearLayout
-            android:id="@+id/notification_main_column"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="top"
-            android:layout_weight="1"
-            android:paddingStart="@dimen/notification_content_margin_start"
-            android:paddingEnd="@dimen/notification_content_margin_end"
-            android:clipToPadding="false"
-            android:minHeight="@dimen/notification_min_content_height"
-            android:orientation="vertical"
-            >
-            <TextView android:id="@+id/title"
-                android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="top|center_horizontal"
-                android:singleLine="true"
-                android:ellipsize="marquee"
-                android:fadingEdge="horizontal"
-                android:textSize="@dimen/notification_ambient_title_text_size"
-                android:textColor="#ffffffff"
-            />
-            <TextView android:id="@+id/text"
-                android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:textAppearance="@style/TextAppearance.DeviceDefault.Notification"
-                android:singleLine="false"
-                android:layout_weight="1"
-                android:gravity="top|center_horizontal"
-                android:visibility="gone"
-                android:textSize="@dimen/notification_ambient_text_size"
-                android:textColor="#eeffffff"
-                android:layout_marginTop="4dp"
-                android:ellipsize="end"
-                android:maxLines="3"
-            />
-        </LinearLayout>
-        <FrameLayout android:id="@+id/actions_container"
-                     android:layout_width="match_parent"
-                     android:layout_height="wrap_content"
-                     android:layout_gravity="bottom">
-            <com.android.internal.widget.NotificationActionListLayout
-                android:id="@+id/actions"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/notification_action_list_height"
-                android:paddingEnd="4dp"
-                android:orientation="horizontal"
-                android:gravity="center"
-                android:visibility="gone"
-            />
-        </FrameLayout>
-    </LinearLayout>
-</FrameLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c1ae026..c6f4ed0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3722,4 +3722,7 @@
 
     <!-- If the sensor that silences alerts is available or not. -->
     <bool name="config_silenceSensorAvailable">false</bool>
+
+    <!-- Enable Zram writeback feature to allow unused pages in zram be written to flash. -->
+    <bool name="config_zramWriteback">false</bool>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 05a156b..c870683 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -194,9 +194,6 @@
     <!-- The margin for text at the end of the image view for media notifications -->
     <dimen name="notification_media_image_margin_end">72dp</dimen>
 
-    <!-- The additional margin on the sides of the ambient view. -->
-    <dimen name="notification_extra_margin_ambient">16dp</dimen>
-
     <!-- The height of the notification action list -->
     <dimen name="notification_action_list_height">60dp</dimen>
 
@@ -233,9 +230,6 @@
     <!-- The bottom padding for the notification header -->
     <dimen name="notification_header_padding_bottom">16dp</dimen>
 
-    <!-- The margin at the top of the notification header when dozing. -->
-    <dimen name="notification_header_margin_top_ambient">3dp</dimen>
-
     <!-- The margin at the bottom of the notification header. -->
     <dimen name="notification_header_margin_bottom">0dp</dimen>
 
@@ -400,11 +394,6 @@
     <dimen name="notification_title_text_size">14sp</dimen>
     <!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info, Time) -->
     <dimen name="notification_subtext_size">12sp</dimen>
-    <!-- Size of notification text (see TextAppearance.StatusBar.EventContent) -->
-    <dimen name="notification_ambient_text_size">16sp</dimen>
-    <!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title) -->
-    <dimen name="notification_ambient_title_text_size">24sp</dimen>
-
     <!-- Top padding for notifications in the standard layout. -->
     <dimen name="notification_top_pad">10dp</dimen>
 
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 200ef2f..79afe69 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -271,9 +271,6 @@
     <style name="TextAppearance.DeviceDefault.Notification.Info" parent="TextAppearance.Material.Notification.Info">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
-    <style name="TextAppearance.DeviceDefault.Notification.Info.Ambient" parent="TextAppearance.Material.Notification.Info.Ambient">
-        <item name="fontFamily">@string/config_bodyFontFamily</item>
-    </style>
     <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 5a7199d..63ac0e6 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -487,10 +487,6 @@
 
     <style name="TextAppearance.Material.Notification.Time" parent="TextAppearance.Material.Notification.Info" />
 
-    <style name="TextAppearance.Material.Notification.Info.Ambient">
-        <item name="textSize">@dimen/notification_text_size</item>
-    </style>
-
     <style name="TextAppearance.Material.Notification.Emphasis">
         <item name="textColor">#66000000</item>
     </style>
@@ -1308,10 +1304,5 @@
         <item name="gravity">top</item>
     </style>
 
-    <style name="Notification.Header.Ambient">
-        <item name="layout_marginTop">@dimen/notification_header_margin_top_ambient</item>
-        <item name="gravity">top|center_horizontal</item>
-    </style>
-
 
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d36dbfc..cc4599e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3074,8 +3074,6 @@
 
   <java-symbol type="dimen" name="config_appTransitionAnimationDurationScaleDefault" />
 
-  <java-symbol type="layout" name="notification_template_material_ambient" />
-
   <!-- Network Recommendation -->
   <java-symbol type="string" name="config_defaultNetworkRecommendationProviderPackage" />
 
@@ -3235,8 +3233,6 @@
   <java-symbol type="string" name="time_picker_text_input_mode_description"/>
   <java-symbol type="string" name="time_picker_radial_mode_description"/>
 
-  <java-symbol type="layout" name="notification_template_ambient_header" />
-
   <!-- resolver activity -->
   <java-symbol type="drawable" name="resolver_icon_placeholder" />
 
@@ -3541,4 +3537,6 @@
 
   <java-symbol type="bool" name="config_skipSensorAvailable" />
   <java-symbol type="bool" name="config_silenceSensorAvailable" />
+
+  <java-symbol type="bool" name="config_zramWriteback" />
 </resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index d60313a..75a727b 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1711,8 +1711,4 @@
         <item name="notificationHeaderTextAppearance">@style/TextAppearance.DeviceDefault.Notification.Info</item>
     </style>
 
-    <style name="Theme.DeviceDefault.Notification.Ambient" parent="@style/Theme.Material.Notification.Ambient">
-        <item name="notificationHeaderTextAppearance">@style/TextAppearance.DeviceDefault.Notification.Info.Ambient</item>
-    </style>
-
 </resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index ccaf041..6b7698e 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1341,14 +1341,6 @@
         <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size</item>
     </style>
 
-    <!-- Theme for inflating ambient notification -->
-    <style name="Theme.Material.Notification.Ambient">
-        <item name="notificationHeaderStyle">@style/Notification.Header.Ambient</item>
-        <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info.Ambient</item>
-        <item name="notificationHeaderAppNameVisibility">gone</item>
-        <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size_ambient</item>
-    </style>
-
     <!-- Default theme for Settings and activities launched from Settings. -->
     <style name="Theme.Material.Settings" parent="Theme.Material.Light.LightStatusBar">
         <item name="homeAsUpIndicator">@drawable/ic_ab_back_material_settings</item>
diff --git a/core/tests/BroadcastRadioTests/Android.mk b/core/tests/BroadcastRadioTests/Android.mk
index 24f0cf0..6b0484e 100644
--- a/core/tests/BroadcastRadioTests/Android.mk
+++ b/core/tests/BroadcastRadioTests/Android.mk
@@ -25,7 +25,7 @@
 # LOCAL_SDK_VERSION := current
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test testng
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util androidx.test.rules testng
 
 LOCAL_JAVA_LIBRARIES := android.test.base
 
diff --git a/core/tests/BroadcastRadioTests/AndroidManifest.xml b/core/tests/BroadcastRadioTests/AndroidManifest.xml
index d9b5522..ce12cc9 100644
--- a/core/tests/BroadcastRadioTests/AndroidManifest.xml
+++ b/core/tests/BroadcastRadioTests/AndroidManifest.xml
@@ -24,7 +24,7 @@
     </application>
 
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="android.hardware.radio.tests"
         android:label="Tests for Broadcast Radio APIs" >
     </instrumentation>
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
index fdaba08..d2bd1e1 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
@@ -15,22 +15,29 @@
  */
 package android.hardware.radio.tests.functional;
 
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.testng.Assert.assertThrows;
+
 import android.Manifest;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 import android.hardware.radio.RadioTuner;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 
-import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
@@ -41,19 +48,10 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import static org.junit.Assert.*;
-import static org.junit.Assume.*;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.after;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atMost;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.testng.Assert.assertThrows;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * A test for broadcast radio API.
diff --git a/core/tests/coretests/README b/core/tests/coretests/README
index ea282a0..34beb45 100644
--- a/core/tests/coretests/README
+++ b/core/tests/coretests/README
@@ -30,9 +30,9 @@
 
   adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
   adb shell am instrument -w \
-    com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+    com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
 
-To run a tests within a specific package, add the following argument AFTER -w:
+To run a tests within a specific package, add -e AFTER -w and before the runner class:
 
     -e package android.content.pm
 
diff --git a/core/tests/coretests/res/color/drawable_in_color_dir_invalid.xml b/core/tests/coretests/res/color/drawable_in_color_dir_invalid.xml
new file mode 100644
index 0000000..8d630b0
--- /dev/null
+++ b/core/tests/coretests/res/color/drawable_in_color_dir_invalid.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- This drawable is purposely in the color directory to test a backwards compatible fallback. -->
+<layer-list
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/icon" android:drawable="@string/app_name" />
+</layer-list>
\ No newline at end of file
diff --git a/core/tests/coretests/res/color/drawable_in_color_dir_valid.xml b/core/tests/coretests/res/color/drawable_in_color_dir_valid.xml
new file mode 100644
index 0000000..d140475
--- /dev/null
+++ b/core/tests/coretests/res/color/drawable_in_color_dir_valid.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- This drawable is purposely in the color directory to test a backwards compatible fallback. -->
+<layer-list
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/background" android:drawable="@drawable/test32x24" />
+    <item android:id="@android:id/icon" android:drawable="@drawable/test16x12" />
+</layer-list>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
new file mode 100644
index 0000000..c4df88b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 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.content.res;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.graphics.drawable.ColorStateListDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ResourcesDrawableTest {
+
+    @Test
+    public void testLoadColorAsDrawable() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Resources resources = context.getResources();
+        Drawable drawable = resources.getDrawable(R.color.color1);
+        assertTrue(drawable instanceof ColorStateListDrawable);
+    }
+
+    @Test
+    public void testLoadColorAsDrawableFailureThrowsOriginalException() throws Throwable {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Resources resources = context.getResources();
+
+        Exception exception = null;
+
+        try {
+            resources.getDrawable(R.color.drawable_in_color_dir_invalid);
+        } catch (Exception e) {
+            exception = e;
+        }
+
+        assertNotNull(
+                "Loading drawable_in_color_dir_invalid should throw an exception",
+                exception
+        );
+
+        assertEquals(
+                "Can't find ColorStateList from drawable resource ID #0x"
+                        + Integer.toHexString(R.color.drawable_in_color_dir_invalid),
+                exception.getCause().getCause().getMessage()
+        );
+    }
+
+    @Test
+    public void testLoadNormalDrawableInColorDir() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Resources resources = context.getResources();
+        Drawable drawable = resources.getDrawable(R.color.drawable_in_color_dir_valid);
+        assertTrue(drawable instanceof LayerDrawable);
+    }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 93af013..0fb1c7c 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -199,6 +199,7 @@
                     Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
                     Settings.Global.DATA_STALL_EVALUATION_TYPE,
                     Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL,
+                    Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK,
                     Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD,
                     Settings.Global.DEBUG_APP,
                     Settings.Global.DEBUG_VIEW_ATTRIBUTES,
@@ -312,6 +313,7 @@
                     Settings.Global.MDC_INITIAL_MAX_RETRY,
                     Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
                     Settings.Global.MHL_POWER_CHARGE_ENABLED,
+                    Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS,
                     Settings.Global.MOBILE_DATA, // Candidate for backup?
                     Settings.Global.MOBILE_DATA_ALWAYS_ON,
                     Settings.Global.MODE_RINGER,
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 05c0df7..ff97aa1 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -151,7 +151,7 @@
         }
 
         @Override
-        void internalNotifyViewTextChanged(AutofillId id, CharSequence text, int flags) {
+        void internalNotifyViewTextChanged(AutofillId id, CharSequence text) {
             throw new UnsupportedOperationException("should not have been called");
         }
     }
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
index d161059..cc2d8d2 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
@@ -38,16 +38,11 @@
 
 LOCAL_MIN_SDK_VERSION := 8
 
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex \
-     -D jack.dex.output.multidex.legacy=true
-
 include $(BUILD_PACKAGE)
 
-ifndef LOCAL_JACK_ENABLED
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacyandexception/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
-endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
index cf8fc92..c577eef 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -38,9 +38,9 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
@@ -69,9 +69,9 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList2): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList2): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList2)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
index 2ce50b3..da40940 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
@@ -30,14 +30,13 @@
 	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
 
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.dex.output.multidex.legacy=true
 
 LOCAL_DEX_PREOPT := false
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
index 8b0c750..665e22d 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
@@ -35,9 +35,9 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
index a36c993..c827fa8 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
@@ -35,9 +35,9 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
index 6b7418c..3d6ad7d 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
@@ -35,9 +35,9 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
 	$(hide) mkdir -p $(dir $@)
-	$(MAINDEXCLASSES) $< 1>$@
+	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java
index 6c757f9..3922d2f 100644
--- a/location/java/android/location/GnssSingleSatCorrection.java
+++ b/location/java/android/location/GnssSingleSatCorrection.java
@@ -16,11 +16,14 @@
 
 package android.location;
 
+import android.annotation.FloatRange;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 /**
  * A container with measurement corrections for a single visible satellite
  *
@@ -31,9 +34,9 @@
 
     /**
      * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
-     * #mSatIsLos}.
+     * #mProbSatIsLos}.
      */
-    public static final int HAS_SAT_IS_LOS_MASK = 1 << 0;
+    public static final int HAS_PROB_SAT_IS_LOS_MASK = 1 << 0;
 
     /**
      * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
@@ -78,9 +81,11 @@
     private float mCarrierFrequencyHz;
 
     /**
-     * True if the satellite is estimated to be in Line-of-Sight condition at the given location.
+     * The probability that the satellite is estimated to be in Line-of-Sight condition at the given
+     * location.
      */
-    private boolean mSatIsLos;
+    @FloatRange(from = 0f, to = 1f)
+    private float mProbSatIsLos;
 
     /**
      * Excess path length to be subtracted from pseudorange before using it in calculating location.
@@ -103,7 +108,7 @@
         mSatId = builder.mSatId;
         mConstellationType = builder.mConstellationType;
         mCarrierFrequencyHz = builder.mCarrierFrequencyHz;
-        mSatIsLos = builder.mSatIsLos;
+        mProbSatIsLos = builder.mProbSatIsLos;
         mExcessPathLengthMeters = builder.mExcessPathLengthMeters;
         mExcessPathLengthUncertaintyMeters = builder.mExcessPathLengthUncertaintyMeters;
         mReflectingPlane = builder.mReflectingPlane;
@@ -152,9 +157,13 @@
         return mCarrierFrequencyHz;
     }
 
-    /** True if the satellite is line-of-sight */
-    public boolean isSatelliteLineOfSight() {
-        return mSatIsLos;
+    /**
+     * Returns the probability that the satellite is in line-of-sight condition at the given
+     * location.
+     */
+    @FloatRange(from = 0f, to = 1f)
+    public float getProbSatIsLos() {
+        return mProbSatIsLos;
     }
 
     /**
@@ -180,9 +189,9 @@
         return mReflectingPlane;
     }
 
-    /** Returns {@code true} if {@link #isSatelliteLineOfSight()} is valid. */
+    /** Returns {@code true} if {@link #getProbSatIsLos()} is valid. */
     public boolean hasSatelliteLineOfSight() {
-        return (mSingleSatCorrectionFlags & HAS_SAT_IS_LOS_MASK) != 0;
+        return (mSingleSatCorrectionFlags & HAS_PROB_SAT_IS_LOS_MASK) != 0;
     }
 
     /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */
@@ -215,7 +224,7 @@
                                     .setConstellationType(parcel.readInt())
                                     .setSatId(parcel.readInt())
                                     .setCarrierFrequencyHz(parcel.readFloat())
-                                    .setSatIsLos(parcel.readBoolean())
+                                    .setProbSatIsLos(parcel.readFloat())
                                     .setExcessPathLengthMeters(parcel.readFloat())
                                     .setExcessPathLengthUncertaintyMeters(parcel.readFloat())
                                     .setReflectingPlane(
@@ -239,7 +248,7 @@
         builder.append(String.format(format, "ConstellationType = ", mConstellationType));
         builder.append(String.format(format, "SatId = ", mSatId));
         builder.append(String.format(format, "CarrierFrequencyHz = ", mCarrierFrequencyHz));
-        builder.append(String.format(format, "SatIsLos = ", mSatIsLos));
+        builder.append(String.format(format, "ProbSatIsLos = ", mProbSatIsLos));
         builder.append(String.format(format, "ExcessPathLengthMeters = ", mExcessPathLengthMeters));
         builder.append(
                 String.format(
@@ -256,7 +265,7 @@
         parcel.writeInt(mConstellationType);
         parcel.writeInt(mSatId);
         parcel.writeFloat(mCarrierFrequencyHz);
-        parcel.writeBoolean(mSatIsLos);
+        parcel.writeFloat(mProbSatIsLos);
         parcel.writeFloat(mExcessPathLengthMeters);
         parcel.writeFloat(mExcessPathLengthUncertaintyMeters);
         mReflectingPlane.writeToParcel(parcel, flags);
@@ -274,7 +283,7 @@
         private int mConstellationType;
         private int mSatId;
         private float mCarrierFrequencyHz;
-        private boolean mSatIsLos;
+        private float mProbSatIsLos;
         private float mExcessPathLengthMeters;
         private float mExcessPathLengthUncertaintyMeters;
         private GnssReflectingPlane mReflectingPlane;
@@ -303,10 +312,16 @@
             return this;
         }
 
-        /** Sets the line=of-sight state of the satellite */
-        public Builder setSatIsLos(boolean satIsLos) {
-            mSatIsLos = satIsLos;
-            mSingleSatCorrectionFlags = (byte) (mSingleSatCorrectionFlags | HAS_SAT_IS_LOS_MASK);
+        /**
+         * Sets the line-of-sight probability of the satellite at the given location in the range
+         * between 0 and 1.
+         */
+        public Builder setProbSatIsLos(@FloatRange(from = 0f, to = 1f) float probSatIsLos) {
+            Preconditions.checkArgumentInRange(probSatIsLos, 0, 1,
+                    "probSatIsLos should be between 0 and 1.");
+            mProbSatIsLos = probSatIsLos;
+            mSingleSatCorrectionFlags =
+                    (byte) (mSingleSatCorrectionFlags | HAS_PROB_SAT_IS_LOS_MASK);
             return this;
         }
 
diff --git a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
index c18d58f..d6227bb 100644
--- a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
+++ b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
@@ -59,7 +59,7 @@
         assertEquals(GnssStatus.CONSTELLATION_GPS, singleSatCorrection.getConstellationType());
         assertEquals(11, singleSatCorrection.getSatId());
         assertEquals(1575430000f, singleSatCorrection.getCarrierFrequencyHz());
-        assertEquals(false, singleSatCorrection.isSatelliteLineOfSight());
+        assertEquals(0.9f, singleSatCorrection.getProbSatIsLos());
         assertEquals(50.0f, singleSatCorrection.getExcessPathLengthMeters());
         assertEquals(55.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters());
         GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane();
@@ -88,7 +88,7 @@
                 .setConstellationType(GnssStatus.CONSTELLATION_GPS)
                 .setSatId(11)
                 .setCarrierFrequencyHz(1575430000f)
-                .setSatIsLos(false)
+                .setProbSatIsLos(0.9f)
                 .setExcessPathLengthMeters(50.0f)
                 .setExcessPathLengthUncertaintyMeters(55.0f)
                 .setReflectingPlane(generateTestReflectingPlane());
diff --git a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
index 2e54ae4..f358806 100644
--- a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
+++ b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
@@ -44,7 +44,7 @@
         assertEquals(GnssStatus.CONSTELLATION_GALILEO, singleSatCorrection.getConstellationType());
         assertEquals(12, singleSatCorrection.getSatId());
         assertEquals(1575420000f, singleSatCorrection.getCarrierFrequencyHz());
-        assertEquals(true, singleSatCorrection.isSatelliteLineOfSight());
+        assertEquals(0.1f, singleSatCorrection.getProbSatIsLos());
         assertEquals(10.0f, singleSatCorrection.getExcessPathLengthMeters());
         assertEquals(5.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters());
         GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane();
@@ -58,7 +58,7 @@
                 .setConstellationType(singleSatCorr.getConstellationType())
                 .setSatId(singleSatCorr.getSatId())
                 .setCarrierFrequencyHz(singleSatCorr.getCarrierFrequencyHz())
-                .setSatIsLos(singleSatCorr.isSatelliteLineOfSight())
+                .setProbSatIsLos(singleSatCorr.getProbSatIsLos())
                 .setExcessPathLengthMeters(singleSatCorr.getExcessPathLengthMeters())
                 .setExcessPathLengthUncertaintyMeters(
                         singleSatCorr.getExcessPathLengthUncertaintyMeters())
@@ -72,7 +72,7 @@
                         .setConstellationType(GnssStatus.CONSTELLATION_GALILEO)
                         .setSatId(12)
                         .setCarrierFrequencyHz(1575420000f)
-                        .setSatIsLos(true)
+                        .setProbSatIsLos(0.1f)
                         .setExcessPathLengthMeters(10.0f)
                         .setExcessPathLengthUncertaintyMeters(5.0f)
                         .setReflectingPlane(GnssReflectingPlaneTest.generateTestReflectingPlane());
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 8ec0e35..9ac147b 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -150,7 +150,7 @@
      * consumer end-points. For example, if the application intends to send the images to
      * {@link android.media.MediaCodec} or {@link android.media.MediaRecorder} for hardware video
      * encoding, the format and usage flag combination needs to be
-     * {@link ImageFormat#PRIVATE PRIVATE} and {@link HardwareBuffer#USAGE0_VIDEO_ENCODE}. When an
+     * {@link ImageFormat#PRIVATE PRIVATE} and {@link HardwareBuffer#USAGE_VIDEO_ENCODE}. When an
      * {@link ImageReader} object is created with a valid size and such format/usage flag
      * combination, the application can send the {@link Image images} to an {@link ImageWriter} that
      * is created with the input {@link android.view.Surface} provided by the
@@ -173,7 +173,7 @@
      * ImageReaders using other format such as {@link ImageFormat#YUV_420_888 YUV_420_888}.
      * </p>
      * <p>
-     * Note that not all format and usage flag combination is supported by the
+     * Note that not all format and usage flag combinations are supported by the
      * {@link ImageReader}. Below are the supported combinations by the {@link ImageReader}
      * (assuming the consumer end-points support the such image consumption, e.g., hardware video
      * encoding).
@@ -186,13 +186,13 @@
      *   <td>non-{@link android.graphics.ImageFormat#PRIVATE PRIVATE} formats defined by
      *   {@link android.graphics.ImageFormat ImageFormat} or
      *   {@link android.graphics.PixelFormat PixelFormat}</td>
-     *   <td>{@link HardwareBuffer#USAGE0_CPU_READ} or
-     *   {@link HardwareBuffer#USAGE0_CPU_READ_OFTEN}</td>
+     *   <td>{@link HardwareBuffer#USAGE_CPU_READ_RARELY} or
+     *   {@link HardwareBuffer#USAGE_CPU_READ_OFTEN}</td>
      * </tr>
      * <tr>
      *   <td>{@link android.graphics.ImageFormat#PRIVATE}</td>
-     *   <td>{@link HardwareBuffer#USAGE0_VIDEO_ENCODE} or
-     *   {@link HardwareBuffer#USAGE0_GPU_SAMPLED_IMAGE}, or combined</td>
+     *   <td>{@link HardwareBuffer#USAGE_VIDEO_ENCODE} or
+     *   {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE}, or combined</td>
      * </tr>
      * </table>
      * Using other combinations may result in {@link IllegalArgumentException}.
@@ -208,11 +208,10 @@
      *            become available for access through {@link #acquireLatestImage()} or
      *            {@link #acquireNextImage()}. Must be greater than 0.
      * @param usage The intended usage of the images produced by this ImageReader. It needs
-     *            to be one of the Usage0 defined by {@link HardwareBuffer}, or an
+     *            to be one of the Usage defined by {@link HardwareBuffer}, or an
      *            {@link IllegalArgumentException} will be thrown.
      * @see Image
      * @see HardwareBuffer
-     * @hide
      */
     public static ImageReader newInstance(int width, int height, int format, int maxImages,
             long usage) {
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 4c0153f..dd09afc 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -168,7 +168,6 @@
      *            {@link ImageFormat} or {@link PixelFormat}.
      *
      * @return a new ImageWriter instance.
-     * @hide
      */
     public static ImageWriter newInstance(Surface surface, int maxImages, int format) {
         if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 9245c30..e2ba23e 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -47,8 +47,6 @@
     <dimen name="bottom_text_spacing_digital">0dp</dimen>
     <!-- Slice subtitle -->
     <dimen name="widget_label_font_size">16dp</dimen>
-    <!-- Slice offset when pulsing -->
-    <dimen name="widget_pulsing_bottom_padding">48dp</dimen>
     <!-- Clock without header -->
     <dimen name="widget_big_font_size">64dp</dimen>
     <!-- Clock with header -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5a00b45..ab0bbe1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -66,9 +66,7 @@
     <item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item>
 
     <dimen name="group_overflow_number_size">@*android:dimen/notification_text_size</dimen>
-    <dimen name="group_overflow_number_size_dark">16sp</dimen>
     <dimen name="group_overflow_number_padding">@*android:dimen/notification_content_margin_end</dimen>
-    <dimen name="group_overflow_number_extra_padding_dark">@*android:dimen/notification_extra_margin_ambient</dimen>
 
     <!-- max height of a notification such that the content can still fade out when closing -->
     <dimen name="max_notification_fadeout_height">100dp</dimen>
@@ -94,9 +92,6 @@
     <!-- Height of a large notification in the status bar -->
     <dimen name="notification_max_height">294dp</dimen>
 
-    <!-- Height of an ambient notification on ambient display -->
-    <dimen name="notification_ambient_height">400dp</dimen>
-
     <!-- Height of a heads up notification in the status bar for legacy custom views -->
     <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 22a0b36..fa675b7 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -64,48 +64,16 @@
         <item name="hybridNotificationTextStyle">@style/hybrid_notification_text</item>
     </style>
 
-    <style name="HybridNotification.Ambient">
-        <item name="hybridNotificationStyle">@style/hybrid_notification_ambient</item>
-        <item name="hybridNotificationTitleStyle">@style/hybrid_notification_title_ambient</item>
-        <item name="hybridNotificationTextStyle">@style/hybrid_notification_text_ambient</item>
-    </style>
-
-    <style name="hybrid_notification_ambient">
-        <item name="android:paddingStart">@*android:dimen/notification_extra_margin_ambient</item>
-        <item name="android:paddingEnd">@*android:dimen/notification_extra_margin_ambient</item>
-        <item name="android:orientation">vertical</item>
-    </style>
-
     <style name="hybrid_notification">
         <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item>
         <item name="android:paddingEnd">12dp</item>
     </style>
 
-    <style name="hybrid_notification_title_ambient">
-        <item name="android:layout_marginTop">@*android:dimen/notification_header_margin_top_ambient</item>
-        <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item>
-        <item name="android:paddingEnd">@*android:dimen/notification_content_margin_end</item>
-        <item name="android:textAppearance">@*android:style/Notification.Header.Ambient</item>
-        <item name="android:layout_gravity">top|center_horizontal</item>
-        <item name="android:textSize">@*android:dimen/notification_ambient_title_text_size</item>
-        <item name="android:textColor">#ffffffff</item>
-    </style>
-
     <style name="hybrid_notification_title">
         <item name="android:paddingEnd">4dp</item>
         <item name="android:textAppearance">@*android:style/TextAppearance.Material.Notification.Title</item>
     </style>
 
-    <style name="hybrid_notification_text_ambient">
-        <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item>
-        <item name="android:paddingEnd">@*android:dimen/notification_content_margin_end</item>
-        <item name="android:textSize">@*android:dimen/notification_ambient_text_size</item>
-        <item name="android:textColor">#eeffffff</item>
-        <item name="android:gravity">top|center_horizontal</item>
-        <item name="android:ellipsize">end</item>
-        <item name="android:maxLines">3</item>
-    </style>
-
     <style name="hybrid_notification_text"
            parent="@*android:style/Widget.Material.Notification.Text">
         <item name="android:paddingEnd">4dp</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 669e6ff..bac7844 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -90,7 +90,7 @@
      */
     private Runnable mContentChangeListener;
     private Slice mSlice;
-    private boolean mPulsing;
+    private boolean mHasHeader;
 
     public KeyguardSliceView(Context context) {
         this(context, null, 0);
@@ -150,10 +150,18 @@
         Dependency.get(ConfigurationController.class).removeCallback(this);
     }
 
+    /**
+     * Returns whether the current visible slice has a title/header.
+     */
+    public boolean hasHeader() {
+        return mHasHeader;
+    }
+
     private void showSlice() {
         Trace.beginSection("KeyguardSliceView#showSlice");
-        if (mPulsing || mSlice == null) {
+        if (mSlice == null) {
             mRow.setVisibility(GONE);
+            mHasHeader = false;
             if (mContentChangeListener != null) {
                 mContentChangeListener.run();
             }
@@ -162,7 +170,7 @@
 
         ListContent lc = new ListContent(getContext(), mSlice);
         SliceContent headerContent = lc.getHeader();
-        boolean hasHeader = headerContent != null
+        mHasHeader = headerContent != null
                 && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
         List<SliceContent> subItems = new ArrayList<>();
         for (int i = 0; i < lc.getRowItems().size(); i++) {
@@ -177,7 +185,7 @@
         mClickActions.clear();
         final int subItemsCount = subItems.size();
         final int blendedColor = getTextColor();
-        final int startIndex = hasHeader ? 1 : 0; // First item is header; skip it
+        final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
         mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
         for (int i = startIndex; i < subItemsCount; i++) {
             RowContent rc = (RowContent) subItems.get(i);
@@ -189,7 +197,7 @@
                 button = new KeyguardSliceButton(mContext);
                 button.setTextColor(blendedColor);
                 button.setTag(itemTag);
-                final int viewIndex = i - (hasHeader ? 1 : 0);
+                final int viewIndex = i - (mHasHeader ? 1 : 0);
                 mRow.addView(button, viewIndex);
             }
 
@@ -234,18 +242,6 @@
         Trace.endSection();
     }
 
-    public void setPulsing(boolean pulsing, boolean animate) {
-        mPulsing = pulsing;
-        LayoutTransition transition = getLayoutTransition();
-        if (!animate) {
-            setLayoutTransition(null);
-        }
-        showSlice();
-        if (!animate) {
-            setLayoutTransition(transition);
-        }
-    }
-
     public void setDarkAmount(float darkAmount) {
         mDarkAmount = darkAmount;
         mRow.setDarkAmount(darkAmount);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 7ae4c41..bb549ad 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -72,7 +72,6 @@
 
     private ArraySet<View> mVisibleInDoze;
     private boolean mPulsing;
-    private boolean mWasPulsing;
     private float mDarkAmount = 0;
     private int mTextColor;
     private int mLastLayoutHeight;
@@ -210,7 +209,7 @@
     private void onSliceContentChanged() {
         LinearLayout.LayoutParams layoutParams =
                 (LinearLayout.LayoutParams) mClockView.getLayoutParams();
-        layoutParams.bottomMargin = mPulsing ? mSmallClockPadding : 0;
+        layoutParams.bottomMargin = mKeyguardSlice.hasHeader() ? mSmallClockPadding : 0;
         mClockView.setLayoutParams(layoutParams);
     }
 
@@ -220,16 +219,16 @@
     @Override
     public void onLayoutChange(View view, int left, int top, int right, int bottom,
             int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        int heightOffset = mPulsing || mWasPulsing ? 0 : getHeight() - mLastLayoutHeight;
+        boolean smallClock = mKeyguardSlice.hasHeader();
+        int heightOffset = smallClock ? 0 : getHeight() - mLastLayoutHeight;
         long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
-        long delay = mPulsing || mWasPulsing ? 0 : duration / 4;
-        mWasPulsing = false;
+        long delay = smallClock ? 0 : duration / 4;
 
         boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null
                 && mKeyguardSlice.getLayoutTransition().isRunning();
         if (view == mClockView) {
-            float clockScale = mPulsing ? mSmallClockScale : 1;
-            Paint.Style style = mPulsing ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL;
+            float clockScale = smallClock ? mSmallClockScale : 1;
+            Paint.Style style = smallClock ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL;
             mClockView.animate().cancel();
             if (shouldAnimate) {
                 mClockView.setY(oldTop + heightOffset);
@@ -434,15 +433,11 @@
         }
     }
 
-    public void setPulsing(boolean pulsing, boolean animate) {
+    public void setPulsing(boolean pulsing) {
         if (mPulsing == pulsing) {
             return;
         }
-        if (mPulsing) {
-            mWasPulsing = true;
-        }
         mPulsing = pulsing;
-        mKeyguardSlice.setPulsing(pulsing, animate);
         updateDozeVisibleViews();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index bd9ca1a..9ef9c94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -65,12 +65,12 @@
     private static final String TAG = "NotificationShelf";
     private static final long SHELF_IN_TRANSLATION_DURATION = 200;
 
-    private boolean mDark;
     private NotificationIconContainer mShelfIcons;
     private int[] mTmp = new int[2];
     private boolean mHideBackground;
     private int mIconAppearTopPadding;
     private int mShelfAppearTranslation;
+    private float mDarkShelfPadding;
     private int mStatusBarHeight;
     private int mStatusBarPaddingStart;
     private AmbientState mAmbientState;
@@ -140,6 +140,7 @@
         mStatusBarPaddingStart = res.getDimensionPixelOffset(R.dimen.status_bar_padding_start);
         mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height);
         mShelfAppearTranslation = res.getDimensionPixelSize(R.dimen.shelf_appear_translation);
+        mDarkShelfPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
 
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
         layoutParams.height = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
@@ -165,11 +166,11 @@
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
-        super.setDark(dark, fade, delay);
         if (mDark == dark) return;
-        mDark = dark;
+        super.setDark(dark, fade, delay);
         mShelfIcons.setDark(dark, fade, delay);
         updateInteractiveness();
+        updateOutline();
     }
 
     /**
@@ -218,10 +219,9 @@
 
             float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
                     getFullyClosedTranslation());
-            float darkTranslation = mAmbientState.getDarkTopPadding();
             float yRatio = mAmbientState.hasPulsingNotifications() ?
                     0 : mAmbientState.getDarkAmount();
-            viewState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
+            viewState.yTranslation = awakenTranslation + mDarkShelfPadding * yRatio;
             viewState.zTranslation = ambientState.getBaseZHeight();
             // For the small display size, it's not enough to make the icon not covered by
             // the top cutout so the denominator add the height of cutout.
@@ -763,18 +763,14 @@
         }
     }
 
-    public boolean hidesBackground() {
-        return mHideBackground;
-    }
-
     @Override
     protected boolean needsOutline() {
-        return !mHideBackground && super.needsOutline();
+        return !mHideBackground && !mDark && super.needsOutline();
     }
 
     @Override
     protected boolean shouldHideBackground() {
-        return super.shouldHideBackground() || mHideBackground;
+        return super.shouldHideBackground() || mHideBackground || mDark;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 8b0a682..c34d567 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -105,7 +105,7 @@
     private final DoubleTapHelper mDoubleTapHelper;
 
     private boolean mDimmed;
-    private boolean mDark;
+    protected boolean mDark;
 
     protected int mBgTint = NO_COLOR;
     private float mBgAlpha = 1f;
@@ -140,7 +140,6 @@
     private FalsingManager mFalsingManager;
 
     private float mNormalBackgroundVisibilityAmount;
-    private ValueAnimator mFadeInFromDarkAnimator;
     private float mDimmedBackgroundFadeInAmount = -1;
     private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater
             = new ValueAnimator.AnimatorUpdateListener() {
@@ -150,22 +149,6 @@
             mDimmedBackgroundFadeInAmount = mBackgroundDimmed.getAlpha();
         }
     };
-    private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            super.onAnimationEnd(animation);
-            mFadeInFromDarkAnimator = null;
-            mDimmedBackgroundFadeInAmount = -1;
-            updateBackground();
-        }
-    };
-    private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener
-            = new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            updateOutlineAlpha();
-        }
-    };
     private FakeShadowView mFakeShadow;
     private int mCurrentBackgroundTint;
     private int mTargetTint;
@@ -465,22 +448,11 @@
         mDark = dark;
         updateBackground();
         updateBackgroundTint(false);
-        if (!dark && fade && !shouldHideBackground()) {
-            fadeInFromDark(delay);
-        }
-        updateOutlineAlpha();
     }
 
     private void updateOutlineAlpha() {
-        if (mDark) {
-            setOutlineAlpha(0f);
-            return;
-        }
         float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
         alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
-        if (mFadeInFromDarkAnimator != null) {
-            alpha *= mFadeInFromDarkAnimator.getAnimatedFraction();
-        }
         setOutlineAlpha(alpha);
     }
 
@@ -638,36 +610,6 @@
     }
 
     /**
-     * Fades in the background when exiting dark mode.
-     */
-    private void fadeInFromDark(long delay) {
-        final View background = mDimmed ? mBackgroundDimmed : mBackgroundNormal;
-        background.setAlpha(0f);
-        mBackgroundVisibilityUpdater.onAnimationUpdate(null);
-        background.animate()
-                .alpha(1f)
-                .setDuration(DARK_ANIMATION_LENGTH)
-                .setStartDelay(delay)
-                .setInterpolator(Interpolators.ALPHA_IN)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationCancel(Animator animation) {
-                        // Jump state if we are cancelled
-                        background.setAlpha(1f);
-                    }
-                })
-                .setUpdateListener(mBackgroundVisibilityUpdater)
-                .start();
-        mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f);
-        mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH);
-        mFadeInFromDarkAnimator.setStartDelay(delay);
-        mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        mFadeInFromDarkAnimator.addListener(mFadeInEndListener);
-        mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener);
-        mFadeInFromDarkAnimator.start();
-    }
-
-    /**
      * Fades the background when the dimmed state changes.
      */
     private void fadeDimmedBackground() {
@@ -708,9 +650,7 @@
             public void onAnimationEnd(Animator animation) {
                 updateBackground();
                 mBackgroundAnimator = null;
-                if (mFadeInFromDarkAnimator == null) {
-                    mDimmedBackgroundFadeInAmount = -1;
-                }
+                mDimmedBackgroundFadeInAmount = -1;
             }
         });
         mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
@@ -736,7 +676,7 @@
             mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE);
         } else if (mDimmed) {
             // When groups are animating to the expanded state from the lockscreen, show the
-            // normal background instead of the dimmed background
+            // normal background instead of the dimmed background.
             final boolean dontShowDimmed = isGroupExpansionChanging() && isChildInGroup();
             mBackgroundDimmed.setVisibility(dontShowDimmed ? View.INVISIBLE : View.VISIBLE);
             mBackgroundNormal.setVisibility((mActivated || dontShowDimmed)
@@ -760,7 +700,7 @@
     }
 
     protected boolean shouldHideBackground() {
-        return mDark;
+        return false;
     }
 
     private void cancelFadeAnimations() {
@@ -1023,14 +963,11 @@
 
     /**
      * @param withTint should a possible tint be factored in?
-     * @param withOverRide should the value be interpolated with {@link #mOverrideTint}
+     * @param withOverride should the value be interpolated with {@link #mOverrideTint}
      * @return the calculated background color
      */
-    private int calculateBgColor(boolean withTint, boolean withOverRide) {
-        if (withTint && mDark) {
-            return getContext().getColor(R.color.notification_material_background_dark_color);
-        }
-        if (withOverRide && mOverrideTint != NO_COLOR) {
+    private int calculateBgColor(boolean withTint, boolean withOverride) {
+        if (withOverride && mOverrideTint != NO_COLOR) {
             int defaultTint = calculateBgColor(withTint, false);
             return NotificationUtils.interpolateColors(defaultTint, mOverrideTint, mOverrideAmount);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index df0189f..296c061 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -132,7 +132,6 @@
     }
 
     private LayoutListener mLayoutListener;
-    private boolean mDark;
     private boolean mLowPriorityStateUpdated;
     private final NotificationInflater mNotificationInflater;
     private int mIconTransformContentShift;
@@ -146,7 +145,6 @@
     private int mNotificationMinHeight;
     private int mNotificationMinHeightLarge;
     private int mNotificationMaxHeight;
-    private int mNotificationAmbientHeight;
     private int mIncreasedPaddingBetweenElements;
     private int mNotificationLaunchHeight;
     private boolean mMustStayOnScreen;
@@ -677,8 +675,7 @@
         if (headsUpWrapper != null) {
             headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight());
         }
-        layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight,
-                mNotificationAmbientHeight);
+        layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight, headsUpHeight);
     }
 
     public StatusBarNotification getStatusBarNotification() {
@@ -1647,8 +1644,6 @@
                 R.dimen.notification_min_height_increased);
         mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_height);
-        mNotificationAmbientHeight = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_ambient_height);
         mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_heads_up_height_legacy);
         mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
@@ -2008,8 +2003,10 @@
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
+        if (mDark == dark) {
+            return;
+        }
         super.setDark(dark, fade, delay);
-        mDark = dark;
         if (!mIsAmbientPulsing) {
             // Only fade the showing view of the pulsing notification.
             fade = false;
@@ -2018,9 +2015,6 @@
         if (showing != null) {
             showing.setDark(dark, fade, delay);
         }
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.setDark(dark, fade, delay);
-        }
         updateShelfIconColor();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
index 33badaf..90ff4a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
@@ -39,14 +39,10 @@
     private final NotificationDozeHelper mDozer;
     private final ViewGroup mParent;
 
-    private float mOverflowNumberSizeDark;
-    private int mOverflowNumberPaddingDark;
     private float mOverflowNumberSize;
     private int mOverflowNumberPadding;
 
     private int mOverflowNumberColor;
-    private int mOverflowNumberColorDark;
-    private float mDarkAmount = 0f;
 
     public HybridGroupManager(Context ctx, ViewGroup parent) {
         mContext = ctx;
@@ -59,12 +55,8 @@
         Resources res = mContext.getResources();
         mOverflowNumberSize = res.getDimensionPixelSize(
                 R.dimen.group_overflow_number_size);
-        mOverflowNumberSizeDark = res.getDimensionPixelSize(
-                R.dimen.group_overflow_number_size_dark);
         mOverflowNumberPadding = res.getDimensionPixelSize(
                 R.dimen.group_overflow_number_padding);
-        mOverflowNumberPaddingDark = mOverflowNumberPadding + res.getDimensionPixelSize(
-                R.dimen.group_overflow_number_extra_padding_dark);
     }
 
     private HybridNotificationView inflateHybridViewWithStyle(int style) {
@@ -86,13 +78,11 @@
     }
 
     private void updateOverFlowNumberColor(TextView numberView) {
-        numberView.setTextColor(NotificationUtils.interpolateColors(
-                mOverflowNumberColor, mOverflowNumberColorDark, mDarkAmount));
+        numberView.setTextColor(mOverflowNumberColor);
     }
 
-    public void setOverflowNumberColor(TextView numberView, int colorRegular, int colorDark) {
+    public void setOverflowNumberColor(TextView numberView, int colorRegular) {
         mOverflowNumberColor = colorRegular;
-        mOverflowNumberColorDark = colorDark;
         if (numberView != null) {
             updateOverFlowNumberColor(numberView);
         }
@@ -107,7 +97,7 @@
     public HybridNotificationView bindAmbientFromNotification(HybridNotificationView reusableView,
             Notification notification) {
         return bindFromNotificationWithStyle(reusableView, notification,
-                R.style.HybridNotification_Ambient);
+                R.style.HybridNotification);
     }
 
     private HybridNotificationView bindFromNotificationWithStyle(
@@ -150,6 +140,11 @@
                 R.plurals.notification_group_overflow_description, number), number);
 
         reusableView.setContentDescription(contentDescription);
+        reusableView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mOverflowNumberSize);
+        reusableView.setPaddingRelative(reusableView.getPaddingStart(),
+                reusableView.getPaddingTop(), mOverflowNumberPadding,
+                reusableView.getPaddingBottom());
+        updateOverFlowNumberColor(reusableView);
         return reusableView;
     }
 
@@ -163,16 +158,4 @@
         }
         return titleView;
     }
-
-    public void setOverflowNumberDark(TextView view, boolean dark, boolean fade, long delay) {
-        mDozer.setIntensityDark((f)->{
-            mDarkAmount = f;
-            updateOverFlowNumberColor(view);
-        }, dark, fade, delay, view);
-        view.setTextSize(TypedValue.COMPLEX_UNIT_PX,
-                dark ? mOverflowNumberSizeDark : mOverflowNumberSize);
-        int paddingEnd = dark ? mOverflowNumberPaddingDark : mOverflowNumberPadding;
-        view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), paddingEnd,
-                view.getPaddingBottom());
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index cbec37e..1b5013d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -75,7 +75,6 @@
     private int mIntrinsicPadding;
     private int mExpandAnimationTopChange;
     private ExpandableNotificationRow mExpandingNotification;
-    private int mDarkTopPadding;
     private float mDarkAmount;
     private boolean mAppearing;
 
@@ -351,7 +350,8 @@
     }
 
     public boolean hasPulsingNotifications() {
-        return mPulsing;
+        return mPulsing && mAmbientPulseManager != null
+                && mAmbientPulseManager.hasNotifications();
     }
 
     public void setPulsing(boolean hasPulsing) {
@@ -458,14 +458,6 @@
         return mDarkAmount != 0;
     }
 
-    public void setDarkTopPadding(int darkTopPadding) {
-        mDarkTopPadding = darkTopPadding;
-    }
-
-    public int getDarkTopPadding() {
-        return mDarkTopPadding;
-    }
-
     public void setAppearing(boolean appearing) {
         mAppearing = appearing;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 5118036..8ffada4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -316,7 +316,7 @@
         StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
         final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
                 notification.getNotification());
-        RemoteViews header = builder.makeNotificationHeader(false /* ambient */);
+        RemoteViews header = builder.makeNotificationHeader();
         if (mNotificationHeader == null) {
             mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
             final View expandButton = mNotificationHeader.findViewById(
@@ -344,7 +344,7 @@
             builder = Notification.Builder.recoverBuilder(getContext(),
                     notification.getNotification());
         }
-        header = builder.makeNotificationHeader(true /* ambient */);
+        header = builder.makeNotificationHeader();
         if (mNotificationHeaderAmbient == null) {
             mNotificationHeaderAmbient = (ViewGroup) header.apply(getContext(), this);
             mNotificationHeaderWrapperAmbient = NotificationViewWrapper.wrap(getContext(),
@@ -1171,12 +1171,6 @@
         return mIsLowPriority && !mContainingNotification.isExpanded();
     }
 
-    public void setDark(boolean dark, boolean fade, long delay) {
-        if (mOverflowNumber != null) {
-            mHybridGroupManager.setOverflowNumberDark(mOverflowNumber, dark, fade, delay);
-        }
-    }
-
     public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
         if (mNotificationHeader != null) {
             removeView(mNotificationHeader);
@@ -1227,8 +1221,7 @@
 
     public void onNotificationUpdated() {
         mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
-                mContainingNotification.getNotificationColor(),
-                mContainingNotification.getNotificationColorAmbient());
+                mContainingNotification.getNotificationColor());
     }
 
     public int getPositionInLinearLayout(View childInGroup) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 9418601..c5ab9f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
 
+import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -26,26 +27,34 @@
 
 import java.util.HashSet;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A class that manages the roundness for notification views
  */
-class NotificationRoundnessManager implements OnHeadsUpChangedListener {
+@Singleton
+class NotificationRoundnessManager implements OnHeadsUpChangedListener,
+        AmbientPulseManager.OnAmbientChangedListener {
 
+    private final ActivatableNotificationView[] mFirstInSectionViews;
+    private final ActivatableNotificationView[] mLastInSectionViews;
+    private final ActivatableNotificationView[] mTmpFirstInSectionViews;
+    private final ActivatableNotificationView[] mTmpLastInSectionViews;
     private boolean mExpanded;
-    private ActivatableNotificationView[] mFirstInSectionViews;
-    private ActivatableNotificationView[] mLastInSectionViews;
-    private ActivatableNotificationView[] mTmpFirstInSectionViews;
-    private ActivatableNotificationView[] mTmpLastInSectionViews;
     private HashSet<ExpandableView> mAnimatedChildren;
     private Runnable mRoundingChangedCallback;
     private ExpandableNotificationRow mTrackedHeadsUp;
+    private ActivatableNotificationView mTrackedAmbient;
     private float mAppearFraction;
 
-    NotificationRoundnessManager() {
+    @Inject
+    NotificationRoundnessManager(AmbientPulseManager ambientPulseManager) {
         mFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
         mLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
         mTmpFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
         mTmpLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
+        ambientPulseManager.addListener(this);
     }
 
     @Override
@@ -63,6 +72,17 @@
         updateView(row, false /* animate */);
     }
 
+    @Override
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isPulsing) {
+        ActivatableNotificationView row = entry.getRow();
+        if (isPulsing) {
+            mTrackedAmbient = row;
+        } else if (mTrackedAmbient == row) {
+            mTrackedAmbient = null;
+        }
+        updateView(row, false /* animate */);
+    }
+
     private void updateView(ActivatableNotificationView view, boolean animate) {
         boolean changed = updateViewWithoutCallback(view, animate);
         if (changed) {
@@ -125,6 +145,9 @@
             // rounded.
             return 1.0f;
         }
+        if (view == mTrackedAmbient) {
+            return 1.0f;
+        }
         return 0.0f;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index df8ec91..d066567 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -201,12 +201,7 @@
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
     private int mMaxTopPadding;
-    private int mRegularTopPadding;
-    private int mDarkTopPadding;
-    // Current padding, will be either mRegularTopPadding or mDarkTopPadding
     private int mTopPadding;
-    // Distance between AOD separator and shelf
-    private int mDarkShelfPadding;
     private int mBottomMargin;
     private int mBottomInset = 0;
     private float mQsExpansionFraction;
@@ -318,7 +313,7 @@
     private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
             = new HashSet<>();
     private HeadsUpManagerPhone mHeadsUpManager;
-    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
+    private final NotificationRoundnessManager mRoundnessManager;
     private boolean mTrackingHeadsUp;
     private ScrimController mScrimController;
     private boolean mForceNoOverlappingRendering;
@@ -469,7 +464,8 @@
     public NotificationStackScrollLayout(
             @Named(VIEW_CONTEXT) Context context,
             AttributeSet attrs,
-            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress) {
+            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
+            NotificationRoundnessManager notificationRoundnessManager) {
         super(context, attrs, 0, 0);
         Resources res = getResources();
 
@@ -480,6 +476,7 @@
         }
 
         mAmbientState = new AmbientState(context);
+        mRoundnessManager = notificationRoundnessManager;
         mBgColor = context.getColor(R.color.notification_shade_background_color);
         int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
         int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
@@ -496,7 +493,6 @@
                 res.getBoolean(R.bool.config_drawNotificationBackground);
         mFadeNotificationsOnDismiss =
                 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
-        mDarkShelfPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
         mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
         mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
         addOnExpandedHeightListener(mRoundnessManager::setExpanded);
@@ -685,7 +681,7 @@
         int lockScreenTop = mSections[0].getCurrentBounds().top;
         int lockScreenBottom = mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom;
         int darkLeft = getWidth() / 2;
-        int darkTop = mRegularTopPadding;
+        int darkTop = mTopPadding;
 
         float yProgress = 1 - mInterpolatedDarkAmount;
         float xProgress = mDarkXInterpolator.getInterpolation(
@@ -953,8 +949,6 @@
 
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     private void updateAlgorithmHeightAndPadding() {
-        mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding,
-                mInterpolatedDarkAmount);
         mAmbientState.setLayoutHeight(getLayoutHeight());
         updateAlgorithmLayoutMinHeight();
         mAmbientState.setTopPadding(mTopPadding);
@@ -1092,10 +1086,8 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void setTopPadding(int topPadding, boolean animate) {
-        if (mRegularTopPadding != topPadding) {
-            mRegularTopPadding = topPadding;
-            mDarkTopPadding = topPadding + mDarkShelfPadding;
-            mAmbientState.setDarkTopPadding(mDarkTopPadding);
+        if (mTopPadding != topPadding) {
+            mTopPadding = topPadding;
             updateAlgorithmHeightAndPadding();
             updateContentHeight();
             if (animate && mAnimationsEnabled && mIsExpanded) {
@@ -2060,10 +2052,7 @@
         }
         mIntrinsicContentHeight = height;
 
-        // We don't want to use the toppadding since that might be interpolated and we want
-        // to take the final value of the animation.
-        int topPadding = mAmbientState.isFullyDark() ? mDarkTopPadding : mRegularTopPadding;
-        mContentHeight = height + topPadding + mBottomMargin;
+        mContentHeight = height + mTopPadding + mBottomMargin;
         updateScrollability();
         clampScrollPosition();
         mAmbientState.setLayoutMaxHeight(mContentHeight);
@@ -5222,9 +5211,6 @@
         // another "changeViewPosition" call is ever added.
         changeViewPosition(mShelf,
                 getChildCount() - offsetFromEnd);
-
-        // Scrim opacity varies based on notification count
-        mScrimController.setNotificationCount(getNotGoneChildCount());
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 19fce48..b4c205a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -426,6 +426,9 @@
                     mTmpState.yTranslation += mPulsingAppearingTranslation;
                     mTmpState.alpha = 0;
                     mTmpState.applyToView(changingView);
+
+                    mTmpState.copyFrom(mShelf.getViewState());
+                    mTmpState.applyToView(mShelf);
                 }
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
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 6f2b63d..1049773 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -158,8 +158,7 @@
      * @return duration in millis.
      */
     public long getWallpaperAodDuration() {
-        if (mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(UserHandle.USER_CURRENT)
-                || shouldControlScreenOff()) {
+        if (shouldControlScreenOff()) {
             return DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY;
         }
         return mAlwaysOnPolicy.wallpaperVisibilityDuration;
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 577e8d6..280dda0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -84,14 +84,6 @@
         public void onCancelled() {
             pulseFinished();
         }
-
-        /**
-         * Whether to fade out wallpaper.
-         */
-        @Override
-        public  boolean isFadeOutWallpaper() {
-            return mPulseReason == DozeLog.PULSE_REASON_DOCKING;
-        }
     };
 
     public DozeScrimController(DozeParameters dozeParameters) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index a81b7e5..c68fdd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -91,11 +91,6 @@
     private int mBurnInPreventionOffsetY;
 
     /**
-     * Clock vertical padding when pulsing.
-     */
-    private int mPulsingPadding;
-
-    /**
      * Doze/AOD transition amount.
      */
     private float mDarkAmount;
@@ -105,10 +100,6 @@
      */
     private boolean mCurrentlySecure;
 
-    /**
-     * Dozing and receiving a notification (AOD notification.)
-     */
-    private boolean mPulsing;
     private float mEmptyDragAmount;
 
     /**
@@ -123,13 +114,11 @@
                 R.dimen.burn_in_prevention_offset_x);
         mBurnInPreventionOffsetY = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_y);
-        mPulsingPadding = res.getDimensionPixelSize(
-                R.dimen.widget_pulsing_bottom_padding);
     }
 
     public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
             float panelExpansion, int parentHeight, int keyguardStatusHeight, float dark,
-            boolean secure, boolean pulsing, float emptyDragAmount) {
+            boolean secure, float emptyDragAmount) {
         mMinTopMargin = minTopMargin + mContainerTopPadding;
         mMaxShadeBottom = maxShadeBottom;
         mNotificationStackHeight = notificationStackHeight;
@@ -138,7 +127,6 @@
         mKeyguardStatusHeight = keyguardStatusHeight;
         mDarkAmount = dark;
         mCurrentlySecure = secure;
-        mPulsing = pulsing;
         mEmptyDragAmount = emptyDragAmount;
     }
 
@@ -146,7 +134,7 @@
         final int y = getClockY();
         result.clockY = y;
         result.clockAlpha = getClockAlpha(y);
-        result.stackScrollerPadding = y + (mPulsing ? mPulsingPadding : mKeyguardStatusHeight);
+        result.stackScrollerPadding = y + mKeyguardStatusHeight;
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
     }
 
@@ -185,9 +173,6 @@
     private int getClockY() {
         // Dark: Align the bottom edge of the clock at about half of the screen:
         float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
-        if (mPulsing) {
-            clockYDark -= mPulsingPadding;
-        }
         clockYDark = MathUtils.max(0, clockYDark);
 
         float clockYRegular = getExpandedClockPosition();
@@ -230,11 +215,6 @@
                 - mBurnInPreventionOffsetX;
     }
 
-    @VisibleForTesting
-    void setPulsingPadding(int padding) {
-        mPulsingPadding = padding;
-    }
-
     public static class Result {
 
         /**
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 32cc0e6..0d5ebb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -579,7 +579,6 @@
                     mKeyguardStatusView.getHeight(),
                     mInterpolatedDarkAmount,
                     mStatusBar.isKeyguardCurrentlySecure(),
-                    mPulsing,
                     mEmptyDragAmount);
             mClockPositionAlgorithm.run(mClockPositionResult);
             PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
@@ -595,7 +594,8 @@
             stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
         }
         mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
-        mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
+        int burnInXOffset = mPulsing ? 0 : mClockPositionResult.clockX;
+        mNotificationStackScroller.setAntiBurnInOffsetX(burnInXOffset);
 
         mStackScrollerMeasuringPass++;
         requestScrollerTopPaddingUpdate(animate);
@@ -2830,7 +2830,7 @@
             mAnimateNextPositionUpdate = false;
         }
         mNotificationStackScroller.setPulsing(pulsing, animatePulse);
-        mKeyguardStatusView.setPulsing(pulsing, animatePulse);
+        mKeyguardStatusView.setPulsing(pulsing);
         mKeyguardBottomArea.setPulsing(pulsing, animatePulse);
     }
 
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 3568f28..bf143c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -88,16 +88,12 @@
     /**
      * Default alpha value for most scrims.
      */
-    public static final float GRADIENT_SCRIM_ALPHA = 0.45f;
+    public static final float GRADIENT_SCRIM_ALPHA = 0.2f;
     /**
      * A scrim varies its opacity based on a busyness factor, for example
      * how many notifications are currently visible.
      */
     public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.7f;
-    /**
-     * Scrim opacity when a wallpaper doesn't support ambient mode.
-     */
-    public static final float PULSING_WALLPAPER_SCRIM_ALPHA = 0.6f;
 
     /**
      * The most common scrim, the one under the keyguard.
@@ -154,7 +150,6 @@
     private Callback mCallback;
     private boolean mWallpaperSupportsAmbientMode;
     private boolean mScreenOn;
-    private float mNotificationDensity;
 
     // Scrim blanking callbacks
     private Runnable mPendingFrameCallback;
@@ -245,7 +240,7 @@
         mCurrentInFrontTint = state.getFrontTint();
         mCurrentBehindTint = state.getBehindTint();
         mCurrentInFrontAlpha = state.getFrontAlpha();
-        mCurrentBehindAlpha = state.getBehindAlpha(mNotificationDensity);
+        mCurrentBehindAlpha = state.getBehindAlpha();
         applyExpansionToAlpha();
 
         // Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
@@ -279,8 +274,7 @@
         // Docking pulses may take a long time, wallpapers should also fade away after a while.
         if (mWallpaperSupportsAmbientMode && (
                 mDozeParameters.getAlwaysOn() && mState == ScrimState.AOD
-                        || mState == ScrimState.PULSING && mCallback != null
-                        && mCallback.isFadeOutWallpaper())) {
+                        || mState == ScrimState.PULSING && mCallback != null)) {
             if (!mWallpaperVisibilityTimedOut) {
                 mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
                         AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
@@ -416,7 +410,7 @@
             // Either darken of make the scrim transparent when you
             // pull down the shade
             float interpolatedFract = getInterpolatedFraction();
-            float alphaBehind = mState.getBehindAlpha(mNotificationDensity);
+            float alphaBehind = mState.getBehindAlpha();
             if (mDarkenWhileDragging) {
                 mCurrentBehindAlpha = MathUtils.lerp(GRADIENT_SCRIM_ALPHA_BUSY, alphaBehind,
                         interpolatedFract);
@@ -430,24 +424,6 @@
     }
 
     /**
-     * Keyguard and shade scrim opacity varies according to how many notifications are visible.
-     * @param notificationCount Number of visible notifications.
-     */
-    public void setNotificationCount(int notificationCount) {
-        final float maxNotificationDensity = 3;
-        float notificationDensity = Math.min(notificationCount / maxNotificationDensity, 1f);
-        if (mNotificationDensity == notificationDensity) {
-            return;
-        }
-        mNotificationDensity = notificationDensity;
-
-        if (mState == ScrimState.KEYGUARD) {
-            applyExpansionToAlpha();
-            scheduleUpdate();
-        }
-    }
-
-    /**
      * Sets the given drawable as the background of the scrim that shows up behind the
      * notifications.
      */
@@ -909,7 +885,7 @@
         // Backdrop event may arrive after state was already applied,
         // in this case, back-scrim needs to be re-evaluated
         if (mState == ScrimState.AOD || mState == ScrimState.PULSING) {
-            float newBehindAlpha = mState.getBehindAlpha(mNotificationDensity);
+            float newBehindAlpha = mState.getBehindAlpha();
             if (mCurrentBehindAlpha != newBehindAlpha) {
                 mCurrentBehindAlpha = newBehindAlpha;
                 updateScrims();
@@ -923,6 +899,10 @@
         }
     }
 
+    public void setPulseReason(int pulseReason) {
+        ScrimState.PULSING.setPulseReason(pulseReason);
+    }
+
     public interface Callback {
         default void onStart() {
         }
@@ -932,9 +912,6 @@
         }
         default void onCancelled() {
         }
-        default boolean isFadeOutWallpaper() {
-            return false;
-        }
     }
 
     /**
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 72519ba3..11a2d32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -18,8 +18,8 @@
 
 import android.graphics.Color;
 import android.os.Trace;
-import android.util.MathUtils;
 
+import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
@@ -57,13 +57,6 @@
             mCurrentBehindAlpha = mScrimBehindAlphaKeyguard;
             mCurrentInFrontAlpha = 0;
         }
-
-        @Override
-        public float getBehindAlpha(float busynessFactor) {
-            return MathUtils.map(0 /* start */, 1 /* stop */,
-                    mScrimBehindAlphaKeyguard, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
-                    busynessFactor);
-        }
     },
 
     /**
@@ -117,7 +110,7 @@
         }
 
         @Override
-        public float getBehindAlpha(float busyness) {
+        public float getBehindAlpha() {
             return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
         }
 
@@ -133,17 +126,17 @@
     PULSING(5) {
         @Override
         public void prepare(ScrimState previousState) {
-            mCurrentInFrontAlpha = 0;
-            mCurrentInFrontTint = Color.BLACK;
-            mCurrentBehindTint = Color.BLACK;
+            mCurrentInFrontAlpha = 0f;
+            if (mPulseReason == DozeLog.PULSE_REASON_NOTIFICATION
+                    || mPulseReason == DozeLog.PULSE_REASON_DOCKING) {
+                mCurrentBehindAlpha = previousState.getBehindAlpha();
+                mCurrentBehindTint = Color.BLACK;
+            } else {
+                mCurrentBehindAlpha = mScrimBehindAlphaKeyguard;
+                mCurrentBehindTint = Color.TRANSPARENT;
+            }
             mBlankScreen = mDisplayRequiresBlanking;
         }
-
-        @Override
-        public float getBehindAlpha(float busyness) {
-            return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f
-                    : ScrimController.PULSING_WALLPAPER_SCRIM_ALPHA;
-        }
     },
 
     /**
@@ -204,6 +197,7 @@
     int mIndex;
     boolean mHasBackdrop;
     boolean mLaunchingAffordanceWithPreview;
+    int mPulseReason;
 
     ScrimState(int index) {
         mIndex = index;
@@ -235,7 +229,7 @@
         return mCurrentInFrontAlpha;
     }
 
-    public float getBehindAlpha(float busyness) {
+    public float getBehindAlpha() {
         return mCurrentBehindAlpha;
     }
 
@@ -276,6 +270,10 @@
         mAodFrontScrimAlpha = aodFrontScrimAlpha;
     }
 
+    public void setPulseReason(int pulseReason) {
+        mPulseReason = pulseReason;
+    }
+
     public void setScrimBehindAlphaKeyguard(float scrimBehindAlphaKeyguard) {
         mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
     }
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 ed71598..1470d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3883,6 +3883,7 @@
 
         @Override
         public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
+            mScrimController.setPulseReason(reason);
             if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) {
                 mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE");
                 startAssist(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 8ae7d52..ff30a4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -29,7 +29,10 @@
 import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 
@@ -37,6 +40,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.HashSet;
 
@@ -45,14 +50,18 @@
 @RunWithLooper
 public class NotificationRoundnessManagerTest extends SysuiTestCase {
 
-    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
+    private NotificationRoundnessManager mRoundnessManager;
     private HashSet<ExpandableView> mAnimatedChildren = new HashSet<>();
     private Runnable mRoundnessCallback = mock(Runnable.class);
     private ExpandableNotificationRow mFirst;
     private ExpandableNotificationRow mSecond;
+    @Mock
+    private AmbientPulseManager mAmbientPulseManager;
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mRoundnessManager = new NotificationRoundnessManager(mAmbientPulseManager);
         com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
         mFirst = testHelper.createRow();
@@ -127,6 +136,27 @@
     }
 
     @Test
+    public void testRoundnessPulsing() throws Exception {
+        // Let's create a notification that's neither the first or last item of the stack,
+        // this way we'll ensure that it won't have any rounded corners by default.
+        mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
+                createSection(mFirst, mSecond),
+                createSection(null, null)
+        });
+        ExpandableNotificationRow row = new NotificationTestHelper(getContext()).createRow();
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.getRow()).thenReturn(row);
+
+        mRoundnessManager.onAmbientStateChanged(entry, true);
+        Assert.assertEquals(1f, row.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1f, row.getCurrentTopRoundness(), 0.0f);
+
+        mRoundnessManager.onAmbientStateChanged(entry, false);
+        Assert.assertEquals(0f, row.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0f, row.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
     public void testRoundnessSetOnSecondSectionLast() {
         mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
                 createSection(mFirst, mFirst),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 37814f0..736f384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -107,6 +107,7 @@
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
     @Mock private MetricsLogger mMetricsLogger;
+    @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
     private TestableNotificationEntryManager mEntryManager;
     private int mOriginalInterruptionModelSetting;
 
@@ -140,7 +141,7 @@
         // holds a copy of the CUT's instances of these classes, so they still refer to the CUT's
         // member variables, not the spy's member variables.
         mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null,
-                true /* allowLongPress */);
+                true /* allowLongPress */, mNotificationRoundnessManager);
         mStackScroller = spy(mStackScrollerInternal);
         mStackScroller.setShelf(notificationShelf);
         mStackScroller.setStatusBar(mBar);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 27ed9c5..cd52e87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -47,7 +47,6 @@
     private float mPanelExpansion;
     private int mKeyguardStatusHeight;
     private float mDark;
-    private boolean mPulsing;
 
     @Before
     public void setUp() {
@@ -208,20 +207,6 @@
     }
 
     @Test
-    public void notifPositionWhilePulsingOnAOD() {
-        // GIVEN on AOD and pulsing
-        givenAOD();
-        mNotificationStackHeight = EMPTY_HEIGHT;
-        mKeyguardStatusHeight = EMPTY_HEIGHT;
-        mPulsing = true;
-        mClockPositionAlgorithm.setPulsingPadding(200);
-        // WHEN the clock position algorithm is run
-        positionClock();
-        // THEN the notif padding doesn't adjust for pulsing.
-        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
-    }
-
-    @Test
     public void notifPositionMiddleOfScreenOnLockScreen() {
         // GIVEN on lock screen and both stack scroll and clock have 0 height
         givenLockScreen();
@@ -307,20 +292,6 @@
         assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
     }
 
-    @Test
-    public void notifPositionWhilePulsingOnLockScreen() {
-        // GIVEN on lock screen and pulsing
-        givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
-        mKeyguardStatusHeight = EMPTY_HEIGHT;
-        mPulsing = true;
-        mClockPositionAlgorithm.setPulsingPadding(200);
-        // WHEN the clock position algorithm is run
-        positionClock();
-        // THEN the notif padding adjusts for pulsing.
-        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1200);
-    }
-
     private void givenAOD() {
         mPanelExpansion = 1.f;
         mDark = 1.f;
@@ -334,7 +305,7 @@
     private void positionClock() {
         mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
                 mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mDark, SECURE_LOCKED,
-                mPulsing, ZERO_DRAG);
+                ZERO_DRAG);
         mClockPositionAlgorithm.run(mClockPosition);
     }
 }
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 8eb42c4..c20d37f 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
@@ -45,6 +45,7 @@
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.util.function.TriConsumer;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.util.wakelock.WakeLock;
 import com.android.systemui.utils.os.FakeHandler;
@@ -133,7 +134,8 @@
         // Back scrim should be transparent
         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
 
-        // Move on to PULSING and check if the back scrim is still transparent
+        // Pulsing notification should conserve AOD wallpaper.
+        mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION);
         mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
@@ -218,13 +220,14 @@
         mScrimController.finishAnimationsImmediately();
         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
 
+        mScrimController.setPulseReason(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN);
         mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be transparent
         // Back scrim should be semi-transparent so the user can see the wallpaper
         // Pulse callback should have been invoked
         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
-        assertScrimTint(mScrimBehind, true /* tinted */);
+        assertScrimTint(mScrimBehind, false /* tinted */);
     }
 
     @Test
@@ -477,12 +480,8 @@
     @Test
     public void testHoldsPulsingWallpaperAnimationLock() {
         // Pre-conditions
-        mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() {
-            @Override
-            public boolean isFadeOutWallpaper() {
-                return true;
-            }
-        });
+        mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION);
+        mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
         reset(mWakeLock);
 
@@ -491,7 +490,6 @@
         verify(mWakeLock, never()).release();
         mScrimController.finishAnimationsImmediately();
         verify(mWakeLock).release();
-        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
     }
 
     @Test
@@ -504,31 +502,27 @@
     }
 
     @Test
-    public void testWillHidePulsingWallpaper_withRequestFadeOut() {
-        mScrimController.setWallpaperSupportsAmbientMode(true);
-        mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() {
-            @Override
-            public boolean isFadeOutWallpaper() {
-                return true;
-            }
-        });
-        verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
-        verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
-    }
-
-    @Test
-    public void testDoesNotHidePulsingWallpaper_withoutRequestFadeOut() {
-        mScrimController.setWallpaperSupportsAmbientMode(true);
-        mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() {});
-        verify(mAlarmManager, never()).setExact(anyInt(), anyLong(), any(), any(), any());
-    }
-
-    @Test
-    public void testDoesNotHidePulsingWallpaper_withoutCallback() {
-        mScrimController.setWallpaperSupportsAmbientMode(true);
+    public void testWillHidePulsingWallpaper_whenNotification() {
+        mScrimController.setWallpaperSupportsAmbientMode(false);
+        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.finishAnimationsImmediately();
+        mScrimController.setPulseReason(DozeLog.PULSE_REASON_NOTIFICATION);
         mScrimController.transitionTo(ScrimState.PULSING);
-        verify(mAlarmManager, never()).setExact(anyInt(), anyLong(), any(), any(), any());
+        mScrimController.finishAnimationsImmediately();
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimTint(mScrimBehind, true);
+    }
+
+    @Test
+    public void testWillHidePulsingWallpaper_whenDocking() {
+        mScrimController.setWallpaperSupportsAmbientMode(false);
+        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.finishAnimationsImmediately();
+        mScrimController.setPulseReason(DozeLog.PULSE_REASON_DOCKING);
+        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.finishAnimationsImmediately();
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimTint(mScrimBehind, true);
     }
 
     @Test
@@ -560,34 +554,6 @@
         Assert.assertTrue(mScrimController.wasAnimationJustCancelled());
     }
 
-    /**
-     * Number of visible notifications affects scrim opacity.
-     */
-    @Test
-    public void testNotificationDensity() {
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
-        mScrimController.finishAnimationsImmediately();
-
-        mScrimController.setNotificationCount(0);
-        mScrimController.finishAnimationsImmediately();
-        Assert.assertEquals("lower density when no notifications",
-                ScrimController.GRADIENT_SCRIM_ALPHA,  mScrimBehind.getViewAlpha(), 0.01f);
-
-        mScrimController.setNotificationCount(3);
-        mScrimController.finishAnimationsImmediately();
-        Assert.assertEquals("stronger density when notifications are visible",
-                ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,  mScrimBehind.getViewAlpha(), 0.01f);
-    }
-
-    /**
-     * Moving from/to states conserves old notification density.
-     */
-    @Test
-    public void testConservesNotificationDensity() {
-        testConservesNotificationDensity(0 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA);
-        testConservesNotificationDensity(3 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY);
-    }
-
     @Test
     public void testScrimFocus() {
         mScrimController.transitionTo(ScrimState.AOD);
@@ -662,24 +628,6 @@
                 mScrimBehind.getDefaultFocusHighlightEnabled());
     }
 
-    /**
-     * Conserves old notification density after leaving state and coming back.
-     *
-     * @param count How many notification.
-     * @param expectedAlpha Expected alpha.
-     */
-    private void testConservesNotificationDensity(int count, float expectedAlpha) {
-        mScrimController.setNotificationCount(count);
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
-        mScrimController.finishAnimationsImmediately();
-
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertEquals("Doesn't respect notification busyness after transition",
-                expectedAlpha,  mScrimBehind.getViewAlpha(), 0.01f);
-    }
-
     private void assertScrimTint(ScrimView scrimView, boolean tinted) {
         final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
         final String name = scrimView == mScrimInFront ? "front" : "back";
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 36ca52a..763c16f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,6 +16,11 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
 import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
@@ -23,13 +28,9 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
+
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
 
 import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
@@ -121,6 +122,8 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
+import libcore.util.EmptyArray;
+
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.FileDescriptor;
@@ -139,8 +142,6 @@
 import java.util.function.Consumer;
 import java.util.function.IntSupplier;
 
-import libcore.util.EmptyArray;
-
 /**
  * This class is instantiated by the system as a system level service and can be
  * accessed only by the system. The task of this service is to be a centralized
@@ -1827,8 +1828,6 @@
         updateFilterKeyEventsLocked(userState);
         updateTouchExplorationLocked(userState);
         updatePerformGesturesLocked(userState);
-        updateDisplayDaltonizerLocked(userState);
-        updateDisplayInversionLocked(userState);
         updateMagnificationLocked(userState);
         scheduleUpdateFingerprintGestureHandling(userState);
         scheduleUpdateInputFilter(userState);
@@ -2187,14 +2186,6 @@
         return false;
     }
 
-    private void updateDisplayDaltonizerLocked(UserState userState) {
-        DisplayAdjustmentUtils.applyDaltonizerSetting(mContext, userState.mUserId);
-    }
-
-    private void updateDisplayInversionLocked(UserState userState) {
-        DisplayAdjustmentUtils.applyInversionSetting(mContext, userState.mUserId);
-    }
-
     private void updateMagnificationLocked(UserState userState) {
         if (userState.mUserId != mCurrentUserId) {
             return;
@@ -4104,15 +4095,6 @@
         private final Uri mTouchExplorationGrantedAccessibilityServicesUri = Settings.Secure
                 .getUriFor(Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
 
-        private final Uri mDisplayInversionEnabledUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
-
-        private final Uri mDisplayDaltonizerEnabledUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
-
-        private final Uri mDisplayDaltonizerUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER);
-
         private final Uri mHighTextContrastUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED);
 
@@ -4153,12 +4135,6 @@
                     mTouchExplorationGrantedAccessibilityServicesUri,
                     false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
-                    mDisplayInversionEnabledUri, false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(
-                    mDisplayDaltonizerEnabledUri, false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(
-                    mDisplayDaltonizerUri, false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(
                     mHighTextContrastUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
@@ -4202,11 +4178,6 @@
                     if (readTouchExplorationGrantedAccessibilityServicesLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
-                } else if (mDisplayDaltonizerEnabledUri.equals(uri)
-                        || mDisplayDaltonizerUri.equals(uri)) {
-                    updateDisplayDaltonizerLocked(userState);
-                } else if (mDisplayInversionEnabledUri.equals(uri)) {
-                    updateDisplayInversionLocked(userState);
                 } else if (mHighTextContrastUri.equals(uri)) {
                     if (readHighTextContrastEnabledSettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
deleted file mode 100644
index c81a876..0000000
--- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2013 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.accessibility;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.os.Binder;
-import android.provider.Settings.Secure;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.server.LocalServices;
-import com.android.server.display.DisplayTransformManager;
-
-/**
- * Utility methods for performing accessibility display adjustments.
- */
-class DisplayAdjustmentUtils {
-
-    /** Default inversion mode for display color correction. */
-    private static final int DEFAULT_DISPLAY_DALTONIZER =
-            AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY;
-
-    /** Matrix and offset used for converting color to gray-scale. */
-    private static final float[] MATRIX_GRAYSCALE = new float[] {
-        .2126f, .2126f, .2126f, 0,
-        .7152f, .7152f, .7152f, 0,
-        .0722f, .0722f, .0722f, 0,
-             0,      0,      0, 1
-    };
-
-    /**
-     * Matrix and offset used for luminance inversion. Represents a transform
-     * from RGB to YIQ color space, rotation around the Y axis by 180 degrees,
-     * transform back to RGB color space, and subtraction from 1. The last row
-     * represents a non-multiplied addition, see surfaceflinger's ProgramCache
-     * for full implementation details.
-     */
-    private static final float[] MATRIX_INVERT_COLOR = new float[] {
-        0.402f, -0.598f, -0.599f, 0,
-       -1.174f, -0.174f, -1.175f, 0,
-       -0.228f, -0.228f,  0.772f, 0,
-             1,       1,       1, 1
-    };
-
-    public static void applyDaltonizerSetting(Context context, int userId) {
-        final ContentResolver cr = context.getContentResolver();
-        final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
-
-        int daltonizerMode = AccessibilityManager.DALTONIZER_DISABLED;
-        long identity = Binder.clearCallingIdentity();
-        try {
-            if (Secure.getIntForUser(cr,
-                    Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0) {
-                daltonizerMode = Secure.getIntForUser(cr,
-                        Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, DEFAULT_DISPLAY_DALTONIZER, userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-
-        float[] grayscaleMatrix = null;
-        if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
-            // Monochromacy isn't supported by the native Daltonizer.
-            grayscaleMatrix = MATRIX_GRAYSCALE;
-            daltonizerMode = AccessibilityManager.DALTONIZER_DISABLED;
-        }
-        dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, grayscaleMatrix);
-        dtm.setDaltonizerMode(daltonizerMode);
-    }
-
-    /**
-     * Applies the specified user's display color adjustments.
-     */
-    public static void applyInversionSetting(Context context, int userId) {
-        final ContentResolver cr = context.getContentResolver();
-        final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            final boolean invertColors = Secure.getIntForUser(cr,
-                    Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) != 0;
-            dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
-                    invertColors ? MATRIX_INVERT_COLOR : null);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 2a80644..db2a733 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -780,6 +780,13 @@
             });
         refreshZramSettings();
 
+        // Schedule zram writeback unless zram is disabled by persist.sys.zram_enabled
+        String zramPropValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY);
+        if (!zramPropValue.equals("0")
+                && mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_zramWriteback)) {
+            ZramWriteback.scheduleZramWriteback(mContext);
+        }
         // Toggle isolated-enable system property in response to settings
         mContext.getContentResolver().registerContentObserver(
             Settings.Global.getUriFor(Settings.Global.ISOLATED_STORAGE_REMOTE),
@@ -813,6 +820,12 @@
             // changing the property value. There's no race: we're the
             // sole writer.
             SystemProperties.set(ZRAM_ENABLED_PROPERTY, desiredPropertyValue);
+            // Schedule writeback only if zram is being enabled.
+            if (desiredPropertyValue.equals("1")
+                    && mContext.getResources().getBoolean(
+                        com.android.internal.R.bool.config_zramWriteback)) {
+                ZramWriteback.scheduleZramWriteback(mContext);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/ZramWriteback.java b/services/core/java/com/android/server/ZramWriteback.java
new file mode 100644
index 0000000..3a4aff2
--- /dev/null
+++ b/services/core/java/com/android/server/ZramWriteback.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Schedules jobs for triggering zram writeback.
+ */
+public final class ZramWriteback extends JobService {
+    private static final String TAG = "ZramWriteback";
+    private static final boolean DEBUG = false;
+
+    private static final ComponentName sZramWriteback =
+            new ComponentName("android", ZramWriteback.class.getName());
+
+    private static final int MARK_IDLE_JOB_ID = 811;
+    private static final int WRITEBACK_IDLE_JOB_ID = 812;
+
+    private static final int MAX_ZRAM_DEVICES = 256;
+    private static int sZramDeviceId = 0;
+
+    private static final String IDLE_SYS = "/sys/block/zram%d/idle";
+    private static final String IDLE_SYS_ALL_PAGES = "all";
+
+    private static final String WB_SYS = "/sys/block/zram%d/writeback";
+    private static final String WB_SYS_IDLE_PAGES = "idle";
+
+    private static final String WB_STATS_SYS = "/sys/block/zram%d/bd_stat";
+    private static final int WB_STATS_MAX_FILE_SIZE = 128;
+
+    private static final String BDEV_SYS = "/sys/block/zram%d/backing_dev";
+
+    private static final String MARK_IDLE_DELAY_PROP = "ro.zram.mark_idle_delay_mins";
+    private static final String FIRST_WB_DELAY_PROP = "ro.zram.first_wb_delay_mins";
+    private static final String PERIODIC_WB_DELAY_PROP = "ro.zram.periodic_wb_delay_hours";
+
+    private void markPagesAsIdle() {
+        String idlePath = String.format(IDLE_SYS, sZramDeviceId);
+        try {
+            FileUtils.stringToFile(new File(idlePath), IDLE_SYS_ALL_PAGES);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to write to " + idlePath);
+        }
+    }
+
+    private void flushIdlePages() {
+        if (DEBUG) Slog.d(TAG, "Start writing back idle pages to disk");
+        String wbPath = String.format(WB_SYS, sZramDeviceId);
+        try {
+            FileUtils.stringToFile(new File(wbPath), WB_SYS_IDLE_PAGES);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to write to " + wbPath);
+        }
+        if (DEBUG) Slog.d(TAG, "Finished writeback back idle pages");
+    }
+
+    private int getWrittenPageCount() {
+        String wbStatsPath = String.format(WB_STATS_SYS, sZramDeviceId);
+        try {
+            String wbStats = FileUtils
+                    .readTextFile(new File(wbStatsPath), WB_STATS_MAX_FILE_SIZE, "");
+            return Integer.parseInt(wbStats.trim().split("\\s+")[2], 10);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to read writeback stats from " + wbStatsPath);
+        }
+
+        return -1;
+    }
+
+    private void markAndFlushPages() {
+        int pageCount = getWrittenPageCount();
+
+        flushIdlePages();
+        markPagesAsIdle();
+
+        if (pageCount != -1) {
+            Slog.i(TAG, "Total pages written to disk is " + (getWrittenPageCount() - pageCount));
+        }
+    }
+
+    private static boolean isWritebackEnabled() {
+        try {
+            String backingDev = FileUtils
+                    .readTextFile(new File(String.format(BDEV_SYS, sZramDeviceId)), 128, "");
+            if (!"none".equals(backingDev.trim())) {
+                return true;
+            } else {
+                Slog.w(TAG, "Writeback device is not set");
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Writeback is not enabled on zram");
+        }
+        return false;
+    }
+
+    private static void schedNextWriteback(Context context) {
+        int nextWbDelay = SystemProperties.getInt(PERIODIC_WB_DELAY_PROP, 24);
+        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+        js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback)
+                        .setMinimumLatency(TimeUnit.HOURS.toMillis(nextWbDelay))
+                        .setRequiresDeviceIdle(true)
+                        .build());
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+
+        if (!isWritebackEnabled()) {
+            jobFinished(params, false);
+            return false;
+        }
+
+        if (params.getJobId() == MARK_IDLE_JOB_ID) {
+            markPagesAsIdle();
+            jobFinished(params, false);
+            return false;
+        } else {
+            new Thread("ZramWriteback_WritebackIdlePages") {
+                @Override
+                public void run() {
+                    markAndFlushPages();
+                    schedNextWriteback(ZramWriteback.this);
+                    jobFinished(params, false);
+                }
+            }.start();
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        // The thread that triggers the writeback is non-interruptible
+        return false;
+    }
+
+    /**
+     * Schedule the zram writeback job to trigger a writeback when idle
+     */
+    public static void scheduleZramWriteback(Context context) {
+        int markIdleDelay = SystemProperties.getInt(MARK_IDLE_DELAY_PROP, 20);
+        int firstWbDelay = SystemProperties.getInt(FIRST_WB_DELAY_PROP, 180);
+
+        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+        // Schedule a one time job to mark pages as idle. These pages will be written
+        // back at later point if they remain untouched.
+        js.schedule(new JobInfo.Builder(MARK_IDLE_JOB_ID, sZramWriteback)
+                        .setMinimumLatency(TimeUnit.MINUTES.toMillis(markIdleDelay))
+                        .build());
+
+        // Schedule a one time job to flush idle pages to disk.
+        // After the initial writeback, subsequent writebacks are done at interval set
+        // by ro.zram.periodic_wb_delay_hours.
+        js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback)
+                        .setMinimumLatency(TimeUnit.MINUTES.toMillis(firstWbDelay))
+                        .setRequiresDeviceIdle(true)
+                        .build());
+    }
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9a4ca1f..d9fcf9e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1614,7 +1614,9 @@
 
         // For notifications/ring, show the ui before making any adjustments
         // Don't suppress mute/unmute requests
-        if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) {
+        // Don't suppress adjustments for single volume device
+        if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)
+                && !mIsSingleVolume) {
             direction = 0;
             flags &= ~AudioManager.FLAG_PLAY_SOUND;
             flags &= ~AudioManager.FLAG_VIBRATE;
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index f06cf0b..9223739 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -51,6 +51,7 @@
 import android.provider.Settings.System;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AnimationUtils;
 
 import com.android.internal.R;
@@ -402,6 +403,29 @@
         }
     };
 
+    /**
+     * Matrix and offset used for converting color to grayscale.
+     */
+    private static final float[] MATRIX_GRAYSCALE = new float[]{
+            .2126f, .2126f, .2126f, 0f,
+            .7152f, .7152f, .7152f, 0f,
+            .0722f, .0722f, .0722f, 0f,
+            0f, 0f, 0f, 1f
+    };
+
+    /**
+     * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color
+     * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and
+     * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's
+     * ProgramCache for full implementation details.
+     */
+    private static final float[] MATRIX_INVERT_COLOR = new float[] {
+            0.402f, -0.598f, -0.599f, 0f,
+            -1.174f, -0.174f, -1.175f, 0f,
+            -0.228f, -0.228f, 0.772f, 0f,
+            1f, 1f, 1f, 1f
+    };
+
     private final Handler mHandler;
 
     private final AppSaturationController mAppSaturationController = new AppSaturationController();
@@ -543,9 +567,16 @@
                             case System.DISPLAY_COLOR_MODE:
                                 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
                                 break;
-                            case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
                             case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
-                                onAccessibilityTransformChanged();
+                                onAccessibilityInversionChanged();
+                                onAccessibilityActivated();
+                                break;
+                            case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
+                                onAccessibilityDaltonizerChanged();
+                                onAccessibilityActivated();
+                                break;
+                            case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
+                                onAccessibilityDaltonizerChanged();
                                 break;
                             case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
                                 updateDisplayWhiteBalanceStatus();
@@ -574,6 +605,9 @@
         cr.registerContentObserver(
                 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
                 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(
+                Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
         cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
                 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
 
@@ -704,10 +738,43 @@
         dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
     }
 
-    private void onAccessibilityTransformChanged() {
+    private void onAccessibilityActivated() {
         onDisplayColorModeChanged(mNightDisplayController.getColorMode());
     }
 
+    /**
+     * Apply the accessibility daltonizer transform based on the settings value.
+     */
+    private void onAccessibilityDaltonizerChanged() {
+        final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
+                Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0;
+        final int daltonizerMode = enabled ? Secure.getIntForUser(getContext().getContentResolver(),
+                Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
+                AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
+                : AccessibilityManager.DALTONIZER_DISABLED;
+
+        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+        if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
+            // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
+            dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
+                    MATRIX_GRAYSCALE);
+            dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
+        } else {
+            dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
+            dtm.setDaltonizerMode(daltonizerMode);
+        }
+    }
+
+    /**
+     * Apply the accessibility inversion transform based on the settings value.
+     */
+    private void onAccessibilityInversionChanged() {
+        final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
+                Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0;
+        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+        dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
+                enabled ? MATRIX_INVERT_COLOR : null);
+    }
 
     /**
      * Applies current color temperature matrix, or removes it if deactivated.
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index f1951b1..1f9b027 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -26,6 +26,7 @@
 import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
@@ -258,9 +259,9 @@
             localLskfHash = hashCredentialsBySaltedSha256(salt, mCredential);
         }
 
-        Map<String, SecretKey> rawKeys;
+        Map<String, Pair<SecretKey, byte[]>> rawKeysWithMetadata;
         try {
-            rawKeys = getKeysToSync(recoveryAgentUid);
+            rawKeysWithMetadata = getKeysToSync(recoveryAgentUid);
         } catch (GeneralSecurityException e) {
             Log.e(TAG, "Failed to load recoverable keys for sync", e);
             return;
@@ -278,7 +279,9 @@
         }
         // Only include insecure key material for test
         if (mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(rootCertAlias)) {
-            rawKeys = mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+            rawKeysWithMetadata =
+                    mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys(
+                            rawKeysWithMetadata);
         }
 
         SecretKey recoveryKey;
@@ -292,7 +295,7 @@
         Map<String, byte[]> encryptedApplicationKeys;
         try {
             encryptedApplicationKeys = KeySyncUtils.encryptKeysWithRecoveryKey(
-                    recoveryKey, rawKeys);
+                    recoveryKey, rawKeysWithMetadata);
         } catch (InvalidKeyException | NoSuchAlgorithmException e) {
             Log.wtf(TAG,
                     "Should be impossible: could not encrypt application keys with random key",
@@ -356,7 +359,8 @@
                 .setCounterId(counterId)
                 .setServerParams(vaultHandle)
                 .setKeyChainProtectionParams(metadataList)
-                .setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys))
+                .setWrappedApplicationKeys(
+                        createApplicationKeyEntries(encryptedApplicationKeys, rawKeysWithMetadata))
                 .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey);
         try {
             keyChainSnapshotBuilder.setTrustedHardwareCertPath(certPath);
@@ -405,7 +409,7 @@
     /**
      * Returns all of the recoverable keys for the user.
      */
-    private Map<String, SecretKey> getKeysToSync(int recoveryAgentUid)
+    private Map<String, Pair<SecretKey, byte[]>> getKeysToSync(int recoveryAgentUid)
             throws InsecureUserException, KeyStoreException, UnrecoverableKeyException,
             NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
             InvalidKeyException, InvalidAlgorithmParameterException, IOException {
@@ -521,12 +525,14 @@
     }
 
     private static List<WrappedApplicationKey> createApplicationKeyEntries(
-            Map<String, byte[]> encryptedApplicationKeys) {
+            Map<String, byte[]> encryptedApplicationKeys,
+            Map<String, Pair<SecretKey, byte[]>> originalKeysWithMetadata) {
         ArrayList<WrappedApplicationKey> keyEntries = new ArrayList<>();
         for (String alias : encryptedApplicationKeys.keySet()) {
             keyEntries.add(new WrappedApplicationKey.Builder()
                     .setAlias(alias)
                     .setEncryptedKeyMaterial(encryptedApplicationKeys.get(alias))
+                    .setMetadata(originalKeysWithMetadata.get(alias).second)
                     .build());
         }
         return keyEntries;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index 8e6f9cb..24d575e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -16,6 +16,9 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
+import android.annotation.Nullable;
+import android.util.Pair;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.nio.ByteBuffer;
@@ -152,15 +155,28 @@
      * @hide
      */
     public static Map<String, byte[]> encryptKeysWithRecoveryKey(
-            SecretKey recoveryKey, Map<String, SecretKey> keys)
+            SecretKey recoveryKey, Map<String, Pair<SecretKey, byte[]>> keys)
             throws NoSuchAlgorithmException, InvalidKeyException {
         HashMap<String, byte[]> encryptedKeys = new HashMap<>();
         for (String alias : keys.keySet()) {
-            SecretKey key = keys.get(alias);
+            SecretKey key = keys.get(alias).first;
+            byte[] metadata = keys.get(alias).second;
+            byte[] header;
+            if (metadata == null) {
+                header = ENCRYPTED_APPLICATION_KEY_HEADER;
+            } else {
+                // The provided metadata, if non-empty, will be bound to the authenticated
+                // encryption process of the key material. As a result, the ciphertext cannot be
+                // decrypted if a wrong metadata is provided during the recovery/decryption process.
+                // Note that Android P devices do not have the API to provide the optional metadata,
+                // so all the keys with non-empty metadata stored on Android Q+ devices cannot be
+                // recovered on Android P devices.
+                header = concat(ENCRYPTED_APPLICATION_KEY_HEADER, metadata);
+            }
             byte[] encryptedKey = SecureBox.encrypt(
                     /*theirPublicKey=*/ null,
                     /*sharedSecret=*/ recoveryKey.getEncoded(),
-                    /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER,
+                    /*header=*/ header,
                     /*payload=*/ key.getEncoded());
             encryptedKeys.put(alias, encryptedKey);
         }
@@ -257,12 +273,19 @@
      * @throws AEADBadTagException if the message has been tampered with or was encrypted with a
      *     different key.
      */
-    public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey)
+    public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey,
+            @Nullable byte[] applicationKeyMetadata)
             throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
+        byte[] header;
+        if (applicationKeyMetadata == null) {
+            header = ENCRYPTED_APPLICATION_KEY_HEADER;
+        } else {
+            header = concat(ENCRYPTED_APPLICATION_KEY_HEADER, applicationKeyMetadata);
+        }
         return SecureBox.decrypt(
                 /*ourPrivateKey=*/ null,
                 /*sharedSecret=*/ recoveryKey,
-                /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER,
+                /*header=*/ header,
                 /*encryptedPayload=*/ encryptedApplicationKey);
     }
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
index c249468..1692e5c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
@@ -17,6 +17,8 @@
 package com.android.server.locksettings.recoverablekeystore;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
 
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 
@@ -24,7 +26,6 @@
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.util.Locale;
-import android.util.Log;
 
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
@@ -84,6 +85,8 @@
      * @param userId The user ID of the profile to which the calling app belongs.
      * @param uid The uid of the application that will own the key.
      * @param alias The alias by which the key will be known in the recoverable key store.
+     * @param metadata The optional metadata that will be authenticated (but unencrypted) together
+     *     with the key material when the key is uploaded to cloud.
      * @throws RecoverableKeyStorageException if there is some error persisting the key either to
      *     the database.
      * @throws KeyStoreException if there is a KeyStore error wrapping the generated key.
@@ -92,12 +95,13 @@
      * @hide
      */
     public byte[] generateAndStoreKey(
-            PlatformEncryptionKey platformKey, int userId, int uid, String alias)
+            PlatformEncryptionKey platformKey, int userId, int uid, String alias,
+            @Nullable byte[] metadata)
             throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException {
         mKeyGenerator.init(KEY_SIZE_BITS);
         SecretKey key = mKeyGenerator.generateKey();
 
-        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key);
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key, metadata);
         long result = mDatabase.insertKey(userId, uid, alias, wrappedKey);
 
         if (result == RESULT_CANNOT_INSERT_ROW) {
@@ -126,6 +130,8 @@
      * @param uid The uid of the application that will own the key.
      * @param alias The alias by which the key will be known in the recoverable key store.
      * @param keyBytes The raw bytes of the AES key to be imported.
+     * @param metadata The optional metadata that will be authenticated (but unencrypted) together
+     *     with the key material when the key is uploaded to cloud.
      * @throws RecoverableKeyStorageException if there is some error persisting the key either to
      *     the database.
      * @throws KeyStoreException if there is a KeyStore error wrapping the generated key.
@@ -135,11 +141,11 @@
      */
     public void importKey(
             @NonNull PlatformEncryptionKey platformKey, int userId, int uid, @NonNull String alias,
-            @NonNull byte[] keyBytes)
+            @NonNull byte[] keyBytes, @Nullable byte[] metadata)
             throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException {
         SecretKey key = new SecretKeySpec(keyBytes, SECRET_KEY_ALGORITHM);
 
-        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key);
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key, metadata);
         long result = mDatabase.insertKey(userId, uid, alias, wrappedKey);
 
         if (result == RESULT_CANNOT_INSERT_ROW) {
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 81d219c..ed864c0 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -604,7 +604,8 @@
 
         try {
             byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey);
-            Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys);
+            Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey,
+                    applicationKeys);
             return importKeyMaterials(userId, uid, keysByAlias);
         } catch (KeyStoreException e) {
             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
@@ -623,7 +624,8 @@
      * @throws KeyStoreException if an error occurs importing the key or getting the grant.
      */
     private @NonNull Map<String, String> importKeyMaterials(
-            int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException {
+            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));
@@ -700,8 +702,6 @@
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
 
-        // TODO: Include metadata in the processes of authentication and storage
-
         PlatformEncryptionKey encryptionKey;
         try {
             encryptionKey = mPlatformKeyManager.getEncryptKey(userId);
@@ -715,8 +715,8 @@
         }
 
         try {
-            byte[] secretKey =
-                    mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
+            byte[] secretKey = mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId,
+                    uid, alias, metadata);
             mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey);
             return getAlias(userId, uid, alias);
         } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
@@ -768,8 +768,6 @@
                             + " bits.");
         }
 
-        // TODO: Include metadata in the processes of authentication and storage
-
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
 
@@ -787,7 +785,8 @@
 
         try {
             // Wrap the key by the platform key and store the wrapped key locally
-            mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes);
+            mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes,
+                    metadata);
 
             // Import the key to Android KeyStore and get grant
             mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes);
@@ -854,17 +853,17 @@
      * @return Map from alias to raw key material.
      * @throws RemoteException if an error occurred decrypting the keys.
      */
-    private @NonNull Map<String, byte[]> recoverApplicationKeys(
-            @NonNull byte[] recoveryKey,
+    private @NonNull Map<String, byte[]> recoverApplicationKeys(@NonNull byte[] recoveryKey,
             @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
         HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>();
         for (WrappedApplicationKey applicationKey : applicationKeys) {
             String alias = applicationKey.getAlias();
             byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial();
+            byte[] keyMetadata = applicationKey.getMetadata();
 
             try {
-                byte[] keyMaterial =
-                        KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial);
+                byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey,
+                        encryptedKeyMaterial, keyMetadata);
                 keyMaterialByAlias.put(alias, keyMaterial);
             } catch (NoSuchAlgorithmException e) {
                 Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
index 5ba3cce..ffea488 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
@@ -18,17 +18,20 @@
 
 import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE;
 
-import com.android.internal.widget.LockPatternUtils;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.security.keystore.recovery.TrustedRootCertificates;
 import android.util.Log;
+import android.util.Pair;
 
-import java.util.HashMap;
+import com.android.internal.widget.LockPatternUtils;
+
 import java.security.cert.X509Certificate;
+import java.util.HashMap;
 import java.util.Map;
+
 import javax.crypto.SecretKey;
 
 /**
@@ -84,22 +87,24 @@
                 || isTestOnlyCertificateAlias(rootCertificateAlias);
     }
 
-    public boolean doesCredentialSupportInsecureMode(int credentialType, String credential) {
+    boolean doesCredentialSupportInsecureMode(int credentialType, String credential) {
         return (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD)
             && (credential != null)
             && credential.startsWith(TrustedRootCertificates.INSECURE_PASSWORD_PREFIX);
     }
 
-    public Map<String, SecretKey> keepOnlyWhitelistedInsecureKeys(Map<String, SecretKey> rawKeys) {
+    Map<String, Pair<SecretKey, byte[]>> keepOnlyWhitelistedInsecureKeys(
+            Map<String, Pair<SecretKey, byte[]>> rawKeys) {
         if (rawKeys == null) {
             return null;
         }
-        Map<String, SecretKey> filteredKeys = new HashMap<>();
-        for (Map.Entry<String, SecretKey> entry : rawKeys.entrySet()) {
+        Map<String, Pair<SecretKey, byte[]>> filteredKeys = new HashMap<>();
+        for (Map.Entry<String, Pair<SecretKey, byte[]>> entry : rawKeys.entrySet()) {
             String alias = entry.getKey();
             if (alias != null
                     && alias.startsWith(TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX)) {
-                filteredKeys.put(entry.getKey(), entry.getValue());
+                filteredKeys.put(entry.getKey(),
+                        Pair.create(entry.getValue().first, entry.getValue().second));
                 Log.d(TAG, "adding key with insecure alias " + alias + " to the recovery snapshot");
             }
         }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
index 0077242..09d7541 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -16,8 +16,10 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
+import android.annotation.Nullable;
 import android.security.keystore.recovery.RecoveryController;
 import android.util.Log;
+import android.util.Pair;
 
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -49,6 +51,7 @@
     private final int mRecoveryStatus;
     private final byte[] mNonce;
     private final byte[] mKeyMaterial;
+    private final byte[] mKeyMetadata;
 
     /**
      * Returns a wrapped form of {@code key}, using {@code wrappingKey} to encrypt the key material.
@@ -58,7 +61,8 @@
      *     {@link android.security.keystore.AndroidKeyStoreKey} for an example of a key that does
      *     not expose its key material.
      */
-    public static WrappedKey fromSecretKey(PlatformEncryptionKey wrappingKey, SecretKey key)
+    public static WrappedKey fromSecretKey(PlatformEncryptionKey wrappingKey, SecretKey key,
+            @Nullable byte[] metadata)
             throws InvalidKeyException, KeyStoreException {
         if (key.getEncoded() == null) {
             throw new InvalidKeyException(
@@ -96,6 +100,7 @@
         return new WrappedKey(
                 /*nonce=*/ cipher.getIV(),
                 /*keyMaterial=*/ encryptedKeyMaterial,
+                /*keyMetadata=*/ metadata,
                 /*platformKeyGenerationId=*/ wrappingKey.getGenerationId(),
                 RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
     }
@@ -110,11 +115,10 @@
      * @see RecoveryController#RECOVERY_STATUS_SYNC_IN_PROGRESS
      * @hide
      */
-    public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) {
-        mNonce = nonce;
-        mKeyMaterial = keyMaterial;
-        mPlatformKeyGenerationId = platformKeyGenerationId;
-        mRecoveryStatus = RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS;
+    public WrappedKey(byte[] nonce, byte[] keyMaterial, @Nullable byte[] keyMetadata,
+            int platformKeyGenerationId) {
+        this(nonce, keyMaterial, keyMetadata, platformKeyGenerationId,
+                RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
     }
 
     /**
@@ -122,15 +126,18 @@
      *
      * @param nonce The nonce with which the key material was encrypted.
      * @param keyMaterial The encrypted bytes of the key material.
+     * @param keyMetadata The metadata that will be authenticated (but unencrypted) together with
+     *     the key material when the key is uploaded to cloud.
      * @param platformKeyGenerationId The generation ID of the key used to wrap this key.
      * @param recoveryStatus recovery status of the key.
      *
      * @hide
      */
-    public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId,
-            int recoveryStatus) {
+    public WrappedKey(byte[] nonce, byte[] keyMaterial, @Nullable byte[] keyMetadata,
+            int platformKeyGenerationId, int recoveryStatus) {
         mNonce = nonce;
         mKeyMaterial = keyMaterial;
+        mKeyMetadata = keyMetadata;
         mPlatformKeyGenerationId = platformKeyGenerationId;
         mRecoveryStatus = recoveryStatus;
     }
@@ -154,6 +161,15 @@
     }
 
     /**
+     * Returns the key metadata.
+     *
+     * @hide
+     */
+    public @Nullable byte[] getKeyMetadata() {
+        return mKeyMetadata;
+    }
+
+    /**
      * Returns the generation ID of the platform key, with which this key was wrapped.
      *
      * @hide
@@ -181,12 +197,12 @@
      *
      * @hide
      */
-    public static Map<String, SecretKey> unwrapKeys(
+    public static Map<String, Pair<SecretKey, byte[]>> unwrapKeys(
             PlatformDecryptionKey platformKey,
             Map<String, WrappedKey> wrappedKeys)
             throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
             InvalidKeyException, InvalidAlgorithmParameterException {
-        HashMap<String, SecretKey> unwrappedKeys = new HashMap<>();
+        HashMap<String, Pair<SecretKey, byte[]>> unwrappedKeys = new HashMap<>();
         Cipher cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
         int platformKeyGenerationId = platformKey.getGenerationId();
 
@@ -219,7 +235,7 @@
                         e);
                 continue;
             }
-            unwrappedKeys.put(alias, key);
+            unwrappedKeys.put(alias, Pair.create(key, wrappedKey.getKeyMetadata()));
         }
 
         return unwrappedKeys;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index e69f73d..dffaffe 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -104,6 +104,12 @@
         values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, LAST_SYNCED_AT_UNSYNCED);
         values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, wrappedKey.getPlatformKeyGenerationId());
         values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, wrappedKey.getRecoveryStatus());
+        byte[] keyMetadata = wrappedKey.getKeyMetadata();
+        if (keyMetadata == null) {
+            values.putNull(KeysEntry.COLUMN_NAME_KEY_METADATA);
+        } else {
+            values.put(KeysEntry.COLUMN_NAME_KEY_METADATA, keyMetadata);
+        }
         return db.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
     }
 
@@ -119,7 +125,8 @@
                 KeysEntry.COLUMN_NAME_NONCE,
                 KeysEntry.COLUMN_NAME_WRAPPED_KEY,
                 KeysEntry.COLUMN_NAME_GENERATION_ID,
-                KeysEntry.COLUMN_NAME_RECOVERY_STATUS};
+                KeysEntry.COLUMN_NAME_RECOVERY_STATUS,
+                KeysEntry.COLUMN_NAME_KEY_METADATA};
         String selection =
                 KeysEntry.COLUMN_NAME_UID + " = ? AND "
                 + KeysEntry.COLUMN_NAME_ALIAS + " = ?";
@@ -155,7 +162,17 @@
                     cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_GENERATION_ID));
             int recoveryStatus = cursor.getInt(
                     cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_RECOVERY_STATUS));
-            return new WrappedKey(nonce, keyMaterial, generationId, recoveryStatus);
+
+            // Retrieve the metadata associated with the key
+            byte[] keyMetadata;
+            int metadataIdx = cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_KEY_METADATA);
+            if (cursor.isNull(metadataIdx)) {
+                keyMetadata = null;
+            } else {
+                keyMetadata = cursor.getBlob(metadataIdx);
+            }
+
+            return new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, recoveryStatus);
         }
     }
 
@@ -252,7 +269,8 @@
                 KeysEntry.COLUMN_NAME_NONCE,
                 KeysEntry.COLUMN_NAME_WRAPPED_KEY,
                 KeysEntry.COLUMN_NAME_ALIAS,
-                KeysEntry.COLUMN_NAME_RECOVERY_STATUS};
+                KeysEntry.COLUMN_NAME_RECOVERY_STATUS,
+                KeysEntry.COLUMN_NAME_KEY_METADATA};
         String selection =
                 KeysEntry.COLUMN_NAME_USER_ID + " = ? AND "
                 + KeysEntry.COLUMN_NAME_UID + " = ? AND "
@@ -283,8 +301,18 @@
                         cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_ALIAS));
                 int recoveryStatus = cursor.getInt(
                         cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_RECOVERY_STATUS));
-                keys.put(alias, new WrappedKey(nonce, keyMaterial, platformKeyGenerationId,
-                        recoveryStatus));
+
+                // Retrieve the metadata associated with the key
+                byte[] keyMetadata;
+                int metadataIdx = cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_KEY_METADATA);
+                if (cursor.isNull(metadataIdx)) {
+                    keyMetadata = null;
+                } else {
+                    keyMetadata = cursor.getBlob(metadataIdx);
+                }
+
+                keys.put(alias, new WrappedKey(nonce, keyMaterial, keyMetadata,
+                        platformKeyGenerationId, recoveryStatus));
             }
             return keys;
         }
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 22e77cc..b58ee4b 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
@@ -67,6 +67,12 @@
          * Status of the key sync {@code RecoveryController#setRecoveryStatus}
          */
         static final String COLUMN_NAME_RECOVERY_STATUS = "recovery_status";
+
+        /**
+         * Data blob that will be authenticated (but encrypted) together with the key when the key
+         * is uploaded to cloud.
+         */
+        static final String COLUMN_NAME_KEY_METADATA = "key_metadata";
     }
 
     /**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index 43efe9c..b0613da 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -32,7 +32,7 @@
 class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
     private static final String TAG = "RecoverableKeyStoreDbHp";
 
-    static final int DATABASE_VERSION = 4;
+    static final int DATABASE_VERSION = 5;
     private static final String DATABASE_NAME = "recoverablekeystore.db";
 
     private static final String SQL_CREATE_KEYS_ENTRY =
@@ -46,6 +46,7 @@
                     + KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER,"
                     + KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER,"
                     + KeysEntry.COLUMN_NAME_RECOVERY_STATUS + " INTEGER,"
+                    + KeysEntry.COLUMN_NAME_KEY_METADATA + " BLOB,"
                     + "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + ","
                     + KeysEntry.COLUMN_NAME_ALIAS + "))";
 
@@ -135,6 +136,11 @@
             oldVersion = 4;
         }
 
+        if (oldVersion < 5 && newVersion >= 5) {
+            upgradeDbForVersion5(db);
+            oldVersion = 5;
+        }
+
         if (oldVersion != newVersion) {
             Log.e(TAG, "Failed to update recoverablekeystore database to the most recent version");
         }
@@ -166,6 +172,13 @@
                 /*defaultStr=*/ null);
     }
 
+    private void upgradeDbForVersion5(SQLiteDatabase db) {
+        Log.d(TAG, "Updating recoverable keystore database to version 5");
+        // adds a column to store the metadata for application keys
+        addColumnToTable(db, KeysEntry.TABLE_NAME,
+                KeysEntry.COLUMN_NAME_KEY_METADATA, "BLOB", /*defaultStr=*/ null);
+    }
+
     private static void addColumnToTable(
             SQLiteDatabase db, String tableName, String column, String columnType,
             String defaultStr) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index fd365a7..b6dae19 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -33,7 +33,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ILauncherApps;
 import android.content.pm.IOnAppsChangedListener;
-import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -43,8 +42,6 @@
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
-import android.content.pm.Signature;
-import android.content.pm.SigningInfo;
 import android.content.pm.UserInfo;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -59,31 +56,21 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
-import android.util.ByteStringUtils;
 import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.StatLogger;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Service that manages requests and callbacks for launchers that support
@@ -121,17 +108,6 @@
     static class LauncherAppsImpl extends ILauncherApps.Stub {
         private static final boolean DEBUG = false;
         private static final String TAG = "LauncherAppsService";
-
-        // Stats
-        @VisibleForTesting
-        interface Stats {
-            int INIT_VOUCHED_SIGNATURES = 0;
-            int COUNT = INIT_VOUCHED_SIGNATURES + 1;
-        }
-        private final StatLogger mStatLogger = new StatLogger(new String[] {
-                "initVouchedSignatures"
-        });
-
         private final Context mContext;
         private final UserManager mUm;
         private final UserManagerInternal mUserManagerInternal;
@@ -141,16 +117,11 @@
         private final PackageCallbackList<IOnAppsChangedListener> mListeners
                 = new PackageCallbackList<IOnAppsChangedListener>();
         private final DevicePolicyManager mDpm;
-        private final ConcurrentHashMap<UserHandle, Set<String>> mVouchedSignaturesByUser;
-        private final Set<String> mVouchProviders;
 
         private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
-        private final VouchesChangedMonitor mVouchesChangedMonitor = new VouchesChangedMonitor();
 
         private final Handler mCallbackHandler;
 
-        private final Object mVouchedSignaturesLocked = new Object();
-
         public LauncherAppsImpl(Context context) {
             mContext = context;
             mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -165,9 +136,6 @@
             mShortcutServiceInternal.addListener(mPackageMonitor);
             mCallbackHandler = BackgroundThread.getHandler();
             mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-            mVouchedSignaturesByUser = new ConcurrentHashMap<>();
-            mVouchProviders = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
-            mVouchesChangedMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
         }
 
         @VisibleForTesting
@@ -387,7 +355,7 @@
                     }
                     ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
                             callingUid, user.getIdentifier());
-                    if (shouldShowHiddenApp(user, appInfo)) {
+                    if (shouldShowHiddenApp(appInfo)) {
                         ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
                         if (info != null) {
                             result.add(info);
@@ -403,7 +371,7 @@
                         user.getIdentifier(), callingUid);
                 for (ApplicationInfo applicationInfo : installedPackages) {
                     if (!visiblePackages.contains(applicationInfo.packageName)) {
-                        if (!shouldShowHiddenApp(user, applicationInfo)) {
+                        if (!shouldShowHiddenApp(applicationInfo)) {
                             continue;
                         }
                         ResolveInfo info = getHiddenAppActivityInfo(applicationInfo.packageName,
@@ -419,126 +387,13 @@
             }
         }
 
-        private boolean shouldShowHiddenApp(UserHandle user, ApplicationInfo appInfo) {
+        private static boolean shouldShowHiddenApp(ApplicationInfo appInfo) {
             if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
                 return false;
             }
-            if (!mVouchedSignaturesByUser.containsKey(user)) {
-                initVouchedSignatures(user);
-            }
-            if (mVouchProviders.contains(appInfo.packageName)) {
-                // If it's a vouching packages then we must show hidden app
-                return true;
-            }
-            // If app's signature is in vouch list, do not show hidden app
-            final Set<String> vouches = mVouchedSignaturesByUser.get(user);
-            try {
-                final PackageInfo pkgInfo = mContext.getPackageManager().getPackageInfo(
-                        appInfo.packageName, PackageManager.GET_SIGNING_CERTIFICATES);
-                final Signature[] signatures = getLatestSignatures(pkgInfo.signingInfo);
-                // If any of the signatures appears in vouches, then we don't show hidden app
-                for (Signature signature : signatures) {
-                    final String certDigest = computePackageCertDigest(signature);
-                    if (vouches.contains(certDigest)) {
-                        return false;
-                    }
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                // Should not happen
-            }
             return true;
         }
 
-        @VisibleForTesting
-        static String computePackageCertDigest(Signature signature) {
-            MessageDigest messageDigest;
-            try {
-                messageDigest = MessageDigest.getInstance("SHA1");
-            } catch (NoSuchAlgorithmException e) {
-                // Should not happen
-                return null;
-            }
-            messageDigest.update(signature.toByteArray());
-            final byte[] digest = messageDigest.digest();
-            return ByteStringUtils.toHexString(digest);
-        }
-
-        @VisibleForTesting
-        static Signature[] getLatestSignatures(SigningInfo signingInfo) {
-            if (signingInfo.hasMultipleSigners()) {
-                return signingInfo.getApkContentsSigners();
-            } else {
-                final Signature[] signatures = signingInfo.getSigningCertificateHistory();
-                return new Signature[]{signatures[0]};
-            }
-        }
-
-        private void updateVouches(String packageName, UserHandle user) {
-            final PackageManagerInternal pmInt =
-                    LocalServices.getService(PackageManagerInternal.class);
-            ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName,
-                    PackageManager.GET_META_DATA, Binder.getCallingUid(), user.getIdentifier());
-            updateVouches(appInfo, user);
-        }
-
-        private void updateVouches(ApplicationInfo appInfo, UserHandle user) {
-            if (appInfo.metaData == null) {
-                // No meta-data
-                return;
-            }
-            int tokenResourceId = appInfo.metaData.getInt(LauncherApps.VOUCHED_CERTS_KEY);
-            if (tokenResourceId == 0) {
-                // No xml file
-                return;
-            }
-            mVouchProviders.add(appInfo.packageName);
-            Set<String> vouches = mVouchedSignaturesByUser.get(user);
-            try {
-                List<String> signatures = Arrays.asList(
-                        mContext.getPackageManager().getResourcesForApplication(
-                                appInfo.packageName).getStringArray(tokenResourceId));
-                for (String signature : signatures) {
-                    vouches.add(signature.toUpperCase());
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                // Should not happen
-            }
-        }
-
-        private void initVouchedSignatures(UserHandle user) {
-            synchronized (mVouchedSignaturesLocked) {
-                if (mVouchedSignaturesByUser.contains(user)) {
-                    return;
-                }
-                final long startTime = mStatLogger.getTime();
-
-                Set<String> vouches = Collections.newSetFromMap(
-                        new ConcurrentHashMap<String, Boolean>());
-
-                final int callingUid = injectBinderCallingUid();
-                long ident = Binder.clearCallingIdentity();
-                try {
-                    final PackageManagerInternal pmInt =
-                            LocalServices.getService(PackageManagerInternal.class);
-                    List<ApplicationInfo> installedPackages = pmInt.getInstalledApplications(
-                            PackageManager.GET_META_DATA, user.getIdentifier(), callingUid);
-                    for (ApplicationInfo appInfo : installedPackages) {
-                        updateVouches(appInfo, user);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-                mVouchedSignaturesByUser.putIfAbsent(user, vouches);
-                mStatLogger.logDurationStat(Stats.INIT_VOUCHED_SIGNATURES, startTime);
-            }
-        }
-
-        @Override
-        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
-            mStatLogger.dump(pw, "  ");
-        }
-
         @Override
         public ActivityInfo resolveActivity(
                 String callingPackage, ComponentName component, UserHandle user)
@@ -905,18 +760,6 @@
             mCallbackHandler.post(r);
         }
 
-        private class VouchesChangedMonitor extends PackageMonitor {
-            @Override
-            public void onPackageAdded(String packageName, int uid) {
-                updateVouches(packageName, new UserHandle(getChangingUserId()));
-            }
-
-            @Override
-            public void onPackageModified(String packageName) {
-                updateVouches(packageName, new UserHandle(getChangingUserId()));
-            }
-        }
-
         private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
 
             // TODO Simplify with lambdas.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6fe32c5..3300900 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1460,8 +1460,9 @@
                             Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod,
                                     args.traceCookie);
                         }
-                    } else {
-                        Slog.e(TAG, "Bogus post-install token " + msg.arg1);
+                    } else if (DEBUG_INSTALL) {
+                        // No post-install when we run restore from installExistingPackageForUser
+                        Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
                     }
 
                     Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
@@ -12737,6 +12738,11 @@
     @Override
     public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
             int installReason) {
+        if (DEBUG_INSTALL) {
+            Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
+                    + " installFlags=" + installFlags + " installReason=" + installReason);
+        }
+
         final int callingUid = Binder.getCallingUid();
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
                 != PackageManager.PERMISSION_GRANTED
@@ -12807,6 +12813,11 @@
                 synchronized (mPackages) {
                     updateSequenceNumberLP(pkgSetting, new int[]{ userId });
                 }
+                // start async restore with no post-install since we finish install here
+                PackageInstalledInfo res =
+                        createPackageInstalledInfo(PackageManager.INSTALL_SUCCEEDED);
+                res.pkg = pkgSetting.pkg;
+                restoreAndPostInstall(userId, res, null);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -13746,8 +13757,8 @@
                 }
             }
             for (InstallRequest request : installRequests) {
-                resolvePackageInstalledInfo(request.args,
-                        request.installResult);
+                restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
+                        new PostInstallData(request.args, request.installResult));
             }
         });
     }
@@ -13762,7 +13773,14 @@
         return res;
     }
 
-    private void resolvePackageInstalledInfo(InstallArgs args, PackageInstalledInfo res) {
+    /** @param data Post-install is performed only if this is non-null. */
+    private void restoreAndPostInstall(
+            int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
+        if (DEBUG_INSTALL) {
+            Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package="
+                    + res.pkg.packageName);
+        }
+
         // A restore should be performed at this point if (a) the install
         // succeeded, (b) the operation is not an update, and (c) the new
         // package has not opted out of backup participation.
@@ -13778,9 +13796,12 @@
         int token;
         if (mNextInstallToken < 0) mNextInstallToken = 1;
         token = mNextInstallToken++;
+        if (data != null) {
+            mRunningInstalls.put(token, data);
+        } else if (DEBUG_INSTALL) {
+            Log.v(TAG, "No post-install required for " + token);
+        }
 
-        PostInstallData data = new PostInstallData(args, res);
-        mRunningInstalls.put(token, data);
         if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
 
         if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
@@ -13791,7 +13812,6 @@
             IBackupManager bm = IBackupManager.Stub.asInterface(
                     ServiceManager.getService(Context.BACKUP_SERVICE));
             if (bm != null) {
-                int userId = args.user.getIdentifier();
                 // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
                 // in the BackupManager. USER_ALL is used in compatibility tests.
                 if (userId == UserHandle.USER_ALL) {
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index dcb2ff5..f3c19d0 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -81,7 +81,7 @@
 static jmethodID method_correctionSatConstType;
 static jmethodID method_correctionSatId;
 static jmethodID method_correctionSatCarrierFreq;
-static jmethodID method_correctionSatIsLos;
+static jmethodID method_correctionSatIsLosProb;
 static jmethodID method_correctionSatEpl;
 static jmethodID method_correctionSatEplUnc;
 static jmethodID method_correctionSatRefPlane;
@@ -2277,8 +2277,8 @@
                 singleSatCorrClass, "getSatId", "()I");
             method_correctionSatCarrierFreq = env->GetMethodID(
                 singleSatCorrClass, "getCarrierFrequencyHz", "()F");
-            method_correctionSatIsLos = env->GetMethodID(
-                singleSatCorrClass,"getSatIsLos", "()Z");
+            method_correctionSatIsLosProb = env->GetMethodID(
+                singleSatCorrClass,"getProbSatIsLos", "()F");
             method_correctionSatEpl = env->GetMethodID(
                 singleSatCorrClass, "getExcessPathLengthMeters", "()F");
             method_correctionSatEplUnc = env->GetMethodID(
@@ -2296,8 +2296,8 @@
             env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId);
         jfloat carrierFreqHz = env->CallFloatMethod(
             singleSatCorrectionObj, method_correctionSatCarrierFreq);
-        jboolean satIsLos = env->CallBooleanMethod(singleSatCorrectionObj,
-            method_correctionSatIsLos);
+        jfloat probSatIsLos = env->CallFloatMethod(singleSatCorrectionObj,
+            method_correctionSatIsLosProb);
         jfloat eplMeters =
             env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl);
         jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj,
@@ -2337,7 +2337,7 @@
             .constellation = static_cast<GnssConstellationType>(constType),
             .svid = static_cast<uint16_t>(satId),
             .carrierFrequencyHz = carrierFreqHz,
-            .satIsLos = static_cast<bool>(satIsLos),
+            .probSatIsLos = probSatIsLos,
             .excessPathLengthMeters = eplMeters,
             .excessPathLengthUncertaintyMeters = eplUncMeters,
             .reflectingPlane = reflectingPlane,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 8a9e5d1..c2d4846 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -23,6 +23,7 @@
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -91,6 +92,9 @@
     private static final byte[] TEST_VAULT_HANDLE =
             new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
     private static final String TEST_APP_KEY_ALIAS = "rcleaver";
+    private static final byte[] TEST_APP_KEY_METADATA_NULL = null;
+    private static final byte[] TEST_APP_KEY_METADATA_NON_NULL =
+            "mdata".getBytes(StandardCharsets.UTF_8);
     private static final int TEST_GENERATION_ID = 2;
     private static final int TEST_CREDENTIAL_TYPE = CREDENTIAL_TYPE_PATTERN;
     private static final String TEST_CREDENTIAL = "pas123";
@@ -251,7 +255,7 @@
                 TEST_USER_ID,
                 TEST_RECOVERY_AGENT_UID,
                 TEST_APP_KEY_ALIAS,
-                WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
+                WrappedKey.fromSecretKey(mEncryptKey, applicationKey, TEST_APP_KEY_METADATA_NULL));
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
 
         mKeySyncTask.run();
@@ -267,7 +271,7 @@
                 TEST_USER_ID,
                 TEST_RECOVERY_AGENT_UID,
                 TEST_APP_KEY_ALIAS,
-                WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
+                WrappedKey.fromSecretKey(mEncryptKey, applicationKey, TEST_APP_KEY_METADATA_NULL));
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
@@ -545,18 +549,20 @@
         assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
         assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias());
         byte[] appKey = KeySyncUtils.decryptApplicationKey(
-                recoveryKey, keyData.getEncryptedKeyMaterial());
+                recoveryKey, keyData.getEncryptedKeyMaterial(), TEST_APP_KEY_METADATA_NULL);
         assertThat(appKey).isEqualTo(applicationKey.getEncoded());
     }
 
     @Test
-    public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath() throws Exception {
+    public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath_withNullKeyMetadata()
+            throws Exception {
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         mRecoverableKeyStoreDb.setServerParams(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
-        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS,
+                TEST_APP_KEY_METADATA_NULL);
 
         mKeySyncTask.run();
 
@@ -564,6 +570,33 @@
         verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
         List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
         assertThat(applicationKeys).hasSize(1);
+        WrappedApplicationKey keyData = applicationKeys.get(0);
+        assertThat(keyData.getAlias()).isEqualTo(TEST_APP_KEY_ALIAS);
+        assertThat(keyData.getMetadata()).isEqualTo(TEST_APP_KEY_METADATA_NULL);
+        assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
+                .isEqualTo(TestData.CERT_PATH_1);
+    }
+
+    @Test
+    public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath_withNonNullKeyMetadata()
+            throws Exception {
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+        mRecoverableKeyStoreDb.setServerParams(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+        when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS,
+                TEST_APP_KEY_METADATA_NON_NULL);
+
+        mKeySyncTask.run();
+
+        KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+        verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
+        List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
+        assertThat(applicationKeys).hasSize(1);
+        WrappedApplicationKey keyData = applicationKeys.get(0);
+        assertThat(keyData.getAlias()).isEqualTo(TEST_APP_KEY_ALIAS);
+        assertThat(keyData.getMetadata()).isEqualTo(TEST_APP_KEY_METADATA_NON_NULL);
         assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
                 .isEqualTo(TestData.CERT_PATH_1);
     }
@@ -788,6 +821,11 @@
 
     private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias)
             throws Exception{
+        return addApplicationKey(userId, recoveryAgentUid, alias, TEST_APP_KEY_METADATA_NULL);
+    }
+
+    private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias,
+            byte[] metadata) throws Exception {
         SecretKey applicationKey = generateKey();
         mRecoverableKeyStoreDb.setServerParams(
                 userId, recoveryAgentUid, TEST_VAULT_HANDLE);
@@ -800,7 +838,7 @@
                 userId,
                 recoveryAgentUid,
                 alias,
-                WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
+                WrappedKey.fromSecretKey(mEncryptKey, applicationKey, metadata));
         return applicationKey;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index f832d3c..178fd10 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -22,6 +22,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
+import android.util.Pair;
+
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -65,7 +67,8 @@
     private static final byte[] RECOVERY_RESPONSE_HEADER =
             "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
     private static final int PUBLIC_KEY_LENGTH_BYTES = 65;
-
+    private static final byte[] NULL_METADATA = null;
+    private static final byte[] NON_NULL_METADATA = "somemetadata".getBytes(StandardCharsets.UTF_8);
 
     @Test
     public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
@@ -125,18 +128,35 @@
     }
 
     @Test
-    public void decryptApplicationKey_decryptsAnApplicationKeyEncryptedWithSecureBox()
-            throws Exception {
+    public void decryptApplicationKey_decryptsAnApplicationKey_nullMetadata() throws Exception {
         String alias = "phoebe";
         SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
         SecretKey applicationKey = generateApplicationKey();
         Map<String, byte[]> encryptedKeys =
                 KeySyncUtils.encryptKeysWithRecoveryKey(
-                        recoveryKey, ImmutableMap.of(alias, applicationKey));
+                        recoveryKey,
+                        ImmutableMap.of(alias, Pair.create(applicationKey, NULL_METADATA)));
         byte[] encryptedKey = encryptedKeys.get(alias);
 
-        byte[] keyMaterial =
-                KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(), encryptedKey);
+        byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
+                encryptedKey, NULL_METADATA);
+
+        assertArrayEquals(applicationKey.getEncoded(), keyMaterial);
+    }
+
+    @Test
+    public void decryptApplicationKey_decryptsAnApplicationKey_nonNullMetadata() throws Exception {
+        String alias = "phoebe";
+        SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
+        SecretKey applicationKey = generateApplicationKey();
+        Map<String, byte[]> encryptedKeys =
+                KeySyncUtils.encryptKeysWithRecoveryKey(
+                        recoveryKey,
+                        ImmutableMap.of(alias, Pair.create(applicationKey, NON_NULL_METADATA)));
+        byte[] encryptedKey = encryptedKeys.get(alias);
+
+        byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
+                encryptedKey, NON_NULL_METADATA);
 
         assertArrayEquals(applicationKey.getEncoded(), keyMaterial);
     }
@@ -147,12 +167,13 @@
         Map<String, byte[]> encryptedKeys =
                 KeySyncUtils.encryptKeysWithRecoveryKey(
                         KeySyncUtils.generateRecoveryKey(),
-                        ImmutableMap.of("casper", generateApplicationKey()));
+                        ImmutableMap.of("casper",
+                                Pair.create(generateApplicationKey(), NULL_METADATA)));
         byte[] encryptedKey = encryptedKeys.get(alias);
 
         try {
-            KeySyncUtils.decryptApplicationKey(
-                    KeySyncUtils.generateRecoveryKey().getEncoded(), encryptedKey);
+            KeySyncUtils.decryptApplicationKey(KeySyncUtils.generateRecoveryKey().getEncoded(),
+                    encryptedKey, NULL_METADATA);
             fail("Did not throw decrypting with bad key.");
         } catch (AEADBadTagException error) {
             // expected
@@ -160,6 +181,47 @@
     }
 
     @Test
+    public void decryptApplicationKey_throwsIfWrongMetadata() throws Exception {
+        String alias1 = "casper1";
+        String alias2 = "casper2";
+        String alias3 = "casper3";
+        SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
+
+        Map<String, byte[]> encryptedKeys =
+                KeySyncUtils.encryptKeysWithRecoveryKey(
+                        recoveryKey,
+                        ImmutableMap.of(
+                                alias1,
+                                Pair.create(generateApplicationKey(), NULL_METADATA),
+                                alias2,
+                                Pair.create(generateApplicationKey(), NON_NULL_METADATA),
+                                alias3,
+                                Pair.create(generateApplicationKey(), NON_NULL_METADATA)));
+
+        try {
+            KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
+                    encryptedKeys.get(alias1), NON_NULL_METADATA);
+            fail("Did not throw decrypting with wrong metadata.");
+        } catch (AEADBadTagException error) {
+            // expected
+        }
+        try {
+            KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
+                    encryptedKeys.get(alias2), NULL_METADATA);
+            fail("Did not throw decrypting with wrong metadata.");
+        } catch (AEADBadTagException error) {
+            // expected
+        }
+        try {
+            KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
+                    encryptedKeys.get(alias3), "different".getBytes(StandardCharsets.UTF_8));
+            fail("Did not throw decrypting with wrong metadata.");
+        } catch (AEADBadTagException error) {
+            // expected
+        }
+    }
+
+    @Test
     public void decryptRecoveryKey_decryptsALocallyEncryptedKey() throws Exception {
         SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
         byte[] encrypted = KeySyncUtils.locallyEncryptRecoveryKey(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
index 48afb8b..c295177 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import android.content.Context;
 import android.security.keystore.AndroidKeyStoreSecretKey;
@@ -60,6 +61,9 @@
     private static final int TEST_USER_ID = 1000;
     private static final int KEYSTORE_UID_SELF = -1;
     private static final int GCM_TAG_LENGTH_BITS = 128;
+    private static final byte[] NULL_METADATA = null;
+    private static final byte[] NON_NULL_METADATA = "test-metadata".getBytes(
+            StandardCharsets.UTF_8);
 
     private PlatformEncryptionKey mPlatformKey;
     private PlatformDecryptionKey mDecryptKey;
@@ -90,18 +94,29 @@
     }
 
     @Test
-    public void generateAndStoreKey_storesWrappedKey() throws Exception {
+    public void generateAndStoreKey_storesWrappedKey_nullMetadata() throws Exception {
         mRecoverableKeyGenerator.generateAndStoreKey(
-                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS);
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NULL_METADATA);
 
         WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
         assertNotNull(wrappedKey);
+        assertNull(wrappedKey.getKeyMetadata());
+    }
+
+    @Test
+    public void generateAndStoreKey_storesWrappedKey_nonNullMetadata() throws Exception {
+        mRecoverableKeyGenerator.generateAndStoreKey(
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NON_NULL_METADATA);
+
+        WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
+        assertNotNull(wrappedKey);
+        assertArrayEquals(NON_NULL_METADATA, wrappedKey.getKeyMetadata());
     }
 
     @Test
     public void generateAndStoreKey_returnsRawMaterialOfCorrectLength() throws Exception {
         byte[] rawKey = mRecoverableKeyGenerator.generateAndStoreKey(
-                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS);
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NON_NULL_METADATA);
 
         assertEquals(KEY_SIZE_BYTES, rawKey.length);
     }
@@ -109,7 +124,7 @@
     @Test
     public void generateAndStoreKey_storesTheWrappedVersionOfTheRawMaterial() throws Exception {
         byte[] rawMaterial = mRecoverableKeyGenerator.generateAndStoreKey(
-                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS);
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, NULL_METADATA);
 
         WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
         Cipher cipher = Cipher.getInstance(KEY_WRAP_ALGORITHM);
@@ -120,10 +135,30 @@
     }
 
     @Test
+    public void importKey_storesNullMetadata() throws Exception {
+        mRecoverableKeyGenerator.importKey(
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS,
+                randomBytes(KEY_SIZE_BYTES),
+                NULL_METADATA);
+        assertNull(mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS).getKeyMetadata());
+    }
+
+    @Test
+    public void importKey_storesNonNullMetadata() throws Exception {
+        mRecoverableKeyGenerator.importKey(
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS,
+                randomBytes(KEY_SIZE_BYTES),
+                NON_NULL_METADATA);
+        assertArrayEquals(NON_NULL_METADATA,
+                mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS).getKeyMetadata());
+    }
+
+    @Test
     public void importKey_storesTheWrappedVersionOfTheRawMaterial() throws Exception {
         byte[] rawMaterial = randomBytes(KEY_SIZE_BYTES);
         mRecoverableKeyGenerator.importKey(
-                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, rawMaterial);
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, rawMaterial,
+                NULL_METADATA);
 
         WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
         Cipher cipher = Cipher.getInstance(KEY_WRAP_ALGORITHM);
@@ -145,10 +180,6 @@
         return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
     }
 
-    private static byte[] getUtf8Bytes(String s) {
-        return s.getBytes(StandardCharsets.UTF_8);
-    }
-
     private static byte[] randomBytes(int n) {
         byte[] bytes = new byte[n];
         new Random().nextBytes(bytes);
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 b15863d..c78b96d 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
@@ -52,6 +52,7 @@
 import android.security.keystore.recovery.RecoveryCertPath;
 import android.security.keystore.recovery.TrustedRootCertificates;
 import android.security.keystore.recovery.WrappedApplicationKey;
+import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -134,6 +135,8 @@
     private static final int GENERATION_ID = 1;
     private static final byte[] NONCE = getUtf8Bytes("nonce");
     private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial");
+    private static final byte[] KEY_METADATA_NULL = null;
+    private static final byte[] KEY_METADATA_NON_NULL = getUtf8Bytes("keymetametadata");
     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";
@@ -231,6 +234,77 @@
     }
 
     @Test
+    public void importKeyWithMetadata_nullMetadata_storesTheKey() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
+
+        mRecoverableKeyStoreManager.importKeyWithMetadata(
+                TEST_ALIAS, keyMaterial, KEY_METADATA_NULL);
+
+        assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
+    public void importKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
+
+        mRecoverableKeyStoreManager.importKeyWithMetadata(
+                TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL);
+
+        assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
+    public void importKeyWithMetadata_throwsIfInvalidLength() throws Exception {
+        byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1);
+        try {
+            mRecoverableKeyStoreManager.importKeyWithMetadata(
+                    TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL);
+            fail("should have thrown");
+        } catch (ServiceSpecificException e) {
+            assertThat(e.getMessage()).contains("not contain 256 bits");
+        }
+    }
+
+    @Test
+    public void importKeyWithMetadata_throwsIfNullKey() throws Exception {
+        try {
+            mRecoverableKeyStoreManager.importKeyWithMetadata(
+                    TEST_ALIAS, /*keyBytes=*/ null, KEY_METADATA_NON_NULL);
+            fail("should have thrown");
+        } catch (NullPointerException e) {
+            assertThat(e.getMessage()).contains("is null");
+        }
+    }
+
+    @Test
+    public void generateKeyWithMetadata_nullMetadata_storesTheKey() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+
+        mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL);
+
+        assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
+    public void generateKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+
+        mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NON_NULL);
+
+        assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
     public void removeKey_removesAKey() throws Exception {
         int uid = Binder.getCallingUid();
         mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
@@ -1143,7 +1217,10 @@
         int status = 100;
         int status2 = 200;
         String alias = "key1";
-        WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, GENERATION_ID, status);
+        byte[] keyMetadata = null;
+
+        WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, keyMetadata, GENERATION_ID,
+                status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
         Map<String, Integer> statuses =
                 mRecoverableKeyStoreManager.getRecoveryStatus();
@@ -1169,7 +1246,8 @@
     private static byte[] encryptedApplicationKey(
             SecretKey recoveryKey, byte[] applicationKey) throws Exception {
         return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
-                TEST_ALIAS, new SecretKeySpec(applicationKey, "AES")
+                TEST_ALIAS,
+                Pair.create(new SecretKeySpec(applicationKey, "AES"), /*metadata=*/ null)
         )).get(TEST_ALIAS);
     }
 
@@ -1203,7 +1281,7 @@
 
     private void generateKeyAndSimulateSync(int userId, int uid, int snapshotVersion)
             throws Exception{
-        mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
+        mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL);
         // Simulate key sync.
         mRecoverableKeyStoreDb.setSnapshotVersion(userId, uid, snapshotVersion);
         mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
index 944d6e0..9b4c3be 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
@@ -3,6 +3,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.security.keystore.recovery.TrustedRootCertificates;
+import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -64,10 +65,10 @@
 
     @Test
     public void testKeepOnlyWhitelistedInsecureKeys_emptyKeysList() throws Exception {
-        Map<String, SecretKey> rawKeys = new HashMap<>();
-        Map<String, SecretKey> expectedResult = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>();
 
-        Map<String, SecretKey> filteredKeys =
+        Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
         assertThat(filteredKeys.entrySet()).containsAllIn(rawKeys.entrySet());
@@ -75,13 +76,13 @@
 
     @Test
     public void testKeepOnlyWhitelistedInsecureKeys_singleNonWhitelistedKey() throws Exception {
-        Map<String, SecretKey> rawKeys = new HashMap<>();
-        Map<String, SecretKey> expectedResult = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>();
 
         String alias = "secureAlias";
-        rawKeys.put(alias, TestData.generateKey());
+        rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null));
 
-        Map<String, SecretKey> filteredKeys =
+        Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
         assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
@@ -89,14 +90,14 @@
 
     @Test
     public void testKeepOnlyWhitelistedInsecureKeys_singleWhitelistedKey() throws Exception {
-        Map<String, SecretKey> rawKeys = new HashMap<>();
-        Map<String, SecretKey> expectedResult = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>();
 
         String alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX;
-        rawKeys.put(alias, TestData.generateKey());
+        rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null));
         expectedResult.put(alias, rawKeys.get(alias));
 
-        Map<String, SecretKey> filteredKeys =
+        Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
         assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
@@ -104,21 +105,21 @@
 
     @Test
     public void testKeepOnlyWhitelistedInsecureKeys() throws Exception {
-        Map<String, SecretKey> rawKeys = new HashMap<>();
-        Map<String, SecretKey> expectedResult = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> rawKeys = new HashMap<>();
+        Map<String, Pair<SecretKey, byte[]>> expectedResult = new HashMap<>();
 
         String alias = "SECURE_ALIAS" + TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX;
-        rawKeys.put(alias, TestData.generateKey());
+        rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null));
 
         alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "1";
-        rawKeys.put(alias, TestData.generateKey());
+        rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null));
         expectedResult.put(alias, rawKeys.get(alias));
 
         alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "2";
-        rawKeys.put(alias, TestData.generateKey());
+        rawKeys.put(alias, Pair.create(TestData.generateKey(), /*metadata=*/ null));
         expectedResult.put(alias, rawKeys.get(alias));
 
-        Map<String, SecretKey> filteredKeys =
+        Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
         assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
index b5ee60e..9813ab7 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
@@ -24,6 +24,7 @@
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
+import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -32,6 +33,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.nio.charset.StandardCharsets;
 import java.security.KeyStore;
 import java.util.HashMap;
 import java.util.Map;
@@ -47,26 +49,49 @@
     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
     private static final String KEY_ALGORITHM = "AES";
     private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
-    private static final String WRAPPING_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias";
+    private static final String WRAPPED_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias";
     private static final int GENERATION_ID = 1;
     private static final int GCM_TAG_LENGTH_BYTES = 16;
     private static final int BITS_PER_BYTE = 8;
     private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE;
+    private static final byte[] NULL_METADATA = null;
+    private static final byte[] NON_NULL_METADATA = "keyMetadata".getBytes(StandardCharsets.UTF_8);
 
     @After
     public void tearDown() throws Exception {
         KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
         keyStore.load(/*param=*/ null);
-        keyStore.deleteEntry(WRAPPING_KEY_ALIAS);
+        keyStore.deleteEntry(WRAPPED_KEY_ALIAS);
     }
 
+    // TODO: Add tests for non-null metadata
+
     @Test
-    public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped() throws Exception {
+    public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped_nullMetadata() throws Exception {
         PlatformEncryptionKey wrappingKey = new PlatformEncryptionKey(
                 GENERATION_ID, generateAndroidKeyStoreKey());
         SecretKey rawKey = generateKey();
 
-        WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey);
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey, NULL_METADATA);
+
+        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+        cipher.init(
+                Cipher.UNWRAP_MODE,
+                wrappingKey.getKey(),
+                new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
+        SecretKey unwrappedKey = (SecretKey) cipher.unwrap(
+                wrappedKey.getKeyMaterial(), KEY_ALGORITHM, Cipher.SECRET_KEY);
+        assertEquals(rawKey, unwrappedKey);
+    }
+
+    @Test
+    public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped_nonNullMetadata()
+            throws Exception {
+        PlatformEncryptionKey wrappingKey = new PlatformEncryptionKey(
+                GENERATION_ID, generateAndroidKeyStoreKey());
+        SecretKey rawKey = generateKey();
+
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey, NON_NULL_METADATA);
 
         Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
         cipher.init(
@@ -84,27 +109,47 @@
                 GENERATION_ID, generateAndroidKeyStoreKey());
         SecretKey rawKey = generateKey();
 
-        WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey);
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey, NULL_METADATA);
 
         assertEquals(GENERATION_ID, wrappedKey.getPlatformKeyGenerationId());
     }
 
     @Test
-    public void decryptWrappedKeys_decryptsWrappedKeys() throws Exception {
+    public void decryptWrappedKeys_decryptsWrappedKeys_nullMetadata() throws Exception {
         String alias = "karlin";
         AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
         SecretKey appKey = generateKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
-                new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey);
+                new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
         HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
         keysByAlias.put(alias, wrappedKey);
 
-        Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(
+        Map<String, Pair<SecretKey, byte[]>> unwrappedKeys = WrappedKey.unwrapKeys(
                 new PlatformDecryptionKey(GENERATION_ID, platformKey), keysByAlias);
 
         assertEquals(1, unwrappedKeys.size());
         assertTrue(unwrappedKeys.containsKey(alias));
-        assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).getEncoded());
+        assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).first.getEncoded());
+        assertArrayEquals(null, unwrappedKeys.get(alias).second);
+    }
+
+    @Test
+    public void decryptWrappedKeys_decryptsWrappedKeys_nonNullMetadata() throws Exception {
+        String alias = "karlin";
+        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey appKey = generateKey();
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(
+                new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NON_NULL_METADATA);
+        HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
+        keysByAlias.put(alias, wrappedKey);
+
+        Map<String, Pair<SecretKey, byte[]>> unwrappedKeys = WrappedKey.unwrapKeys(
+                new PlatformDecryptionKey(GENERATION_ID, platformKey), keysByAlias);
+
+        assertEquals(1, unwrappedKeys.size());
+        assertTrue(unwrappedKeys.containsKey(alias));
+        assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).first.getEncoded());
+        assertArrayEquals(NON_NULL_METADATA, unwrappedKeys.get(alias).second);
     }
 
     @Test
@@ -113,11 +158,11 @@
         AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
         SecretKey appKey = generateKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
-                new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey);
+                new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
         HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
         keysByAlias.put(alias, wrappedKey);
 
-        Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(
+        Map<String, Pair<SecretKey, byte[]>> unwrappedKeys = WrappedKey.unwrapKeys(
                 new PlatformDecryptionKey(GENERATION_ID, generateAndroidKeyStoreKey()),
                 keysByAlias);
 
@@ -128,7 +173,8 @@
     public void decryptWrappedKeys_throwsIfPlatformKeyGenerationIdDoesNotMatch() throws Exception {
         AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
-                new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey());
+                new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey(),
+                /*metadata=*/ null);
         HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
         keysByAlias.put("benji", wrappedKey);
 
@@ -156,19 +202,11 @@
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
         keyGenerator.init(new KeyGenParameterSpec.Builder(
-                WRAPPING_KEY_ALIAS,
+                WRAPPED_KEY_ALIAS,
                 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
         return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
     }
-
-    private PlatformDecryptionKey generatePlatformDecryptionKey() throws Exception {
-        return generatePlatformDecryptionKey(GENERATION_ID);
-    }
-
-    private PlatformDecryptionKey generatePlatformDecryptionKey(int generationId) throws Exception {
-        return new PlatformDecryptionKey(generationId, generateAndroidKeyStoreKey());
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
index 7130b42..35215c3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
@@ -60,6 +60,7 @@
     private static final String TEST_ROOT_ALIAS = "root_cert_alias";
     private static final byte[] TEST_CERT_PATH = "test-cert-path".getBytes(UTF_8);
     private static final long TEST_CERT_SERIAL = 1000L;
+    private static final byte[] TEST_KEY_METADATA = "test-key-metadata".getBytes(UTF_8);
 
     private static final String SQL_CREATE_V2_TABLE_KEYS =
             "CREATE TABLE " + KeysEntry.TABLE_NAME + "( "
@@ -120,14 +121,14 @@
     @Test
     public void onCreate() throws Exception {
         mDatabaseHelper.onCreate(mDatabase);
-        checkAllColumns();
+        checkAllColumns_latest();
     }
 
     @Test
     public void onUpgrade_beforeV2() throws Exception {
         mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 1,
                 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
-        checkAllColumns();
+        checkAllColumns_latest();
     }
 
     @Test
@@ -135,11 +136,11 @@
         createV2Tables();
         mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2,
                 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
-        checkAllColumns();
+        checkAllColumns_latest();
     }
 
     @Test
-    public void onUpgrade_v2_to_v3_to_v4() throws Exception {
+    public void onUpgrade_v2_to_v3_to_v4_to_latest() throws Exception {
         createV2Tables();
 
         assertThat(isRootOfTrustTableAvailable()).isFalse(); // V2 doesn't have the table;
@@ -148,9 +149,12 @@
 
         assertThat(isRootOfTrustTableAvailable()).isFalse(); // V3 doesn't have the table;
 
-        mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3,
+        mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3, /*newVersion=*/ 4);
+        checkAllColumns_v4();
+
+        mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 4,
                 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
-        checkAllColumns();
+        checkAllColumns_latest();
     }
 
     private boolean isRootOfTrustTableAvailable() {
@@ -160,11 +164,11 @@
         values.put(RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS, TEST_ROOT_ALIAS);
         values.put(RootOfTrustEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
         values.put(RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
-        return mDatabase.insert(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)
+        return mDatabase.replace(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)
                 > -1;
     }
 
-    private void checkAllColumns() throws Exception {
+    private void checkAllColumns_v4() throws Exception {
         // Check the table containing encrypted application keys
         ContentValues values = new ContentValues();
         values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
@@ -175,7 +179,7 @@
         values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID);
         values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT);
         values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS);
-        assertThat(mDatabase.insert(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+        assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
                 .isGreaterThan(-1L);
 
         // Check the table about user metadata
@@ -183,7 +187,8 @@
         values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
         values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID,
                 TEST_PLATFORM_KEY_GENERATION_ID);
-        assertThat(mDatabase.insert(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+        assertThat(
+                mDatabase.replace(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
                 .isGreaterThan(-1L);
 
         // Check the table about recovery service metadata
@@ -202,11 +207,32 @@
         values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
         values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
         assertThat(
-                mDatabase.insert(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
+                mDatabase.replace(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
                         values))
                 .isGreaterThan(-1L);
 
         // Check the table about recovery service and root of trust data introduced in V4
         assertThat(isRootOfTrustTableAvailable()).isTrue();
     }
+
+    private void checkAllColumns_latest() throws Exception {
+        // Check all columns of the previous version first.
+        checkAllColumns_v4();
+
+        ContentValues values = new ContentValues();
+        values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+        values.put(KeysEntry.COLUMN_NAME_UID, TEST_UID);
+        values.put(KeysEntry.COLUMN_NAME_ALIAS, TEST_ALIAS);
+        values.put(KeysEntry.COLUMN_NAME_NONCE, TEST_NONCE);
+        values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, TEST_WRAPPED_KEY);
+        values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID);
+        values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT);
+        values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS);
+
+        // This column is added when upgrading from v4 to v5
+        values.put(KeysEntry.COLUMN_NAME_KEY_METADATA, TEST_KEY_METADATA);
+
+        assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+                .isGreaterThan(-1L);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 6a26f8c..7de9ffc 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -80,25 +80,33 @@
     public void insertKey_replacesOldKey() {
         int userId = 12;
         int uid = 10009;
-        String alias = "test";
-        WrappedKey oldWrappedKey = new WrappedKey(
-                getUtf8Bytes("nonce1"),
-                getUtf8Bytes("keymaterial1"),
-                /*platformKeyGenerationId=*/ 1);
-        mRecoverableKeyStoreDb.insertKey(
-                userId, uid, alias, oldWrappedKey);
-        byte[] nonce = getUtf8Bytes("nonce2");
-        byte[] keyMaterial = getUtf8Bytes("keymaterial2");
-        WrappedKey newWrappedKey = new WrappedKey(
-                nonce, keyMaterial, /*platformKeyGenerationId=*/2);
+        String alias = "test-alias";
 
-        mRecoverableKeyStoreDb.insertKey(
-                userId, uid, alias, newWrappedKey);
+        byte[] nonce = getUtf8Bytes("nonce1");
+        byte[] keyMaterial = getUtf8Bytes("keymaterial1");
+        byte[] keyMetadata = null;
+        int generationId = 1;
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
 
         WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
         assertArrayEquals(nonce, retrievedKey.getNonce());
         assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
-        assertEquals(2, retrievedKey.getPlatformKeyGenerationId());
+        assertArrayEquals(keyMetadata, retrievedKey.getKeyMetadata());
+        assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
+
+        nonce = getUtf8Bytes("nonce2");
+        keyMaterial = getUtf8Bytes("keymaterial2");
+        keyMetadata = getUtf8Bytes("keymetadata2");
+        generationId = 2;
+        wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
+
+        retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
+        assertArrayEquals(nonce, retrievedKey.getNonce());
+        assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
+        assertArrayEquals(keyMetadata, retrievedKey.getKeyMetadata());
+        assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
     }
 
     @Test
@@ -108,10 +116,12 @@
         WrappedKey key1 = new WrappedKey(
                 getUtf8Bytes("nonce1"),
                 getUtf8Bytes("key1"),
+                /*metadata=*/ null,
                 /*platformKeyGenerationId=*/ 1);
         WrappedKey key2 = new WrappedKey(
                 getUtf8Bytes("nonce2"),
                 getUtf8Bytes("key2"),
+                /*metadata=*/ null,
                 /*platformKeyGenerationId=*/ 1);
 
         mRecoverableKeyStoreDb.insertKey(userId, /*uid=*/ 1, alias, key1);
@@ -133,6 +143,7 @@
         WrappedKey key = new WrappedKey(
                 getUtf8Bytes("nonce1"),
                 getUtf8Bytes("key1"),
+                /*metadata=*/ null,
                 /*platformKeyGenerationId=*/ 1);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, key);
 
@@ -158,13 +169,16 @@
         String alias = "test";
         byte[] nonce = getUtf8Bytes("nonce");
         byte[] keyMaterial = getUtf8Bytes("keymaterial");
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, 120);
+        byte[] keyMetadata = getUtf8Bytes("keymetametametadata");
+
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, 120);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
 
         WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
 
         assertArrayEquals(nonce, retrievedKey.getNonce());
         assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
+        assertArrayEquals(keyMetadata, retrievedKey.getKeyMetadata());
         assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
         assertEquals(status,retrievedKey.getRecoveryStatus());
     }
@@ -174,20 +188,37 @@
         int userId = 12;
         int uid = 1009;
         int generationId = 6;
-        String alias = "test";
-        byte[] nonce = getUtf8Bytes("nonce");
-        byte[] keyMaterial = getUtf8Bytes("keymaterial");
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId);
-        mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
+
+        String alias1 = "alias1";
+        byte[] nonce1 = getUtf8Bytes("nonce1");
+        byte[] keyMaterial1 = getUtf8Bytes("keymaterial1");
+        byte[] keyMetadata1 = getUtf8Bytes("keyallmetadata1");
+        WrappedKey wrappedKey1 = new WrappedKey(nonce1, keyMaterial1, keyMetadata1, generationId);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias1, wrappedKey1);
+
+        String alias2 = "alias2";
+        byte[] nonce2 = getUtf8Bytes("nonce2");
+        byte[] keyMaterial2 = getUtf8Bytes("keymaterial2");
+        byte[] keyMetadata2 = null;
+        WrappedKey wrappedKey2 = new WrappedKey(nonce2, keyMaterial2, keyMetadata2, generationId);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias2, wrappedKey2);
 
         Map<String, WrappedKey> keys = mRecoverableKeyStoreDb.getAllKeys(userId, uid, generationId);
+        assertEquals(2, keys.size());
 
-        assertEquals(1, keys.size());
-        assertTrue(keys.containsKey(alias));
-        WrappedKey retrievedKey = keys.get(alias);
-        assertArrayEquals(nonce, retrievedKey.getNonce());
-        assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
-        assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
+        assertTrue(keys.containsKey(alias1));
+        WrappedKey retrievedKey1 = keys.get(alias1);
+        assertArrayEquals(nonce1, retrievedKey1.getNonce());
+        assertArrayEquals(keyMaterial1, retrievedKey1.getKeyMaterial());
+        assertArrayEquals(keyMetadata1, retrievedKey1.getKeyMetadata());
+        assertEquals(generationId, retrievedKey1.getPlatformKeyGenerationId());
+
+        assertTrue(keys.containsKey(alias2));
+        WrappedKey retrievedKey2 = keys.get(alias2);
+        assertArrayEquals(nonce2, retrievedKey2.getNonce());
+        assertArrayEquals(keyMaterial2, retrievedKey2.getKeyMaterial());
+        assertArrayEquals(keyMetadata2, retrievedKey2.getKeyMetadata());
+        assertEquals(generationId, retrievedKey2.getPlatformKeyGenerationId());
     }
 
     @Test
@@ -197,6 +228,7 @@
         WrappedKey wrappedKey = new WrappedKey(
                 getUtf8Bytes("nonce"),
                 getUtf8Bytes("keymaterial"),
+                /*metadata=*/ null,
                 /*platformKeyGenerationId=*/ 5);
         mRecoverableKeyStoreDb.insertKey(
                 userId, uid, /*alias=*/ "test", wrappedKey);
@@ -212,7 +244,8 @@
         int generationId = 12;
         int uid = 10009;
         WrappedKey wrappedKey = new WrappedKey(
-                getUtf8Bytes("nonce"), getUtf8Bytes("keymaterial"), generationId);
+                getUtf8Bytes("nonce"), getUtf8Bytes("keymaterial"), /*metadata=*/ null,
+                generationId);
         mRecoverableKeyStoreDb.insertKey(
                 /*userId=*/ 1, uid, /*alias=*/ "test", wrappedKey);
 
@@ -255,7 +288,10 @@
         String alias = "test";
         byte[] nonce = getUtf8Bytes("nonce");
         byte[] keyMaterial = getUtf8Bytes("keymaterial");
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+        byte[] keyMetadata = null;
+
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId,
+                status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
 
         WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
@@ -279,12 +315,16 @@
         String alias3 = "test3";
         byte[] nonce = getUtf8Bytes("nonce");
         byte[] keyMaterial = getUtf8Bytes("keymaterial");
+        byte[] keyMetadata = null;
 
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId,
+                status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias2, wrappedKey);
-        WrappedKey wrappedKey2 = new WrappedKey(nonce, keyMaterial, generationId, status);
-        mRecoverableKeyStoreDb.insertKey(userId, uid, alias3, wrappedKey);
-        WrappedKey wrappedKeyWithDefaultStatus = new WrappedKey(nonce, keyMaterial, generationId);
+        WrappedKey wrappedKey2 = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId,
+                status);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias3, wrappedKey2);
+        WrappedKey wrappedKeyWithDefaultStatus = new WrappedKey(nonce, keyMaterial, keyMetadata,
+                generationId);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKeyWithDefaultStatus);
 
         Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid);
@@ -333,7 +373,10 @@
         String alias = "test";
         byte[] nonce = getUtf8Bytes("nonce");
         byte[] keyMaterial = getUtf8Bytes("keymaterial");
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+        byte[] keyMetadata = null;
+
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId,
+                status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
 
         WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
@@ -357,7 +400,10 @@
         String alias = "test";
         byte[] nonce = getUtf8Bytes("nonce");
         byte[] keyMaterial = getUtf8Bytes("keymaterial");
-        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+        byte[] keyMetadata = null;
+
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, keyMetadata, generationId,
+                status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
 
         WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
diff --git a/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java
deleted file mode 100644
index d7dc58d..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2019 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.pm;
-
-import static org.junit.Assert.assertEquals;
-
-import android.content.pm.PackageParser;
-import android.content.pm.Signature;
-import android.content.pm.SigningInfo;
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@Presubmit
-@RunWith(MockitoJUnitRunner.class)
-public class LauncherAppsServiceTest {
-
-    private static final Signature SIGNATURE_1 = new Signature(new byte[]{0x00, 0x01, 0x02, 0x03});
-    private static final Signature SIGNATURE_2 = new Signature(new byte[]{0x04, 0x05, 0x06, 0x07});
-    private static final Signature SIGNATURE_3 = new Signature(new byte[]{0x08, 0x09, 0x10, 0x11});
-
-    @Test
-    public void testComputePackageCertDigest() {
-        String digest = LauncherAppsService.LauncherAppsImpl.computePackageCertDigest(SIGNATURE_1);
-        assertEquals("A02A05B025B928C039CF1AE7E8EE04E7C190C0DB", digest);
-    }
-
-    @Test
-    public void testGetLatestSignaturesWithSingleCert() {
-        SigningInfo signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
-                        new Signature[]{SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
-                        null,
-                        null));
-        Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
-                signingInfo);
-        assertEquals(1, signatures.length);
-        assertEquals(SIGNATURE_1, signatures[0]);
-    }
-
-    @Test
-    public void testGetLatestSignaturesWithMultiCert() {
-        SigningInfo signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
-                        new Signature[]{SIGNATURE_1, SIGNATURE_2},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
-                        null,
-                        null));
-        Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
-                signingInfo);
-        assertEquals(2, signatures.length);
-        assertEquals(SIGNATURE_1, signatures[0]);
-        assertEquals(SIGNATURE_2, signatures[1]);
-    }
-
-    @Test
-    public void testGetLatestSignaturesWithCertHistory() {
-        SigningInfo signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
-                        new Signature[]{SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
-                        null,
-                        new Signature[]{SIGNATURE_2, SIGNATURE_3}));
-        Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
-                signingInfo);
-        assertEquals(1, signatures.length);
-        assertEquals(SIGNATURE_2, signatures[0]);
-    }
-
-}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 17516bc2..6e8d038 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -87,7 +87,7 @@
     public static final int CMD_NET_STAT_POLL = BASE + 40;
     public static final int EVENT_DATA_RAT_CHANGED = BASE + 41;
     public static final int CMD_CLEAR_PROVISIONING_SPINNER = BASE + 42;
-    public static final int EVENT_REDIRECTION_DETECTED = BASE + 44;
+    public static final int EVENT_NETWORK_STATUS_CHANGED = BASE + 44;
     public static final int EVENT_PCO_DATA_RECEIVED = BASE + 45;
     public static final int EVENT_DATA_ENABLED_CHANGED = BASE + 46;
     public static final int EVENT_DATA_RECONNECT = BASE + 47;
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index f6f35fd..6850673 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -54,6 +54,7 @@
     libnativehelper \
     libpackagelistparser \
     libpcre2 \
+    libprocessgroup \
     libselinux \
     libui \
     libutils \
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index d8f9618..a844cfee 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -535,7 +535,10 @@
 
         IpSecTransformResponse createTransformResp =
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
+
+        Socket socket = new Socket();
+        socket.bind(null);
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
 
         int resourceId = createTransformResp.resourceId;
         mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId);
@@ -552,7 +555,9 @@
 
     @Test
     public void testRemoveTransportModeTransform() throws Exception {
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
+        Socket socket = new Socket();
+        socket.bind(null);
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
         mIpSecService.removeTransportModeTransforms(pfd);
 
         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 724446e..5be7c7b 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -422,7 +422,9 @@
 
     @Test
     public void testRemoveTransportModeTransform() throws Exception {
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
+        Socket socket = new Socket();
+        socket.bind(null);
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
         mIpSecService.removeTransportModeTransforms(pfd);
 
         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
diff --git a/tools/processors/view_inspector/Android.bp b/tools/processors/view_inspector/Android.bp
index ca6b3c4..9b5df56 100644
--- a/tools/processors/view_inspector/Android.bp
+++ b/tools/processors/view_inspector/Android.bp
@@ -5,7 +5,7 @@
     java_resource_dirs: ["src/resources"],
 
     static_libs: [
-    	"javapoet",
+        "javapoet",
     ],
 
     use_tools_jar: true,
@@ -18,9 +18,9 @@
     java_resource_dirs: ["test/resources"],
 
     static_libs: [
-        "guava",
         "junit",
-        "view-inspector-annotation-processor",
+        "guava",
+        "view-inspector-annotation-processor"
     ],
 
     test_suites: ["general-tests"],
diff --git a/tools/processors/view_inspector/TEST_MAPPING b/tools/processors/view_inspector/TEST_MAPPING
index a91b5b4..b4c9cab 100644
--- a/tools/processors/view_inspector/TEST_MAPPING
+++ b/tools/processors/view_inspector/TEST_MAPPING
@@ -2,6 +2,8 @@
   "presubmit": [
     {
       "name": "view-inspector-annotation-processor-test"
+    }, {
+      "name": "CtsViewInspectorAnnotationProcessorTestCases"
     }
   ]
 }
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
index f157949..fc4cd01 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -24,6 +24,7 @@
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.Elements;
 import javax.lang.model.util.Types;
@@ -78,40 +79,126 @@
     }
 
     /**
-     * Extract a string-valued property from an {@link AnnotationMirror}.
+     * Determine if an annotation with the supplied qualified name is present on the element.
      *
-     * @param propertyName The name of the requested property
-     * @param annotationMirror The mirror to search for the property
-     * @return The String value of the annotation or null
+     * @param element The element to check for the presence of an annotation
+     * @param annotationQualifiedName The name of the annotation to check for
+     * @return True if the annotation is present, false otherwise
      */
-    Optional<String> stringProperty(String propertyName, AnnotationMirror annotationMirror) {
-        final AnnotationValue value = valueByName(propertyName, annotationMirror);
-        if (value != null) {
-            return Optional.of((String) value.getValue());
-        } else {
-            return Optional.empty();
+    boolean hasAnnotation(Element element, String annotationQualifiedName) {
+        final TypeElement namedElement = mElementUtils.getTypeElement(annotationQualifiedName);
+
+        if (namedElement != null) {
+            final TypeMirror annotationType = namedElement.asType();
+
+            for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
+                if (mTypeUtils.isSubtype(annotation.getAnnotationType(), annotationType)) {
+                    return true;
+                }
+            }
         }
+
+        return false;
     }
 
+    /**
+     * Get the typed value of an annotation property by name.
+     *
+     * The returned optional will be empty if the value was left at the default, or if the value
+     * of the property is null.
+     *
+     * @param propertyName The name of the property to search for
+     * @param valueClass The expected class of the property value
+     * @param element The element the annotation is on, used for exceptions
+     * @param annotationMirror An annotation mirror to search for the property
+     * @param <T> The type of the value
+     * @return An optional containing the typed value of the named property
+     */
+    <T> Optional<T> typedValueByName(
+            String propertyName,
+            Class<T> valueClass,
+            Element element,
+            AnnotationMirror annotationMirror) {
+        return valueByName(propertyName, annotationMirror).map(annotationValue -> {
+            final Object value = annotationValue.getValue();
+
+            if (value == null) {
+                throw new ProcessingException(
+                        String.format(
+                                "Unexpected null value for annotation property \"%s\".",
+                                propertyName),
+                        element,
+                        annotationMirror,
+                        annotationValue);
+            }
+
+            if (valueClass.isAssignableFrom(value.getClass())) {
+                return valueClass.cast(value);
+            } else {
+                throw new ProcessingException(
+                        String.format(
+                                "Expected annotation property \"%s\" to have type %s, but got %s.",
+                                propertyName,
+                                valueClass.getCanonicalName(),
+                                value.getClass().getCanonicalName()),
+                        element,
+                        annotationMirror,
+                        annotationValue);
+            }
+        });
+    }
+
+    /**
+     * Get the untyped value of an annotation property by name.
+     *
+     * The returned optional will be empty if the value was left at the default, or if the value
+     * of the property is null.
+     *
+     * @param propertyName The name of the property to search for
+     * @param element The element the annotation is on, used for exceptions
+     * @param annotationMirror An annotation mirror to search for the property
+     * @return An optional containing the untyped value of the named property
+     * @see AnnotationValue#getValue()
+     */
+    Optional<Object> untypedValueByName(
+            String propertyName,
+            Element element,
+            AnnotationMirror annotationMirror) {
+        return valueByName(propertyName, annotationMirror).map(annotationValue -> {
+            final Object value = annotationValue.getValue();
+
+            if (value == null) {
+                throw new ProcessingException(
+                        String.format(
+                                "Unexpected null value for annotation property \"%s\".",
+                                propertyName),
+                        element,
+                        annotationMirror,
+                        annotationValue);
+            }
+
+            return value;
+        });
+    }
 
     /**
      * Extract a {@link AnnotationValue} from a mirror by string property name.
      *
      * @param propertyName The name of the property requested property
-     * @param annotationMirror
-     * @return
+     * @param annotationMirror The mirror to search for the property
+     * @return The value of the property
      */
-    AnnotationValue valueByName(String propertyName, AnnotationMirror annotationMirror) {
+    Optional<AnnotationValue> valueByName(String propertyName, AnnotationMirror annotationMirror) {
         final Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap =
                 annotationMirror.getElementValues();
 
         for (ExecutableElement method : valueMap.keySet()) {
             if (method.getSimpleName().contentEquals(propertyName)) {
-                return valueMap.get(method);
+                return Optional.ofNullable(valueMap.get(method));
             }
         }
 
-        return null;
+        // Property not explicitly defined, use default value.
+        return Optional.empty();
     }
-
 }
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
index 579745d..f1ebb87 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -21,6 +21,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 
 /**
@@ -70,7 +71,7 @@
      * @return The property or an empty optional
      */
     public Optional<Property> getProperty(String name) {
-        return Optional.of(mPropertyMap.get(name));
+        return Optional.ofNullable(mPropertyMap.get(name));
     }
 
     /**
@@ -87,13 +88,15 @@
      */
     public static final class Property {
         private final String mName;
-        private String mGetter;
-        private Type mType;
+        private final String mGetter;
+        private final Type mType;
         private boolean mAttributeIdInferrableFromR = true;
         private int mAttributeId = 0;
 
-        public Property(String name) {
-            mName = name;
+        public Property(String name, String getter, Type type) {
+            mName = Objects.requireNonNull(name, "Name must not be null");
+            mGetter = Objects.requireNonNull(getter, "Getter must not be null");
+            mType = Objects.requireNonNull(type, "Type must not be null");
         }
 
         public int getAttributeId() {
@@ -126,18 +129,10 @@
             return mGetter;
         }
 
-        public void setGetter(String getter) {
-            mGetter = getter;
-        }
-
         public Type getType() {
             return mType;
         }
 
-        public void setType(Type type) {
-            mType = type;
-        }
-
         public enum Type {
             /** Primitive or boxed {@code boolean} */
             BOOLEAN,
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
index a186a82..46819b2 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -23,7 +23,7 @@
 import javax.lang.model.element.Element;
 
 /**
- * Process {InspectableNodeName} annotations
+ * Process {@code @InspectableNodeName} annotations.
  *
  * @see android.view.inspector.InspectableNodeName
  */
@@ -58,7 +58,8 @@
         try {
             final AnnotationMirror mirror =
                     mAnnotationUtils.exactlyOneMirror(mQualifiedName, element);
-            final Optional<String> nodeName = mAnnotationUtils.stringProperty("value", mirror);
+            final Optional<String> nodeName = mAnnotationUtils
+                    .typedValueByName("value", String.class, element, mirror);
 
             if (!model.getNodeName().isPresent() || model.getNodeName().equals(nodeName)) {
                 model.setNodeName(nodeName);
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
new file mode 100644
index 0000000..f666be7
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2019 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.processor.view.inspector;
+
+import android.processor.view.inspector.InspectableClassModel.Property;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Process {@code @InspectableProperty} annotations.
+ *
+ * @see android.view.inspector.InspectableProperty
+ */
+public final class InspectablePropertyProcessor implements ModelProcessor {
+    private final String mQualifiedName;
+    private final ProcessingEnvironment mProcessingEnv;
+    private final AnnotationUtils mAnnotationUtils;
+
+    /**
+     * Regex that matches methods names of the form {@code #getValue()}.
+     */
+    private static final Pattern GETTER_GET_PREFIX = Pattern.compile("\\Aget[A-Z]");
+
+    /**
+     * Regex that matches method name of the form {@code #isPredicate()}.
+     */
+    private static final Pattern GETTER_IS_PREFIX = Pattern.compile("\\Ais[A-Z]");
+
+    /**
+     * Set of android and androidx annotation qualified names for colors packed into {@code int}.
+     *
+     * @see android.annotation.ColorInt
+     */
+    private static final String[] COLOR_INT_ANNOTATION_NAMES = {
+            "android.annotation.ColorInt",
+            "androidx.annotation.ColorInt"};
+
+    /**
+     * Set of android and androidx annotation qualified names for colors packed into {@code long}.
+     * @see android.annotation.ColorLong
+     */
+    private static final String[] COLOR_LONG_ANNOTATION_NAMES = {
+            "android.annotation.ColorLong",
+            "androidx.annotation.ColorLong"};
+
+    /**
+     * @param annotationQualifiedName The qualified name of the annotation to process
+     * @param processingEnv The processing environment from the parent processor
+     */
+    public InspectablePropertyProcessor(
+            String annotationQualifiedName,
+            ProcessingEnvironment processingEnv) {
+        mQualifiedName = annotationQualifiedName;
+        mProcessingEnv = processingEnv;
+        mAnnotationUtils = new AnnotationUtils(processingEnv);
+    }
+
+    @Override
+    public void process(Element element, InspectableClassModel model) {
+        try {
+            final AnnotationMirror annotation =
+                    mAnnotationUtils.exactlyOneMirror(mQualifiedName, element);
+            final ExecutableElement getter = ensureGetter(element);
+            final Property property = buildProperty(getter, annotation);
+
+            model.getProperty(property.getName()).ifPresent(p -> {
+                throw new ProcessingException(
+                        String.format(
+                                "Property \"%s\" is already defined on #%s().",
+                                p.getName(),
+                                p.getGetter()),
+                        getter,
+                        annotation);
+            });
+
+            model.putProperty(property);
+        } catch (ProcessingException processingException) {
+            processingException.print(mProcessingEnv.getMessager());
+        }
+    }
+
+    /**
+     * Check that an element is shaped like a getter.
+     *
+     * @param element An element that hopefully represents a getter
+     * @throws ProcessingException if the element isn't a getter
+     * @return An {@link ExecutableElement} that represents a getter method.
+     */
+    private ExecutableElement ensureGetter(Element element) {
+        if (element.getKind() != ElementKind.METHOD) {
+            throw new ProcessingException(
+                    String.format("Expected a method, got a %s", element.getKind()),
+                    element);
+        }
+
+        final ExecutableElement method = (ExecutableElement) element;
+        final Set<Modifier> modifiers = method.getModifiers();
+
+        if (modifiers.contains(Modifier.PRIVATE)) {
+            throw new ProcessingException(
+                    "Property getter methods must not be private.",
+                    element);
+        }
+
+        if (modifiers.contains(Modifier.ABSTRACT)) {
+            throw new ProcessingException(
+                    "Property getter methods must not be abstract.",
+                    element);
+        }
+
+        if (modifiers.contains(Modifier.STATIC)) {
+            throw new ProcessingException(
+                    "Property getter methods must not be static.",
+                    element);
+        }
+
+        if (!method.getParameters().isEmpty()) {
+            throw new ProcessingException(
+                    String.format(
+                            "Expected a getter method to take no parameters, "
+                            + "but got %d parameters.",
+                            method.getParameters().size()),
+                    element);
+        }
+
+        if (method.isVarArgs()) {
+            throw new ProcessingException(
+                    "Expected a getter method to take no arguments, but got a var args method.",
+                    element);
+        }
+
+        if (method.getReturnType() instanceof NoType) {
+            throw new ProcessingException(
+                    "Expected a getter to have a return type, got void.",
+                    element);
+        }
+
+        return method;
+    }
+
+    /**
+     * Build a {@link Property} from a getter and an inspectable property annotation.
+     *
+     * @param getter An element representing the getter to build from
+     * @param annotation A mirror of an inspectable property-shaped annotation
+     * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled
+     * @return A property for the getter and annotation
+     */
+    private Property buildProperty(ExecutableElement getter, AnnotationMirror annotation) {
+        final String name = mAnnotationUtils
+                .typedValueByName("name", String.class, getter, annotation)
+                .orElseGet(() -> inferPropertyNameFromGetter(getter));
+
+        final Property property = new Property(
+                name,
+                getter.getSimpleName().toString(),
+                determinePropertyType(getter, annotation));
+
+        mAnnotationUtils
+                .typedValueByName("hasAttributeId", Boolean.class, getter, annotation)
+                .ifPresent(property::setAttributeIdInferrableFromR);
+
+        mAnnotationUtils
+                .typedValueByName("attributeId", Integer.class, getter, annotation)
+                .ifPresent(property::setAttributeId);
+
+        return property;
+    }
+
+    /**
+     * Determine the property type from the annotation, return type, or context clues.
+     *
+     * @param getter An element representing the getter to build from
+     * @param annotation A mirror of an inspectable property-shaped annotation
+     * @return The resolved property type
+     * @throws ProcessingException If the property type cannot be resolved
+     * @see android.view.inspector.InspectableProperty#valueType()
+     */
+    private Property.Type determinePropertyType(
+            ExecutableElement getter,
+            AnnotationMirror annotation) {
+
+        final String valueType = mAnnotationUtils
+                .untypedValueByName("valueType", getter, annotation)
+                .map(Object::toString)
+                .orElse("INFERRED");
+
+        final Property.Type returnType = convertReturnTypeToPropertyType(getter);
+
+        switch (valueType) {
+            case "INFERRED":
+                if (hasColorAnnotation(getter)) {
+                    return Property.Type.COLOR;
+                } else {
+                    return returnType;
+                }
+            case "NONE":
+                return returnType;
+            case "COLOR":
+                switch (returnType) {
+                    case COLOR:
+                    case INT:
+                    case LONG:
+                        return Property.Type.COLOR;
+                    default:
+                        throw new ProcessingException(
+                                "Color must be a long, integer, or android.graphics.Color",
+                                getter,
+                                annotation);
+                }
+            case "GRAVITY":
+                if (returnType == Property.Type.INT) {
+                    return Property.Type.GRAVITY;
+                } else {
+                    throw new ProcessingException(
+                            String.format("Gravity must be an integer, got %s", returnType),
+                            getter,
+                            annotation);
+                }
+            case "INT_ENUM":
+            case "INT_FLAG":
+                throw new ProcessingException("Not implemented", getter, annotation);
+            default:
+                throw new ProcessingException(
+                        String.format("Unknown value type enumeration value: %s", valueType),
+                        getter,
+                        annotation);
+        }
+    }
+
+    /**
+     * Get a property type from the return type of a getter.
+     *
+     * @param getter The getter to extract the return type of
+     * @throws ProcessingException If the return type is not a primitive or an object
+     * @return The property type returned by the getter
+     */
+    private Property.Type convertReturnTypeToPropertyType(ExecutableElement getter) {
+        final TypeMirror returnType = getter.getReturnType();
+
+        switch (unboxType(returnType)) {
+            case BOOLEAN:
+                return Property.Type.BOOLEAN;
+            case BYTE:
+                return Property.Type.BYTE;
+            case CHAR:
+                return Property.Type.CHAR;
+            case DOUBLE:
+                return Property.Type.DOUBLE;
+            case FLOAT:
+                return Property.Type.FLOAT;
+            case INT:
+                return Property.Type.INT;
+            case LONG:
+                return Property.Type.LONG;
+            case SHORT:
+                return Property.Type.SHORT;
+            case DECLARED:
+                if (isColorType(returnType)) {
+                    return Property.Type.COLOR;
+                } else {
+                    return Property.Type.OBJECT;
+                }
+            default:
+                throw new ProcessingException(
+                        String.format("Unsupported return type %s.", returnType),
+                        getter);
+        }
+    }
+
+    /**
+     * Determine if a getter is annotated with color annotation matching its return type.
+     *
+     * Note that an {@code int} return value annotated with {@link android.annotation.ColorLong} is
+     * not considered to be annotated, nor is a {@code long} annotated with
+     * {@link android.annotation.ColorInt}.
+     *
+     * @param getter The getter to query
+     * @return True if the getter has a color annotation, false otherwise
+     *
+     */
+    private boolean hasColorAnnotation(ExecutableElement getter) {
+        switch (unboxType(getter.getReturnType())) {
+            case INT:
+                for (String name : COLOR_INT_ANNOTATION_NAMES) {
+                    if (mAnnotationUtils.hasAnnotation(getter, name)) {
+                        return true;
+                    }
+                }
+                return false;
+            case LONG:
+                for (String name : COLOR_LONG_ANNOTATION_NAMES) {
+                    if (mAnnotationUtils.hasAnnotation(getter, name)) {
+                        return true;
+                    }
+                }
+                return false;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Infer a property name from a getter method.
+     *
+     * If the method is prefixed with {@code get}, the prefix will be stripped, and the
+     * capitalization fixed. E.g.: {@code getSomeProperty} to {@code someProperty}.
+     *
+     * Additionally, if the method's return type is a boolean, an {@code is} prefix will also be
+     * stripped. E.g.: {@code isPropertyEnabled} to {@code propertyEnabled}.
+     *
+     * Failing that, this method will just return the full name of the getter.
+     *
+     * @param getter An element representing a getter
+     * @return A string property name
+     */
+    private String inferPropertyNameFromGetter(ExecutableElement getter) {
+        final String name = getter.getSimpleName().toString();
+
+        if (GETTER_GET_PREFIX.matcher(name).find()) {
+            return name.substring(3, 4).toLowerCase() + name.substring(4);
+        } else if (isBoolean(getter.getReturnType()) && GETTER_IS_PREFIX.matcher(name).find()) {
+            return name.substring(2, 3).toLowerCase() + name.substring(3);
+        } else {
+            return name;
+        }
+    }
+
+    /**
+     * Determine if a {@link TypeMirror} is a boxed or unboxed boolean.
+     *
+     * @param type The type mirror to check
+     * @return True if the type is a boolean
+     */
+    private boolean isBoolean(TypeMirror type) {
+        if (type.getKind() == TypeKind.DECLARED) {
+            return mProcessingEnv.getTypeUtils().unboxedType(type).getKind() == TypeKind.BOOLEAN;
+        } else {
+            return type.getKind() == TypeKind.BOOLEAN;
+        }
+    }
+
+    /**
+     * Unbox a type mirror if it represents a boxed type, otherwise pass it through.
+     *
+     * @param typeMirror The type mirror to unbox
+     * @return The same type mirror, or an unboxed primitive version
+     */
+    private TypeKind unboxType(TypeMirror typeMirror) {
+        final TypeKind typeKind = typeMirror.getKind();
+
+        if (typeKind.isPrimitive()) {
+            return typeKind;
+        } else if (typeKind == TypeKind.DECLARED) {
+            try {
+                return mProcessingEnv.getTypeUtils().unboxedType(typeMirror).getKind();
+            } catch (IllegalArgumentException e) {
+                return typeKind;
+            }
+        } else {
+            return typeKind;
+        }
+    }
+
+    /**
+     * Determine if a type mirror represents a subtype of {@link android.graphics.Color}.
+     *
+     * @param typeMirror The type mirror to test
+     * @return True if it represents a subclass of color, false otherwise
+     */
+    private boolean isColorType(TypeMirror typeMirror) {
+        final TypeElement colorType = mProcessingEnv
+                .getElementUtils()
+                .getTypeElement("android.graphics.Color");
+
+        if (colorType == null) {
+            return false;
+        } else {
+            return mProcessingEnv.getTypeUtils().isSubtype(typeMirror, colorType.asType());
+        }
+    }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
index 8b2cc84..dd4d8f5 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -51,12 +51,6 @@
     private static final ClassName R_CLASS_NAME = ClassName.get("android", "R");
 
     /**
-     * The class name of {@link android.content.res.ResourceId}.
-     */
-    private static final ClassName RESOURCE_ID_CLASS_NAME = ClassName.get(
-            "android.content.res", "ResourceId");
-
-    /**
      * The class name of {@link android.view.inspector.InspectionCompanion}.
      */
     private static final ClassName INSPECTION_COMPANION = ClassName.get(
@@ -91,11 +85,11 @@
     private static final String GENERATED_CLASS_SUFFIX = "$$InspectionCompanion";
 
     /**
-     * The null resource ID.
+     * The null resource ID, copied to avoid a host dependency on platform code.
      *
      * @see android.content.res.Resources#ID_NULL
      */
-    private static final int NO_ID = 0;
+    private static final int ID_NULL = 0;
 
     /**
      * @param filer A filer to write the generated source to
@@ -289,8 +283,8 @@
         if (property.isAttributeIdInferrableFromR()) {
             builder.add("$T.attr.$L", R_CLASS_NAME, property.getName());
         } else {
-            if (property.getAttributeId() == NO_ID) {
-                builder.add("$T.ID_NULL", RESOURCE_ID_CLASS_NAME);
+            if (property.getAttributeId() == ID_NULL) {
+                builder.add("$L", ID_NULL);
             } else {
                 builder.add("$L", String.format("0x%08x", property.getAttributeId()));
             }
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
index 3ffcff8..6f52260 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
index e531b67..455f5b0 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -46,11 +46,14 @@
  * @see android.view.inspector.InspectableProperty
  */
 @SupportedAnnotationTypes({
-        PlatformInspectableProcessor.NODE_NAME_QUALIFIED_NAME
+        PlatformInspectableProcessor.NODE_NAME_QUALIFIED_NAME,
+        PlatformInspectableProcessor.PROPERTY_QUALIFIED_NAME
 })
 public final class PlatformInspectableProcessor extends AbstractProcessor {
     static final String NODE_NAME_QUALIFIED_NAME =
             "android.view.inspector.InspectableNodeName";
+    static final String PROPERTY_QUALIFIED_NAME =
+            "android.view.inspector.InspectableProperty";
 
     @Override
     public SourceVersion getSupportedSourceVersion() {
@@ -68,6 +71,11 @@
                         new InspectableNodeNameProcessor(NODE_NAME_QUALIFIED_NAME, processingEnv),
                         modelMap);
 
+            } else if (annotation.getQualifiedName().contentEquals(PROPERTY_QUALIFIED_NAME)) {
+                runModelProcessor(
+                        roundEnv.getElementsAnnotatedWith(annotation),
+                        new InspectablePropertyProcessor(PROPERTY_QUALIFIED_NAME, processingEnv),
+                        modelMap);
 
             } else {
                 fail("Unexpected annotation type", annotation);
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
index 6360e0a..b4c6466 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
diff --git a/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
index fa4f71f..79185cc 100644
--- a/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
@@ -1 +1 @@
-android.processor.inspector.view.PlatformInspectableProcessor
+android.processor.view.inspector.PlatformInspectableProcessor
diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
index f639719..b0775dc 100644
--- a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
+++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -20,7 +20,7 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
-import static junit.framework.TestCase.fail;
+import static junit.framework.Assert.fail;
 
 import com.google.common.base.Charsets;
 import com.google.common.io.Resources;
@@ -65,27 +65,28 @@
 
     @Test
     public void testSimpleProperties() {
-        addProperty("boolean", Property.Type.BOOLEAN, "getBoolean");
-        addProperty("byte", Property.Type.BYTE, "getByte");
-        addProperty("char", Property.Type.CHAR, "getChar");
-        addProperty("double", Property.Type.DOUBLE, "getDouble");
-        addProperty("float", Property.Type.FLOAT, "getFloat");
-        addProperty("int", Property.Type.INT, "getInt");
-        addProperty("long", Property.Type.LONG, "getLong");
-        addProperty("short", Property.Type.SHORT, "getShort");
+        addProperty("boolean", "getBoolean", Property.Type.BOOLEAN);
+        addProperty("byte", "getByte", Property.Type.BYTE);
+        addProperty("char", "getChar", Property.Type.CHAR);
+        addProperty("double", "getDouble", Property.Type.DOUBLE);
+        addProperty("float", "getFloat", Property.Type.FLOAT);
+        addProperty("int", "getInt", Property.Type.INT);
+        addProperty("long", "getLong", Property.Type.LONG);
+        addProperty("short", "getShort", Property.Type.SHORT);
 
-        addProperty("object", Property.Type.OBJECT, "getObject");
-        addProperty("color", Property.Type.COLOR, "getColor");
-        addProperty("gravity", Property.Type.GRAVITY, "getGravity");
+        addProperty("object", "getObject", Property.Type.OBJECT);
+        addProperty("color", "getColor", Property.Type.COLOR);
+        addProperty("gravity", "getGravity", Property.Type.GRAVITY);
 
         assertGeneratedFileEquals("SimpleProperties");
     }
 
     @Test
     public void testNoAttributeId() {
-        final Property property = new Property("noAttributeProperty");
-        property.setType(Property.Type.INT);
-        property.setGetter("getNoAttributeProperty");
+        final Property property = new Property(
+                "noAttributeProperty",
+                "getNoAttributeProperty",
+                Property.Type.INT);
         property.setAttributeIdInferrableFromR(false);
         mModel.putProperty(property);
 
@@ -94,19 +95,18 @@
 
     @Test
     public void testSuppliedAttributeId() {
-        final Property property = new Property("suppliedAttributeProperty");
-        property.setType(Property.Type.INT);
-        property.setGetter("getSuppliedAttributeProperty");
+        final Property property = new Property(
+                "suppliedAttributeProperty",
+                "getSuppliedAttributeProperty",
+                Property.Type.INT);
         property.setAttributeId(0xdecafbad);
         mModel.putProperty(property);
 
         assertGeneratedFileEquals("SuppliedAttributeId");
     }
 
-    private Property addProperty(String name, Property.Type type, String getter) {
-        final Property property = new Property(name);
-        property.setType(type);
-        property.setGetter(getter);
+    private Property addProperty(String name, String getter, Property.Type type) {
+        final Property property = new Property(name, getter, type);
         mModel.putProperty(property);
         return property;
     }
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
index 277e840..23d0f78 100644
--- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
@@ -1,6 +1,5 @@
 package com.android.inspectable;
 
-import android.content.res.ResourceId;
 import android.view.inspector.InspectionCompanion;
 import android.view.inspector.PropertyMapper;
 import android.view.inspector.PropertyReader;
@@ -25,7 +24,7 @@
 
     @Override
     public void mapProperties(PropertyMapper propertyMapper) {
-        mNoAttributePropertyId = propertyMapper.mapInt("noAttributeProperty", ResourceId.ID_NULL);
+        mNoAttributePropertyId = propertyMapper.mapInt("noAttributeProperty", 0);
         mPropertiesMapped = true;
     }
 
diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
index 3b4a6cd..b8c82fd 100644
--- a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
+++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
@@ -101,7 +101,7 @@
     /**
      * Easy Connect Failure event: General protocol failure.
      */
-    public static final int EASY_CONNECT_EVENT_FAILURE = -7;
+    public static final int EASY_CONNECT_EVENT_FAILURE_GENERIC = -7;
 
     /**
      * Easy Connect Failure event: Feature or option is not supported.
@@ -123,7 +123,7 @@
             EASY_CONNECT_EVENT_FAILURE_CONFIGURATION,
             EASY_CONNECT_EVENT_FAILURE_BUSY,
             EASY_CONNECT_EVENT_FAILURE_TIMEOUT,
-            EASY_CONNECT_EVENT_FAILURE,
+            EASY_CONNECT_EVENT_FAILURE_GENERIC,
             EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED,
             EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK,
     })
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 07f7cb3..46c4191 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -46,7 +46,7 @@
  */
 interface IWifiManager
 {
-    int getSupportedFeatures();
+    long getSupportedFeatures();
 
     WifiActivityEnergyInfo reportActivityInfo();
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7cc89ac3..5742bd5 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2083,7 +2083,7 @@
     /** @hide */
     public static final int WIFI_FEATURE_DPP              = 0x80000000; // DPP (Easy-Connect)
 
-    private int getSupportedFeatures() {
+    private long getSupportedFeatures() {
         try {
             return mService.getSupportedFeatures();
         } catch (RemoteException e) {
@@ -2091,7 +2091,7 @@
         }
     }
 
-    private boolean isFeatureSupported(int feature) {
+    private boolean isFeatureSupported(long feature) {
         return (getSupportedFeatures() & feature) == feature;
     }
     /**
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 2c96c05..b3ac9f1 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -64,7 +64,7 @@
     private static final String TAG = BaseWifiService.class.getSimpleName();
 
     @Override
-    public int getSupportedFeatures() {
+    public long getSupportedFeatures() {
         throw new UnsupportedOperationException();
     }