Merge "Add tests for AtomMetricStats."
diff --git a/api/current.txt b/api/current.txt
index c0d10a6..b793470b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -48997,6 +48997,7 @@
     method public boolean[] hasKeys(int...);
     method public boolean hasMicrophone();
     method public boolean isEnabled();
+    method public boolean isExternal();
     method public boolean isVirtual();
     method public boolean supportsSource(int);
     method public void writeToParcel(android.os.Parcel, int);
@@ -52612,7 +52613,6 @@
   public abstract class Animation implements java.lang.Cloneable {
     ctor public Animation();
     ctor public Animation(android.content.Context, android.util.AttributeSet);
-    method public void addAnimationListener(android.view.animation.Animation.AnimationListener);
     method protected void applyTransformation(float, android.view.animation.Transformation);
     method public void cancel();
     method protected android.view.animation.Animation clone() throws java.lang.CloneNotSupportedException;
@@ -52637,7 +52637,6 @@
     method public void initialize(int, int, int, int);
     method public boolean isFillEnabled();
     method public boolean isInitialized();
-    method public void removeAnimationListener(android.view.animation.Animation.AnimationListener);
     method public void reset();
     method protected float resolveSize(int, float, int, int);
     method public void restrictDuration(long);
@@ -56375,6 +56374,7 @@
     ctor public ProgressBar(android.content.Context, android.util.AttributeSet);
     ctor public ProgressBar(android.content.Context, android.util.AttributeSet, int);
     ctor public ProgressBar(android.content.Context, android.util.AttributeSet, int, int);
+    method @Nullable public android.graphics.drawable.Drawable getCurrentDrawable();
     method public android.graphics.drawable.Drawable getIndeterminateDrawable();
     method @Nullable public android.content.res.ColorStateList getIndeterminateTintList();
     method @Nullable public android.graphics.PorterDuff.Mode getIndeterminateTintMode();
@@ -61199,9 +61199,9 @@
     method public String getName();
     method public int getReferenceKind();
     method public default boolean isVarArgs();
-    method public static boolean refKindIsField(int);
-    method public static boolean refKindIsValid(int);
-    method public static String refKindName(int);
+    method @Deprecated public static boolean refKindIsField(int);
+    method @Deprecated public static boolean refKindIsValid(int);
+    method @Deprecated public static String refKindName(int);
     method public static String referenceKindToString(int);
     method public <T extends java.lang.reflect.Member> T reflectAs(Class<T>, java.lang.invoke.MethodHandles.Lookup);
     method public static String toString(int, Class<?>, String, java.lang.invoke.MethodType);
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 7295259..868a9de 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
+import android.os.Build;
 import android.os.NullVibrator;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -54,7 +55,7 @@
     private final int mProductId;
     private final String mDescriptor;
     private final InputDeviceIdentifier mIdentifier;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private final boolean mIsExternal;
     private final int mSources;
     private final int mKeyboardType;
@@ -608,10 +609,7 @@
      * peripheral bus), otherwise it is built-in.
      *
      * @return True if the device is external.
-     *
-     * @hide
      */
-    @UnsupportedAppUsage
     public boolean isExternal() {
         return mIsExternal;
     }
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index dfd9a2e..e095094 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.RectF;
+import android.os.Build;
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.util.AttributeSet;
@@ -30,9 +31,6 @@
 
 import dalvik.system.CloseGuard;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Abstraction for an Animation that can be applied to Views, Surfaces, or
  * other objects. See the {@link android.view.animation animation package
@@ -187,15 +185,12 @@
     /**
      * An animation listener to be notified when the animation starts, ends or repeats.
      */
-    @UnsupportedAppUsage
+    // If you need to chain the AnimationListener, wrap the existing Animation into an AnimationSet
+    // and add your new listener to that set
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117519981)
     private AnimationListener mListener;
 
     /**
-     * A list of animation listeners to be notified when the animation starts, ends or repeats.
-     */
-    private List<AnimationListener> mListeners;
-
-    /**
      * Desired Z order mode during animation.
      */
     private int mZAdjustment;
@@ -833,7 +828,7 @@
     }
 
     private boolean hasAnimationListener() {
-        return mListener != null || (mListeners != null && !mListeners.isEmpty());
+        return mListener != null;
     }
 
     /**
@@ -848,32 +843,6 @@
     }
 
     /**
-     * <p>Adds an animation listener to this animation. The animation listener
-     * is notified of animation events such as the end of the animation or the
-     * repetition of the animation.</p>
-     *
-     * @param listener the animation listener to be notified
-     */
-    public void addAnimationListener(AnimationListener listener) {
-        if (mListeners == null) {
-            mListeners = new ArrayList<>(1);
-        }
-        mListeners.add(listener);
-    }
-
-    /**
-     * <p>Removes an animation listener that has been added with
-     * {@link #addAnimationListener(AnimationListener)}.</p>
-     *
-     * @param listener the animation listener to be removed
-     */
-    public void removeAnimationListener(AnimationListener listener) {
-        if (mListeners != null) {
-            mListeners.remove(listener);
-        }
-    }
-
-    /**
      * Gurantees that this animation has an interpolator. Will use
      * a AccelerateDecelerateInterpolator is nothing else was specified.
      */
@@ -1003,33 +972,18 @@
         if (mListener != null) {
             mListener.onAnimationStart(this);
         }
-        if (mListeners != null && !mListeners.isEmpty()) {
-            for (AnimationListener listener : mListeners) {
-                listener.onAnimationStart(this);
-            }
-        }
     }
 
     void dispatchAnimationRepeat() {
         if (mListener != null) {
             mListener.onAnimationRepeat(this);
         }
-        if (mListeners != null && !mListeners.isEmpty()) {
-            for (AnimationListener listener : mListeners) {
-                listener.onAnimationRepeat(this);
-            }
-        }
     }
 
     void dispatchAnimationEnd() {
         if (mListener != null) {
             mListener.onAnimationEnd(this);
         }
-        if (mListeners != null && !mListeners.isEmpty()) {
-            for (AnimationListener listener : mListeners) {
-                listener.onAnimationEnd(this);
-            }
-        }
     }
 
     /**
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 6b48c65..30df5b5 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -211,7 +211,12 @@
 
     private Drawable mIndeterminateDrawable;
     private Drawable mProgressDrawable;
-    @UnsupportedAppUsage
+    /**
+     * Outside the framework, instead of accessing this directly, please use
+     * {@link #getCurrentDrawable()}, {@link #setProgressDrawable(Drawable)},
+     * {@link #setIndeterminateDrawable(Drawable)} and their tiled versions.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private Drawable mCurrentDrawable;
     private ProgressTintInfo mProgressTintInfo;
 
@@ -1298,9 +1303,14 @@
     }
 
     /**
-     * @return The drawable currently used to draw the progress bar
+     * Returns the drawable currently used to draw the progress bar. This will be
+     * either {@link #getProgressDrawable()} or {@link #getIndeterminateDrawable()}
+     * depending on whether the progress bar is in determinate or indeterminate mode.
+     *
+     * @return the drawable currently used to draw the progress bar
      */
-    Drawable getCurrentDrawable() {
+    @Nullable
+    public Drawable getCurrentDrawable() {
         return mCurrentDrawable;
     }
 
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 7f462cb..106b909 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -610,8 +610,16 @@
                 resolver.registerContentObserver(uri, true,
                         mFormatChangeObserver, UserHandle.USER_ALL);
             } else {
+                // UserHandle.myUserId() is needed. This class is supported by the
+                // remote views mechanism and as a part of that the remote views
+                // can be inflated by a context for another user without the app
+                // having interact users permission - just for loading resources.
+                // For example, when adding widgets from a managed profile to the
+                // home screen. Therefore, we register the ContentObserver with the user
+                // the app is running (e.g. the launcher) and not the user of the
+                // context (e.g. the widget's profile).
                 resolver.registerContentObserver(uri, true,
-                        mFormatChangeObserver);
+                        mFormatChangeObserver, UserHandle.myUserId());
             }
         }
     }
diff --git a/core/jni/runtime_native_boot-flags-test.sh b/core/jni/runtime_native_boot-flags-test.sh
new file mode 100755
index 0000000..66e18bb
--- /dev/null
+++ b/core/jni/runtime_native_boot-flags-test.sh
@@ -0,0 +1,244 @@
+#!/bin/bash
+
+# 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.
+#
+
+# Test Android Runtime (Boot) device configuration flags (living in namespace
+# `runtime_native_boot`).
+
+me=$(basename $0)
+
+# Namespace containing the tested flag.
+namespace=runtime_native_boot
+# Default set of checked zygote processes.
+zygotes=
+
+# Status of whole test script.
+exit_status=0
+
+function say {
+  echo "$me: $*"
+}
+
+function banner {
+  local separator=$(echo "$@" | sed s/./=/g )
+  say "$separator"
+  say "$@"
+  say "$separator"
+}
+
+function fail {
+  say "FAILED: $@"
+  exit_status=1
+}
+
+function reboot_and_wait_for_device {
+  say "Rebooting device..."
+  adb reboot
+  adb wait-for-device >/dev/null
+  # Wait until the device has finished booting. Give the device 60 iterations
+  # (~60 seconds) to try and finish booting before declaring defeat.
+  local niters=60
+  for i in $(seq $niters); do
+    [[ $(adb shell getprop sys.boot_completed) -eq 1 ]] && return 0
+    sleep 1
+  done
+  fail "Device did not finish booting before timeout (~$niters seconds)"
+}
+
+# check_device_config_flag CONTEXT FLAG VALUE
+# -------------------------------------------
+# Check that the device configuration flag FLAG is set to VALUE. Use CONTEXT in
+# logging.
+function check_device_config_flag {
+  local context=$1
+  local flag=$2
+  local value=$3
+
+  say "[$context] Check that the device configuration flag is set..."
+  local flag_value=$(adb shell device_config get "$namespace" "$flag")
+  [[ "$flag_value" = "$value" ]] \
+    || fail "Device configuration flag \`$flag\` set to \`$flag_value\` (expected \`$value\`)"
+}
+
+# check_no_device_config_flag CONTEXT FLAG
+# ----------------------------------------
+# Check that the device configuration flag FLAG is not set. Use CONTEXT in
+# logging.
+function check_no_device_config_flag {
+  local context=$1
+  local flag=$2
+
+  say "[$context] Check that the device configuration flag is not set..."
+  local flag_value=$(adb shell device_config get "$namespace" "$flag")
+  [[ "$flag_value" = null ]] \
+    || fail "Device configuration flag \`$flag\` set to \`$flag_value\` (expected `null`)"
+}
+
+# get_system_property PROP
+# ------------------------
+# Get system property PROP associated with a device configuration flag.
+function get_system_property {
+  local prop=$1
+
+  # Note that we need to be root to read that system property.
+  adb root >/dev/null
+  local prop_value=$(adb shell getprop "$prop")
+  adb unroot >/dev/null
+  echo "$prop_value"
+}
+
+# check_system_property CONTEXT PROP VALUE
+# ----------------------------------------
+# Check that the system property PROP associated with a device configuration
+# flag is set to VALUE. Use CONTEXT in logging.
+function check_system_property {
+  local context=$1
+  local prop=$2
+  local value=$3
+
+  say "[$context] Check that the persistent system property is set..."
+  local prop_value=$(get_system_property "$prop")
+  [[ "$prop_value" = "$value" ]] \
+    || fail "System property \`$prop\` set to \`$prop_value\` (expected \`$value\`)"
+}
+
+# check_no_system_property CONTEXT PROP
+# -------------------------------------
+# Check that the system property PROP associated with a device configuration
+# flag is not set. Use CONTEXT in logging.
+function check_no_system_property {
+  local context=$1
+  local prop=$2
+
+  say "[$context] Check that the persistent system property is not set..."
+  local prop_value=$(get_system_property "$prop")
+  [[ -z "$prop_value" ]] \
+    || fail "System property \`$prop\` set to \`$prop_value\` (expected unset property)"
+}
+
+# find_zygote_runtime_option ZYGOTE RUNTIME_OPTION
+# ------------------------------------------------
+# Return whether ZYGOTE is passed RUNTIME_OPTION.
+function find_zygote_runtime_option {
+  local zygote=$1
+  local runtime_option=$2
+
+  adb logcat -d -s "$zygote" | grep -q -e "option\[[0-9]\+\]=$runtime_option"
+}
+
+# check_zygote_gc_runtime_option CONTEXT VALUE
+# --------------------------------------------
+# Check that all zygote processes are passed device configuration flag VALUE as
+# GC runtime option. Use CONTEXT in logging.
+function check_zygote_gc_runtime_option {
+  local context=$1
+  local value=$2
+
+  say \
+    "[$context] Check that all zygote processes are passed the flag value as a GC runtime option..."
+  local runtime_option="-Xgc:$value"
+  for zygote in $zygotes; do
+    find_zygote_runtime_option "$zygote" "$runtime_option"\
+      || fail "Found no \`$runtime_option\` among runtime options passed to \`$zygote\`"
+  done
+}
+
+# check_no_zygote_gc_runtime_option CONTEXT VALUE
+# -----------------------------------------------
+# Check that no zygote process is passed device configuration flag VALUE as GC
+# runtime option.  Use CONTEXT in logging.
+function check_no_zygote_gc_runtime_option {
+  local context=$1
+  local value=$2
+
+  say "[$context] Check no zygote process is passed the flag value as a GC runtime option..."
+  local runtime_option="-Xgc:$value"
+  for zygote in $zygotes; do
+    find_zygote_runtime_option "$zygote" "$runtime_option"\
+      && fail "Found \`$runtime_option\` among runtime options passed to \`$zygote\`"
+  done
+}
+
+# test_android_runtime_flag FLAG VALUE
+# ------------------------------------
+# Test device configuration FLAG with VALUE.
+function test_android_runtime_flag {
+  local flag=$1
+  local value=$2
+
+  # Persistent system property (set after a reboot) associated with the device
+  # configuration flag.
+  local prop="persist.device_config.$namespace.$flag"
+
+  banner "Testing \`$flag\` value \`$value\`."
+
+  say "Setting device configuration flag..."
+  adb shell device_config put "$namespace" "$flag" "$value"
+  # Give some time to the device to digest this change before rebooting.
+  sleep 3
+
+  # Check that both the device configuration flag and the associated system
+  # property are set, but that the zygote hasn't had the flag passed to it as a
+  # GC runtime option (as we haven't rebooted yet).
+  local context="Flag set, before reboot"
+  check_device_config_flag "$context" "$flag" "$value"
+  check_system_property "$context" "$prop" "$value"
+  check_no_zygote_gc_runtime_option "$context" "$value"
+
+  # Reboot device for the flag value to take effect.
+  reboot_and_wait_for_device
+  context="Flag set, after 1st reboot"
+  check_device_config_flag "$context" "$flag" "$value"
+  check_system_property "$context" "$prop" "$value"
+  check_zygote_gc_runtime_option "$context" "$value"
+
+  # Reboot device a second time and check that the state has persisted.
+  reboot_and_wait_for_device
+  context="Flag set, after 2nd reboot"
+  check_device_config_flag "$context" "$flag" "$value"
+  check_system_property "$context" "$prop" "$value"
+  check_zygote_gc_runtime_option "$context" "$value"
+
+  say "Unsetting device configuration flag..."
+  adb shell device_config delete "$namespace" "$flag" >/dev/null
+  # Give some time to the device to digest this change before rebooting.
+  sleep 3
+
+  # Reboot and check that the device is back to its default state.
+  reboot_and_wait_for_device
+  context="Flag unset, after 3rd reboot"
+  check_no_device_config_flag "$context" "$flag"
+  check_no_system_property "$context" "$prop"
+  check_no_zygote_gc_runtime_option "$context" "$value"
+}
+
+# Enumerate Zygote processes.
+case $(adb shell getprop ro.zygote) in
+  (zygote32) zygotes="zygote";;
+  (zygote64) zygotes="zygote64";;
+  (zygote32_64|zygote64_32) zygotes="zygote zygote64";;
+esac
+
+# Test "gctype" flag values.
+test_android_runtime_flag gctype nogenerational_cc
+test_android_runtime_flag gctype generational_cc
+
+if [[ "$exit_status" -eq 0 ]]; then
+  banner "All tests passed."
+else
+  banner "Test(s) failed."
+fi
+exit $exit_status
diff --git a/core/res/res/drawable/ic_action_open.xml b/core/res/res/drawable/ic_action_open.xml
new file mode 100644
index 0000000..3d3d36e
--- /dev/null
+++ b/core/res/res/drawable/ic_action_open.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+  <path
+      android:fillColor="#FF737373"
+      android:pathData="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 410a8c16..8e251fd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3643,4 +3643,6 @@
   <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" />
   <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" />
   <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
+
+  <java-symbol type="drawable" name="ic_action_open" />
 </resources>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index b6b229c..c5e598d 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -230,6 +230,10 @@
         NotificationEntry entry =
                 new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
         SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry);
+        if (DEBUG) {
+            Log.d(TAG, String.format("Creating Adjustment for %s, with %d actions, and %d replies.",
+                    sbn.getKey(), suggestions.actions.size(), suggestions.replies.size()));
+        }
         return createEnqueuedNotificationAdjustment(
                 entry, suggestions.actions, suggestions.replies);
     }
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 08cc39f..f372fe5 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -21,6 +21,7 @@
 import android.app.Person;
 import android.app.RemoteAction;
 import android.content.Context;
+import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.Process;
@@ -42,6 +43,7 @@
 import java.util.Collections;
 import java.util.Deque;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 public class SmartActionsHelper {
@@ -120,6 +122,12 @@
         if (messages.isEmpty()) {
             return Collections.emptyList();
         }
+        // Do not generate smart actions if the last message is from the local user.
+        ConversationActions.Message lastMessage = messages.get(messages.size() - 1);
+        if (arePersonsEqual(
+                ConversationActions.Message.PERSON_USER_SELF, lastMessage.getAuthor())) {
+            return Collections.emptyList();
+        }
 
         TextClassifier.EntityConfig.Builder typeConfigBuilder =
                 new TextClassifier.EntityConfig.Builder();
@@ -230,8 +238,11 @@
 
     private Notification.Action createNotificationAction(
             RemoteAction remoteAction, String actionType) {
+        Icon icon = remoteAction.shouldShowIcon()
+                ? remoteAction.getIcon()
+                : Icon.createWithResource(mContext, com.android.internal.R.drawable.ic_action_open);
         return new Notification.Action.Builder(
-                remoteAction.getIcon(),
+                icon,
                 remoteAction.getTitle(),
                 remoteAction.getActionIntent())
                 .setContextual(true)
@@ -312,13 +323,12 @@
             if (message == null) {
                 continue;
             }
+            // As per the javadoc of Notification.addMessage, null means local user.
             Person senderPerson = message.getSenderPerson();
-            // Skip encoding once the sender is missing as it is important to distinguish
-            // local user and remote user when generating replies.
             if (senderPerson == null) {
-                break;
+                senderPerson = localUser;
             }
-            Person author = localUser != null && localUser.equals(senderPerson)
+            Person author = localUser != null && arePersonsEqual(localUser, senderPerson)
                     ? ConversationActions.Message.PERSON_USER_SELF : senderPerson;
             extractMessages.push(new ConversationActions.Message.Builder(author)
                     .setText(message.getText())
@@ -333,6 +343,12 @@
         return new ArrayList<>(extractMessages);
     }
 
+    private static boolean arePersonsEqual(@NonNull Person left, @NonNull Person right) {
+        return Objects.equals(left.getKey(), right.getKey())
+                && Objects.equals(left.getName(), right.getName())
+                && Objects.equals(left.getUri(), right.getUri());
+    }
+
     static class SmartSuggestions {
         public final ArrayList<CharSequence> replies;
         public final ArrayList<Notification.Action> actions;
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
index ebbd961..74c20fc 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
@@ -222,22 +222,29 @@
 
         List<ConversationActions.Message> messages =
                 runSuggestAndCaptureRequest().getConversation();
-        assertThat(messages).hasSize(3);
+        assertThat(messages).hasSize(4);
 
-        ConversationActions.Message secondMessage = messages.get(0);
+        ConversationActions.Message firstMessage = messages.get(0);
+        MessageSubject.assertThat(firstMessage).hasText("firstMessage");
+        MessageSubject.assertThat(firstMessage)
+                .hasPerson(ConversationActions.Message.PERSON_USER_SELF);
+        MessageSubject.assertThat(firstMessage)
+                .hasReferenceTime(createZonedDateTimeFromMsUtc(1000));
+
+        ConversationActions.Message secondMessage = messages.get(1);
         MessageSubject.assertThat(secondMessage).hasText("secondMessage");
         MessageSubject.assertThat(secondMessage)
                 .hasPerson(ConversationActions.Message.PERSON_USER_SELF);
         MessageSubject.assertThat(secondMessage)
                 .hasReferenceTime(createZonedDateTimeFromMsUtc(2000));
 
-        ConversationActions.Message thirdMessage = messages.get(1);
+        ConversationActions.Message thirdMessage = messages.get(2);
         MessageSubject.assertThat(thirdMessage).hasText("thirdMessage");
         MessageSubject.assertThat(thirdMessage).hasPerson(userA);
         MessageSubject.assertThat(thirdMessage)
                 .hasReferenceTime(createZonedDateTimeFromMsUtc(3000));
 
-        ConversationActions.Message fourthMessage = messages.get(2);
+        ConversationActions.Message fourthMessage = messages.get(3);
         MessageSubject.assertThat(fourthMessage).hasText("fourthMessage");
         MessageSubject.assertThat(fourthMessage).hasPerson(userB);
         MessageSubject.assertThat(fourthMessage)
@@ -245,6 +252,28 @@
     }
 
     @Test
+    public void testSuggest_lastMessageLocalUser() {
+        Person me = new Person.Builder().setName("Me").build();
+        Person userA = new Person.Builder().setName("A").build();
+        Notification.MessagingStyle style =
+                new Notification.MessagingStyle(me)
+                        .addMessage("firstMessage", 1000, userA)
+                        .addMessage("secondMessage", 2000, me);
+        Notification notification =
+                mNotificationBuilder
+                        .setContentText("You have two new messages")
+                        .setStyle(style)
+                        .setActions(createReplyAction())
+                        .build();
+        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+
+        mSmartActionsHelper.suggest(createNotificationEntry());
+
+        verify(mTextClassifier, never())
+                .suggestConversationActions(any(ConversationActions.Request.class));
+    }
+
+    @Test
     public void testSuggest_messageStyle_noPerson() {
         Person me = new Person.Builder().setName("Me").build();
         Notification.MessagingStyle style =
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index 36d0659..f1158ef 100644
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -17,6 +17,7 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
     <solid android:color="#4a4a4a" />
-    <padding android:padding="@dimen/ongoing_appops_chip_bg_padding" />
+    <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
+        android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding"/>
     <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index cbdd51b..58fe811 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -29,14 +29,25 @@
     android:orientation="horizontal"
     android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
     android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
-    android:background="@drawable/privacy_chip_bg"
     android:focusable="true">
 
+        <TextView
+            android:id="@+id/in_use_text"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_vertical|start"
+            android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed"
+            android:gravity="center_vertical"
+            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            android:textColor="@color/status_bar_clock_color"
+            android:text="@string/ongoing_privacy_chip_in_use"
+            />
+
         <LinearLayout
             android:id="@+id/icons_container"
             android:layout_height="match_parent"
             android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical|start"
+            android:layout_gravity="center_vertical"
             android:gravity="center_vertical"
             />
 
@@ -51,7 +62,7 @@
             android:gravity="center_vertical"
             android:textAppearance="@style/TextAppearance.StatusBar.Clock"
             android:textColor="@color/status_bar_clock_color"
-            android:layout_marginStart="@dimen/ongoing_appops_chip_icon_margin"
-            android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin"
+            android:layout_marginStart="@dimen/ongoing_appops_chip_icon_margin_collapsed"
+            android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed"
         />
 </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0344535..1e1245f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -967,7 +967,7 @@
     <!-- Height and width of App Opp icons in Ongoing App Ops dialog -->
     <dimen name="ongoing_appops_dialog_icon_size">24dp</dimen>
     <!-- Left margin of App Opp icons in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_icon_margin">8dp</dimen>
+    <dimen name="ongoing_appops_dialog_icon_margin">12dp</dimen>
     <!-- Height and width of Application icons in Ongoing App Ops dialog -->
     <dimen name="ongoing_appops_dialog_app_icon_size">32dp</dimen>
     <!-- Height and width of Plus sign in Ongoing App Ops dialog -->
@@ -988,12 +988,14 @@
     <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
     <!-- Padding between background of Ongoing App Ops chip and content -->
     <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen>
-    <!-- Margin between icons of Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_chip_icon_margin">4dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip when QQS-->
+    <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip when QS-->
+    <dimen name="ongoing_appops_chip_icon_margin_expanded">8dp</dimen>
     <!-- Icon size of Ongoing App Ops chip -->
     <dimen name="ongoing_appops_chip_icon_size">18dp</dimen>
     <!-- Radius of Ongoing App Ops chip corners -->
-    <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
+    <dimen name="ongoing_appops_chip_bg_corner_radius">4dp</dimen>
     <!-- Text size for Ongoing App Ops dialog title -->
     <dimen name="ongoing_appops_dialog_title_size">20sp</dimen>
     <!-- Text size for Ongoing App Ops dialog items -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index db4a6cc..89c6c8a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2301,6 +2301,9 @@
     <!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
     <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
 
+    <!-- Ongoing Privacy "Chip" in use text [CHAR LIMIT=10]-->
+    <string name="ongoing_privacy_chip_in_use">In use:</string>
+
     <!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]-->
     <plurals name="ongoing_privacy_chip_content_multiple_apps_single_op">
         <item quantity="one"><xliff:g id="num_apps" example="1">%1$d</xliff:g> application is using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index bf9d7ba..976a766 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -34,8 +34,10 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.StatsLog;
 import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
@@ -234,6 +236,8 @@
             mStackView.collapseStack(() -> {
                 try {
                     n.contentIntent.send();
+                    logBubbleClickEvent(mEntry.notification,
+                            StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_APP);
                 } catch (PendingIntent.CanceledException e) {
                     Log.w(TAG, "Failed to send intent for bubble with key: "
                             + (mEntry != null ? mEntry.key : " null entry"));
@@ -242,7 +246,11 @@
         } else if (id == R.id.settings_button) {
             Intent intent = getSettingsIntent(mEntry.notification.getPackageName(),
                     mEntry.notification.getUid());
-            mStackView.collapseStack(() -> mContext.startActivity(intent));
+            mStackView.collapseStack(() -> {
+                mContext.startActivity(intent);
+                logBubbleClickEvent(mEntry.notification,
+                        StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
+            });
         } else if (id == R.id.no_bubbles_button) {
             setBubblesAllowed(false);
         } else if (id == R.id.yes_bubbles_button) {
@@ -262,6 +270,9 @@
             } else if (mOnBubbleBlockedListener != null) {
                 mOnBubbleBlockedListener.onBubbleBlocked(mEntry);
             }
+            logBubbleClickEvent(mEntry.notification,
+                    allowed ? StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_IN :
+                            StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_OUT);
         } catch (RemoteException e) {
             Log.w(TAG, e);
         }
@@ -318,4 +329,22 @@
          */
         void onBubbleBlocked(NotificationEntry entry);
     }
+
+    /**
+     * Logs bubble UI click event.
+     *
+     * @param notification the bubble notification that user is interacting with.
+     * @param action the user interaction enum.
+     */
+    private void logBubbleClickEvent(StatusBarNotification notification, int action) {
+        StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
+                notification.getPackageName(),
+                notification.getNotification().getChannelId(),
+                notification.getId(),
+                mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
+                mStackView.getBubbleCount(),
+                action,
+                mStackView.getNormalizedXPosition(),
+                mStackView.getNormalizedYPosition());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index f6f3fa6..2b344f6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -773,7 +773,7 @@
     /**
      * @return the number of bubbles in the stack view.
      */
-    private int getBubbleCount() {
+    public int getBubbleCount() {
         return mBubbleContainer.getChildCount();
     }
 
@@ -784,14 +784,14 @@
      * @return the index of the bubble view within the bubble stack. The range of the position
      * is between 0 and the bubble count minus 1.
      */
-    private int getBubbleIndex(BubbleView bubbleView) {
+    public int getBubbleIndex(BubbleView bubbleView) {
         return mBubbleContainer.indexOfChild(bubbleView);
     }
 
     /**
      * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
      */
-    private float getNormalizedXPosition() {
+    public float getNormalizedXPosition() {
         return new BigDecimal(getPosition().x / mDisplaySize.x)
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
@@ -800,7 +800,7 @@
     /**
      * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places.
      */
-    private float getNormalizedYPosition() {
+    public float getNormalizedYPosition() {
         return new BigDecimal(getPosition().y / mDisplaySize.y)
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 65ed889..ecbf024 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -16,6 +16,7 @@
 
 import android.content.Context
 import android.util.AttributeSet
+import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
 import android.widget.LinearLayout
@@ -29,14 +30,25 @@
     defStyleRes: Int = 0
 ) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
 
-    private val iconMargin =
-            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin)
+    private val iconMarginExpanded = context.resources.getDimensionPixelSize(
+                    R.dimen.ongoing_appops_chip_icon_margin_expanded)
+    private val iconMarginCollapsed = context.resources.getDimensionPixelSize(
+                    R.dimen.ongoing_appops_chip_icon_margin_collapsed)
     private val iconSize =
             context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
-    val iconColor = context.resources.getColor(
+    private val iconColor = context.resources.getColor(
             R.color.status_bar_clock_color, context.theme)
+    private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
     private lateinit var text: TextView
     private lateinit var iconsContainer: LinearLayout
+    private lateinit var inUseText: TextView
+    var expanded = false
+        set(value) {
+            if (value != field) {
+                field = value
+                updateView()
+            }
+        }
     var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>())
     var privacyList = emptyList<PrivacyItem>()
         set(value) {
@@ -48,15 +60,18 @@
     override fun onFinishInflate() {
         super.onFinishInflate()
 
+        inUseText = findViewById(R.id.in_use_text)
         text = findViewById(R.id.text_container)
         iconsContainer = findViewById(R.id.icons_container)
     }
 
     // Should only be called if the builder icons or app changed
     private fun updateView() {
+        inUseText.visibility = if (expanded) View.GONE else View.VISIBLE
+        background = if (expanded) backgroundDrawable else null
         fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
             iconsContainer.removeAllViews()
-            dialogBuilder.generateIcons().forEach {
+            dialogBuilder.generateIcons().forEachIndexed { i, it ->
                 it.mutate()
                 it.setTint(iconColor)
                 val image = ImageView(context).apply {
@@ -64,17 +79,19 @@
                     scaleType = ImageView.ScaleType.CENTER_INSIDE
                 }
                 iconsContainer.addView(image, iconSize, iconSize)
-                val lp = image.layoutParams as MarginLayoutParams
-                lp.marginStart = iconMargin
-                image.layoutParams = lp
+                if (i != 0) {
+                    val lp = image.layoutParams as MarginLayoutParams
+                    lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed
+                    image.layoutParams = lp
+                }
             }
         }
 
         if (!privacyList.isEmpty()) {
             generateContentDescription()
             setIcons(builder, iconsContainer)
-            text.visibility = if (builder.types.size == 1) VISIBLE else GONE
-            if (builder.types.size == 1) {
+            text.visibility = if (builder.types.size == 1 && expanded) VISIBLE else GONE
+            if (builder.types.size == 1 && expanded) {
                 if (builder.app != null) {
                     text.setText(builder.app?.applicationName)
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index b865ce8..f91c9d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -189,6 +189,7 @@
         addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
                 oldBottom) -> updateAnimator(right - left));
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+        updateEverything();
     }
 
     private void updateAnimator(int width) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 74e82b2..ee9255c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -124,6 +124,7 @@
     private TintedIconManager mIconManager;
     private TouchAnimator mStatusIconsAlphaAnimator;
     private TouchAnimator mHeaderTextContainerAlphaAnimator;
+    private TouchAnimator mPrivacyChipAlphaAnimator;
 
     private View mSystemIconsView;
     private View mQuickQsStatusIcons;
@@ -212,6 +213,8 @@
         mNextAlarmTextView = findViewById(R.id.next_alarm_text);
         mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
         mRingerModeTextView = findViewById(R.id.ringer_mode_text);
+        mPrivacyChip = findViewById(R.id.privacy_chip);
+        mPrivacyChip.setOnClickListener(this);
 
         updateResources();
 
@@ -230,8 +233,6 @@
         mClockView = findViewById(R.id.clock);
         mClockView.setOnClickListener(this);
         mDateView = findViewById(R.id.date);
-        mPrivacyChip = findViewById(R.id.privacy_chip);
-        mPrivacyChip.setOnClickListener(this);
         mSpace = findViewById(R.id.space);
 
         // Tint for the battery icons are handled in setupHost()
@@ -383,6 +384,7 @@
 
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
+        updatePrivacyChipAlphaAnimator();
     }
 
     private void updateStatusIconAlphaAnimator() {
@@ -398,6 +400,12 @@
                 .build();
     }
 
+    private void updatePrivacyChipAlphaAnimator() {
+        mPrivacyChipAlphaAnimator = new TouchAnimator.Builder()
+                .addFloat(mPrivacyChip, "alpha", 1, 0, 1)
+                .build();
+    }
+
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
@@ -431,6 +439,10 @@
         if (mHeaderTextContainerAlphaAnimator != null) {
             mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
         }
+        if (mPrivacyChipAlphaAnimator != null) {
+            mPrivacyChip.setExpanded(expansionFraction > 0.5);
+            mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
+        }
 
         // Check the original expansion fraction - we don't want to show the tooltip until the
         // panel is pulled all the way out.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 8095615..1dc48d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -68,6 +68,7 @@
 public class NotificationContentView extends FrameLayout {
 
     private static final String TAG = "NotificationContentView";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     public static final int VISIBLE_TYPE_CONTRACTED = 0;
     public static final int VISIBLE_TYPE_EXPANDED = 1;
     public static final int VISIBLE_TYPE_HEADSUP = 2;
@@ -1319,6 +1320,14 @@
 
         SmartRepliesAndActions smartRepliesAndActions =
                 chooseSmartRepliesAndActions(mSmartReplyConstants, entry);
+        if (DEBUG) {
+            Log.d(TAG, String.format("Adding suggestions for %s, %d actions, and %d replies.",
+                    entry.notification.getKey(),
+                    smartRepliesAndActions.smartActions == null ? 0 :
+                            smartRepliesAndActions.smartActions.actions.size(),
+                    smartRepliesAndActions.smartReplies == null ? 0 :
+                            smartRepliesAndActions.smartReplies.choices.length));
+        }
 
         applyRemoteInput(entry, smartRepliesAndActions.hasFreeformRemoteInput);
         applySmartReplyView(smartRepliesAndActions, entry);
@@ -1341,6 +1350,10 @@
                 notification.findRemoteInputActionPair(true /* freeform */);
 
         if (!smartReplyConstants.isEnabled()) {
+            if (DEBUG) {
+                Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for "
+                        + entry.notification.getKey());
+            }
             return new SmartRepliesAndActions(null, null, freeformRemoteInputActionPair != null);
         }
         // Only use smart replies from the app if they target P or above. We have this check because
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 1f9b027..e7a71b9 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -20,6 +20,7 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.os.RemoteException;
 import android.security.Scrypt;
 import android.security.keystore.recovery.KeyChainProtectionParams;
 import android.security.keystore.recovery.KeyChainSnapshot;
@@ -162,7 +163,7 @@
         }
     }
 
-    private void syncKeys() {
+    private void syncKeys() throws RemoteException {
         if (mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
             // Application keys for the user will not be available for sync.
             Log.w(TAG, "Credentials are not set for user " + mUserId);
@@ -195,7 +196,7 @@
             && mCredentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
     }
 
-    private void syncKeysForAgent(int recoveryAgentUid) throws IOException {
+    private void syncKeysForAgent(int recoveryAgentUid) throws IOException, RemoteException {
         boolean shouldRecreateCurrentVersion = false;
         if (!shouldCreateSnapshot(recoveryAgentUid)) {
             shouldRecreateCurrentVersion =
@@ -412,7 +413,7 @@
     private Map<String, Pair<SecretKey, byte[]>> getKeysToSync(int recoveryAgentUid)
             throws InsecureUserException, KeyStoreException, UnrecoverableKeyException,
             NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
-            InvalidKeyException, InvalidAlgorithmParameterException, IOException {
+            InvalidKeyException, InvalidAlgorithmParameterException, IOException, RemoteException {
         PlatformDecryptionKey decryptKey = mPlatformKeyManager.getDecryptKey(mUserId);;
         Map<String, WrappedKey> wrappedKeys = mRecoverableKeyStoreDb.getAllKeys(
                 mUserId, recoveryAgentUid, decryptKey.getGenerationId());
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index 9ca052b..1c18771 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -18,15 +18,21 @@
 
 import android.app.KeyguardManager;
 import android.content.Context;
+import android.os.RemoteException;
+import android.security.GateKeeper;
 import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
+import android.service.gatekeeper.IGateKeeperService;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 
 import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
@@ -34,9 +40,11 @@
 import java.security.cert.CertificateException;
 import java.util.Locale;
 
+import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
-import javax.security.auth.DestroyFailedException;
+import javax.crypto.spec.GCMParameterSpec;
 
 /**
  * Manages creating and checking the validity of the platform key.
@@ -67,6 +75,10 @@
     private static final String ENCRYPT_KEY_ALIAS_SUFFIX = "encrypt";
     private static final String DECRYPT_KEY_ALIAS_SUFFIX = "decrypt";
     private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15;
+    private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+    private static final int GCM_TAG_LENGTH_BITS = 128;
+    // Only used for checking if a key is usable
+    private static final byte[] GCM_INSECURE_NONCE_BYTES = new byte[12];
 
     private final Context mContext;
     private final KeyStoreProxy mKeyStore;
@@ -158,12 +170,14 @@
      * @throws KeyStoreException if there is an error in AndroidKeyStore.
      * @throws InsecureUserException if the user does not have a lock screen set.
      * @throws IOException if there was an issue with local database update.
+     * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
      *
      * @hide
      */
     @VisibleForTesting
     void regenerate(int userId)
-            throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException, IOException {
+            throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException, IOException,
+                    RemoteException {
         if (!isAvailable(userId)) {
             throw new InsecureUserException(String.format(
                     Locale.US, "%d does not have a lock screen set.", userId));
@@ -190,11 +204,13 @@
      * @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
      * @throws InsecureUserException if the user does not have a lock screen set.
      * @throws IOException if there was an issue with local database update.
+     * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
      *
      * @hide
      */
-    public PlatformEncryptionKey getEncryptKey(int userId) throws KeyStoreException,
-           UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException, IOException {
+    public PlatformEncryptionKey getEncryptKey(int userId)
+            throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
+                    InsecureUserException, IOException, RemoteException {
         init(userId);
         try {
             // Try to see if the decryption key is still accessible before using the encryption key.
@@ -243,14 +259,18 @@
      * @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
      * @throws InsecureUserException if the user does not have a lock screen set.
      * @throws IOException if there was an issue with local database update.
+     * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
      *
      * @hide
      */
-    public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException,
-           UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException, IOException {
+    public PlatformDecryptionKey getDecryptKey(int userId)
+            throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
+                    InsecureUserException, IOException, RemoteException {
         init(userId);
         try {
-            return getDecryptKeyInternal(userId);
+            PlatformDecryptionKey decryptionKey = getDecryptKeyInternal(userId);
+            ensureDecryptionKeyIsValid(userId, decryptionKey);
+            return decryptionKey;
         } catch (UnrecoverableKeyException e) {
             Log.i(TAG, String.format(Locale.US,
                     "Regenerating permanently invalid Platform key for user %d.",
@@ -284,6 +304,29 @@
     }
 
     /**
+     * Tries to use the decryption key to make sure it is not permanently invalidated. The exception
+     * {@code KeyPermanentlyInvalidatedException} is thrown only when the key is in use.
+     *
+     * <p>Note that we ignore all other InvalidKeyException exceptions, because such an exception
+     * may be thrown for auth-bound keys if there's no recent unlock event.
+     */
+    private void ensureDecryptionKeyIsValid(int userId, PlatformDecryptionKey decryptionKey)
+            throws UnrecoverableKeyException {
+        try {
+            Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM).init(Cipher.UNWRAP_MODE,
+                    decryptionKey.getKey(),
+                    new GCMParameterSpec(GCM_TAG_LENGTH_BITS, GCM_INSECURE_NONCE_BYTES));
+        } catch (KeyPermanentlyInvalidatedException e) {
+            Log.e(TAG, String.format(Locale.US, "The platform key for user %d became invalid.",
+                    userId));
+            throw new UnrecoverableKeyException(e.getMessage());
+        } catch (NoSuchAlgorithmException | InvalidKeyException
+                | InvalidAlgorithmParameterException | NoSuchPaddingException e) {
+            // Ignore all other exceptions
+        }
+    }
+
+    /**
      * Initializes the class. If there is no current platform key, and the user has a lock screen
      * set, will create the platform key and set the generation ID.
      *
@@ -291,11 +334,13 @@
      * @throws KeyStoreException if there was an error in AndroidKeyStore.
      * @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
      * @throws IOException if there was an issue with local database update.
+     * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
      *
      * @hide
      */
     void init(int userId)
-            throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException, IOException {
+            throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException, IOException,
+                    RemoteException {
         if (!isAvailable(userId)) {
             throw new InsecureUserException(String.format(
                     Locale.US, "%d does not have a lock screen set.", userId));
@@ -372,6 +417,11 @@
                 && mKeyStore.containsAlias(getDecryptAlias(userId, generationId));
     }
 
+    @VisibleForTesting
+    IGateKeeperService getGateKeeperService() {
+        return GateKeeper.getService();
+    }
+
     /**
      * Generates a new 256-bit AES key, and loads it into AndroidKeyStore with the given
      * {@code generationId} determining its aliases.
@@ -380,15 +430,23 @@
      *     available since API version 1.
      * @throws KeyStoreException if there was an issue loading the keys into AndroidKeyStore.
      * @throws IOException if there was an issue with local database update.
+     * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
      */
     private void generateAndLoadKey(int userId, int generationId)
-            throws NoSuchAlgorithmException, KeyStoreException, IOException {
+            throws NoSuchAlgorithmException, KeyStoreException, IOException, RemoteException {
         String encryptAlias = getEncryptAlias(userId, generationId);
         String decryptAlias = getDecryptAlias(userId, generationId);
         // SecretKey implementation doesn't provide reliable way to destroy the secret
         // so it may live in memory for some time.
         SecretKey secretKey = generateAesKey();
 
+        long secureUserId = getGateKeeperService().getSecureUserId(userId);
+        // TODO(b/124095438): Propagate this failure instead of silently failing.
+        if (secureUserId == GateKeeper.INVALID_SECURE_USER_ID) {
+            Log.e(TAG, "No SID available for user " + userId);
+            return;
+        }
+
         // Store decryption key first since it is more likely to fail.
         mKeyStore.setEntry(
                 decryptAlias,
@@ -399,7 +457,7 @@
                             USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS)
                     .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
-                    .setBoundToSpecificSecureUserId(userId)
+                    .setBoundToSpecificSecureUserId(secureUserId)
                     .build());
         mKeyStore.setEntry(
                 encryptAlias,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6c3e1f4..00105be 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -188,6 +188,7 @@
 import android.util.TimeUtils;
 import android.util.TypedValue;
 import android.util.proto.ProtoOutputStream;
+import android.view.Choreographer;
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
@@ -843,7 +844,7 @@
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
             synchronized (mGlobalLock) {
                 SurfaceControl.closeTransaction();
-                traceStateLocked(where);
+                mWindowTracing.logState(where);
             }
         } finally {
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -967,7 +968,8 @@
         mWindowPlacerLocked = new WindowSurfacePlacer(this);
         mTaskSnapshotController = new TaskSnapshotController(this);
 
-        mWindowTracing = WindowTracing.createDefaultAndStartLooper(context);
+        mWindowTracing = WindowTracing.createDefaultAndStartLooper(this,
+                Choreographer.getInstance());
 
         LocalServices.addService(WindowManagerPolicy.class, mPolicy);
 
@@ -5765,17 +5767,6 @@
         proto.write(LAST_ORIENTATION, defaultDisplayContent.getLastOrientation());
     }
 
-    void traceStateLocked(String where) {
-        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
-        try {
-            mWindowTracing.traceStateLocked(where, this);
-        } catch (Exception e) {
-            Log.wtf(TAG, "Exception while tracing state", e);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
     private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
             ArrayList<WindowState> windows) {
         pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)");
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 8b1ffa8..abc474d 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -24,12 +24,14 @@
 import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
 
 import android.annotation.Nullable;
-import android.content.Context;
 import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
+import android.view.Choreographer;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.File;
 import java.io.IOException;
@@ -48,6 +50,10 @@
     private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024;
     private static final String TAG = "WindowTracing";
 
+    private final WindowManagerService mService;
+    private final Choreographer mChoreographer;
+    private final WindowManagerGlobalLock mGlobalLock;
+
     private final Object mLock = new Object();
     private final WindowTraceBuffer.Builder mBufferBuilder;
 
@@ -57,11 +63,24 @@
     private boolean mContinuousMode;
     private boolean mEnabled;
     private volatile boolean mEnabledLockFree;
+    private boolean mScheduled;
+    private Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
+            log("onFrame" /* where */);
 
-    WindowTracing(File file) {
+    private WindowTracing(File file, WindowManagerService service, Choreographer choreographer) {
+        this(file, service, choreographer, service.mGlobalLock);
+    }
+
+    @VisibleForTesting
+    WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
+            WindowManagerGlobalLock globalLock) {
         mBufferBuilder = new WindowTraceBuffer.Builder()
                 .setTraceFile(file)
                 .setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE);
+
+        mChoreographer = choreographer;
+        mService = service;
+        mGlobalLock = globalLock;
     }
 
     void startTrace(@Nullable PrintWriter pw) throws IOException {
@@ -111,7 +130,8 @@
         }
     }
 
-    private void setContinuousMode(boolean continuous, PrintWriter pw) {
+    @VisibleForTesting
+    void setContinuousMode(boolean continuous, PrintWriter pw) {
         logAndPrintln(pw, "Setting window tracing continuous mode to " + continuous);
 
         if (mEnabled) {
@@ -123,21 +143,14 @@
                 WindowTraceLogLevel.TRIM;
     }
 
-    private void appendTraceEntry(ProtoOutputStream proto) {
-        if (!mEnabledLockFree) {
-            return;
-        }
-
-        mTraceBuffer.add(proto);
-    }
-
     boolean isEnabled() {
         return mEnabledLockFree;
     }
 
-    static WindowTracing createDefaultAndStartLooper(Context context) {
+    static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
+            Choreographer choreographer) {
         File file = new File("/data/misc/wmtrace/wm_trace.pb");
-        return new WindowTracing(file);
+        return new WindowTracing(file, service, choreographer);
     }
 
     int onShellCommand(ShellCommand shell) {
@@ -164,28 +177,65 @@
         }
     }
 
-    void traceStateLocked(String where, WindowManagerService service) {
+    /**
+     * If tracing is enabled, log the current state or schedule the next frame to be logged,
+     * according to {@link #mContinuousMode}.
+     *
+     * @param where Logging point descriptor
+     */
+    void logState(String where) {
         if (!isEnabled()) {
             return;
         }
 
-        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToBufferLocked");
-        try {
-            ProtoOutputStream os = new ProtoOutputStream();
-            long tokenOuter = os.start(ENTRY);
-            os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
-            os.write(WHERE, where);
+        if (mContinuousMode) {
+            schedule();
+        } else {
+            log(where);
+        }
+    }
 
-            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked");
-            try {
-                long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
-                service.writeToProtoLocked(os, mWindowTraceLogLevel);
-                os.end(tokenInner);
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+    /**
+     * Schedule the log to trace the next frame
+     */
+    private void schedule() {
+        if (mScheduled) {
+            return;
+        }
+
+        mScheduled = true;
+        mChoreographer.postFrameCallback(mFrameCallback);
+    }
+
+    /**
+     * Write the current frame to the buffer
+     *
+     * @param where Logging point descriptor
+     */
+    private void log(String where) {
+        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
+        try {
+            synchronized (mGlobalLock) {
+                ProtoOutputStream os = new ProtoOutputStream();
+                long tokenOuter = os.start(ENTRY);
+                os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+                os.write(WHERE, where);
+
+                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked");
+                try {
+                    long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+                    mService.writeToProtoLocked(os, mWindowTraceLogLevel);
+                    os.end(tokenInner);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+                }
+                os.end(tokenOuter);
+                mTraceBuffer.add(os);
+
+                mScheduled = false;
             }
-            os.end(tokenOuter);
-            appendTraceEntry(os);
+        } catch (Exception e) {
+            Log.wtf(TAG, "Exception while tracing state", e);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index 13436e7..821d97a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -24,14 +24,21 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.testng.Assert.expectThrows;
 
 import android.app.KeyguardManager;
 import android.content.Context;
+import android.os.RemoteException;
+import android.security.GateKeeper;
+import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
+import android.service.gatekeeper.IGateKeeperService;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -53,6 +60,8 @@
 import java.security.UnrecoverableKeyException;
 import java.util.List;
 
+import javax.crypto.KeyGenerator;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class PlatformKeyManagerTest {
@@ -60,10 +69,15 @@
     private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
     private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15;
     private static final int USER_ID_FIXTURE = 42;
+    private static final long USER_SID = 4200L;
+    private static final String KEY_ALGORITHM = "AES";
+    private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+    private static final String TESTING_KEYSTORE_KEY_ALIAS = "testing-key-store-key-alias";
 
     @Mock private Context mContext;
     @Mock private KeyStoreProxy mKeyStoreProxy;
     @Mock private KeyguardManager mKeyguardManager;
+    @Mock private IGateKeeperService mGateKeeperService;
 
     @Captor private ArgumentCaptor<KeyStore.ProtectionParameter> mProtectionParameterCaptor;
     @Captor private ArgumentCaptor<KeyStore.Entry> mEntryArgumentCaptor;
@@ -74,18 +88,19 @@
     private PlatformKeyManager mPlatformKeyManager;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         Context context = InstrumentationRegistry.getTargetContext();
         mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
         mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
-        mPlatformKeyManager = new PlatformKeyManager(
-                mContext, mKeyStoreProxy, mRecoverableKeyStoreDb);
+        mPlatformKeyManager = new PlatformKeyManagerTestable(
+                mContext, mKeyStoreProxy, mRecoverableKeyStoreDb, mGateKeeperService);
 
         when(mContext.getSystemService(anyString())).thenReturn(mKeyguardManager);
         when(mContext.getSystemServiceName(any())).thenReturn("test");
         when(mKeyguardManager.isDeviceSecure(USER_ID_FIXTURE)).thenReturn(true);
+        when(mGateKeeperService.getSecureUserId(USER_ID_FIXTURE)).thenReturn(USER_SID);
     }
 
     @After
@@ -192,11 +207,36 @@
         mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertEquals(
-                USER_ID_FIXTURE,
+                USER_SID,
                 getDecryptKeyProtection().getBoundToSpecificSecureUserId());
     }
 
     @Test
+    public void init_doesNotCreateDecryptKeyIfNoSid() throws Exception {
+        when(mGateKeeperService.getSecureUserId(USER_ID_FIXTURE))
+                .thenReturn(GateKeeper.INVALID_SECURE_USER_ID);
+
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
+
+        verify(mKeyStoreProxy, never()).setEntry(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+                any(),
+                any());
+    }
+
+    @Test
+    public void init_doesNotCreateDecryptKeyOnGateKeeperException() throws Exception {
+        when(mGateKeeperService.getSecureUserId(USER_ID_FIXTURE)).thenThrow(new RemoteException());
+
+        expectThrows(RemoteException.class, () -> mPlatformKeyManager.init(USER_ID_FIXTURE));
+
+        verify(mKeyStoreProxy, never()).setEntry(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+                any(),
+                any());
+    }
+
+    @Test
     public void init_createsBothKeysWithSameMaterial() throws Exception {
         mPlatformKeyManager.init(USER_ID_FIXTURE);
 
@@ -259,6 +299,9 @@
         when(mKeyStoreProxy
                 .containsAlias("com.android.server.locksettings.recoverablekeystore/"
                         + "platform/42/1/encrypt")).thenReturn(true);
+        when(mKeyStoreProxy.getKey(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+                any())).thenReturn(generateAndroidKeyStoreKey());
 
         mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
 
@@ -281,6 +324,9 @@
         when(mKeyStoreProxy
                 .containsAlias("com.android.server.locksettings.recoverablekeystore/"
                         + "platform/42/2/decrypt")).thenReturn(true);
+        when(mKeyStoreProxy.getKey(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+                any())).thenReturn(generateAndroidKeyStoreKey());
 
         mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
 
@@ -352,6 +398,9 @@
         doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
                 eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
                 any());
+        when(mKeyStoreProxy.getKey(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+                any())).thenReturn(generateAndroidKeyStoreKey());
 
         when(mKeyStoreProxy
                 .containsAlias("com.android.server.locksettings.recoverablekeystore/"
@@ -536,4 +585,34 @@
                 mProtectionParameterCaptor.capture());
         return (KeyProtection) mProtectionParameterCaptor.getValue();
     }
+
+    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance(
+                KEY_ALGORITHM,
+                ANDROID_KEY_STORE_PROVIDER);
+        keyGenerator.init(new KeyGenParameterSpec.Builder(TESTING_KEYSTORE_KEY_ALIAS,
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .build());
+        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+    }
+
+    class PlatformKeyManagerTestable extends PlatformKeyManager {
+        private IGateKeeperService mGateKeeperService;
+
+        PlatformKeyManagerTestable(
+                Context context,
+                KeyStoreProxy keyStoreProxy,
+                RecoverableKeyStoreDb database,
+                IGateKeeperService gateKeeperService) {
+            super(context, keyStoreProxy, database);
+            mGateKeeperService = gateKeeperService;
+        }
+
+        @Override
+        IGateKeeperService getGateKeeperService() {
+            return mGateKeeperService;
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
index b6b9a86..2970c21 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -34,8 +34,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.testing.DexmakerShareClassLoaderRule;
 import android.util.proto.ProtoOutputStream;
+import android.view.Choreographer;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.Preconditions;
@@ -74,6 +74,8 @@
 
     @Mock
     private WindowManagerService mWmMock;
+    @Mock
+    private Choreographer mChoreographer;
     private WindowTracing mWindowTracing;
     private File mFile;
 
@@ -85,7 +87,9 @@
         mFile = testContext.getFileStreamPath("tracing_test.dat");
         mFile.delete();
 
-        mWindowTracing = new WindowTracing(mFile);
+        mWindowTracing = new WindowTracing(mFile, mWmMock, mChoreographer,
+                new WindowManagerGlobalLock());
+        mWindowTracing.setContinuousMode(false /* continuous */, null /* pw */);
     }
 
     @After
@@ -113,15 +117,14 @@
 
     @Test
     public void trace_discared_whenNotTracing() {
-        mWindowTracing.traceStateLocked("where", mWmMock);
+        mWindowTracing.logState("where");
         verifyZeroInteractions(mWmMock);
     }
 
     @Test
     public void trace_dumpsWindowManagerState_whenTracing() throws Exception {
         mWindowTracing.startTrace(mock(PrintWriter.class));
-        mWindowTracing.traceStateLocked("where", mWmMock);
-
+        mWindowTracing.logState("where");
         verify(mWmMock).writeToProtoLocked(any(), eq(WindowTraceLogLevel.TRIM));
     }
 
@@ -147,7 +150,7 @@
                     WindowManagerTraceProto.WHERE, "TEST_WM_PROTO");
             return null;
         }).when(mWmMock).writeToProtoLocked(any(), any());
-        mWindowTracing.traceStateLocked("TEST_WHERE", mWmMock);
+        mWindowTracing.logState("TEST_WHERE");
 
         mWindowTracing.stopTrace(mock(PrintWriter.class));
 
diff --git a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
index 29f10e9..b3b5b29 100644
--- a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
+++ b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
@@ -61,6 +61,7 @@
     public static class TlvConstructor {
         private int mTypeSize;
         private int mLengthSize;
+        private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
 
         private byte[] mArray;
         private int mArrayLength;
@@ -84,6 +85,20 @@
             }
             mTypeSize = typeSize;
             mLengthSize = lengthSize;
+            mPosition = 0;
+        }
+
+        /**
+         * Configure the TLV constructor to use a particular byte order. Should be
+         * {@link ByteOrder#BIG_ENDIAN} (the default at construction) or
+         * {@link ByteOrder#LITTLE_ENDIAN}.
+         *
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor setByteOrder(ByteOrder byteOrder) {
+            mByteOrder = byteOrder;
+            return this;
         }
 
         /**
@@ -96,6 +111,7 @@
         public TlvConstructor wrap(@Nullable byte[] array) {
             mArray = array;
             mArrayLength = (array == null) ? 0 : array.length;
+            mPosition = 0;
             return this;
         }
 
@@ -109,6 +125,7 @@
         public TlvConstructor allocate(int capacity) {
             mArray = new byte[capacity];
             mArrayLength = capacity;
+            mPosition = 0;
             return this;
         }
 
@@ -155,6 +172,18 @@
         }
 
         /**
+         * Copies a raw byte into the TLV buffer - without a type or a length.
+         *
+         * @param b The byte to be inserted into the structure.
+         * @return The constructor to facilitate chaining {@code cts.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor putRawByte(byte b) {
+            checkRawLength(1);
+            mArray[mPosition++] = b;
+            return this;
+        }
+
+        /**
          * Copies a byte array into the TLV with the indicated type. For an LV
          * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
          * TlvConstructor(int, int)} ) the type field is ignored.
@@ -193,6 +222,22 @@
         }
 
         /**
+         * Copies a byte array into the TLV - without a type or a length.
+         *
+         * @param array The array to be copied (in full) into the TLV structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor putRawByteArray(@Nullable byte[] array) {
+            if (array == null) return this;
+
+            checkRawLength(array.length);
+            System.arraycopy(array, 0, mArray, mPosition, array.length);
+            mPosition += array.length;
+            return this;
+        }
+
+        /**
          * Places a zero length element (i.e. Length field = 0) into the TLV.
          * For an LV formatted structure (i.e. typeLength=0 in
          * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is
@@ -221,7 +266,7 @@
         public TlvConstructor putShort(int type, short data) {
             checkLength(2);
             addHeader(type, 2);
-            Memory.pokeShort(mArray, mPosition, data, ByteOrder.BIG_ENDIAN);
+            Memory.pokeShort(mArray, mPosition, data, mByteOrder);
             mPosition += 2;
             return this;
         }
@@ -239,7 +284,7 @@
         public TlvConstructor putInt(int type, int data) {
             checkLength(4);
             addHeader(type, 4);
-            Memory.pokeInt(mArray, mPosition, data, ByteOrder.BIG_ENDIAN);
+            Memory.pokeInt(mArray, mPosition, data, mByteOrder);
             mPosition += 4;
             return this;
         }
@@ -294,18 +339,24 @@
             }
         }
 
+        private void checkRawLength(int dataLength) {
+            if (mPosition + dataLength > mArrayLength) {
+                throw new BufferOverflowException();
+            }
+        }
+
         private void addHeader(int type, int length) {
             if (mTypeSize == 1) {
                 mArray[mPosition] = (byte) type;
             } else if (mTypeSize == 2) {
-                Memory.pokeShort(mArray, mPosition, (short) type, ByteOrder.BIG_ENDIAN);
+                Memory.pokeShort(mArray, mPosition, (short) type, mByteOrder);
             }
             mPosition += mTypeSize;
 
             if (mLengthSize == 1) {
                 mArray[mPosition] = (byte) length;
             } else if (mLengthSize == 2) {
-                Memory.pokeShort(mArray, mPosition, (short) length, ByteOrder.BIG_ENDIAN);
+                Memory.pokeShort(mArray, mPosition, (short) length, mByteOrder);
             }
             mPosition += mLengthSize;
         }
@@ -330,13 +381,19 @@
         public int length;
 
         /**
+         * Control of the endianess of the TLV element - true for big-endian, false for little-
+         * endian.
+         */
+        public ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
+
+        /**
          * The Value (V) field - a raw byte array representing the current TLV
          * element where the entry starts at {@link TlvElement#offset}.
          */
-        public byte[] refArray;
+        private byte[] mRefArray;
 
         /**
-         * The offset to be used into {@link TlvElement#refArray} to access the
+         * The offset to be used into {@link TlvElement#mRefArray} to access the
          * raw data representing the current TLV element.
          */
         public int offset;
@@ -344,7 +401,7 @@
         private TlvElement(int type, int length, @Nullable byte[] refArray, int offset) {
             this.type = type;
             this.length = length;
-            this.refArray = refArray;
+            mRefArray = refArray;
             this.offset = offset;
 
             if (offset + length > refArray.length) {
@@ -353,6 +410,15 @@
         }
 
         /**
+         * Return the raw byte array of the Value (V) field.
+         *
+         * @return The Value (V) field as a byte array.
+         */
+        public byte[] getRawData() {
+            return Arrays.copyOfRange(mRefArray, offset, offset + length);
+        }
+
+        /**
          * Utility function to return a byte representation of a TLV element of
          * length 1. Note: an attempt to call this function on a TLV item whose
          * {@link TlvElement#length} is != 1 will result in an exception.
@@ -364,7 +430,7 @@
                 throw new IllegalArgumentException(
                         "Accesing a byte from a TLV element of length " + length);
             }
-            return refArray[offset];
+            return mRefArray[offset];
         }
 
         /**
@@ -379,7 +445,7 @@
                 throw new IllegalArgumentException(
                         "Accesing a short from a TLV element of length " + length);
             }
-            return Memory.peekShort(refArray, offset, ByteOrder.BIG_ENDIAN);
+            return Memory.peekShort(mRefArray, offset, byteOrder);
         }
 
         /**
@@ -394,7 +460,7 @@
                 throw new IllegalArgumentException(
                         "Accesing an int from a TLV element of length " + length);
             }
-            return Memory.peekInt(refArray, offset, ByteOrder.BIG_ENDIAN);
+            return Memory.peekInt(mRefArray, offset, byteOrder);
         }
 
         /**
@@ -403,7 +469,7 @@
          * @return String repersentation of the current TLV element.
          */
         public String getString() {
-            return new String(refArray, offset, length);
+            return new String(mRefArray, offset, length);
         }
     }
 
@@ -413,6 +479,7 @@
     public static class TlvIterable implements Iterable<TlvElement> {
         private int mTypeSize;
         private int mLengthSize;
+        private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
         private byte[] mArray;
         private int mArrayLength;
 
@@ -440,6 +507,13 @@
         }
 
         /**
+         * Configure the TLV iterator to use little-endian byte ordering.
+         */
+        public void setByteOrder(ByteOrder byteOrder) {
+            mByteOrder = byteOrder;
+        }
+
+        /**
          * Prints out a parsed representation of the TLV-formatted byte array.
          * Whenever possible bytes, shorts, and integer are printed out (for
          * fields whose length is 1, 2, or 4 respectively).
@@ -486,7 +560,7 @@
         public List<byte[]> toList() {
             List<byte[]> list = new ArrayList<>();
             for (TlvElement tlv : this) {
-                list.add(Arrays.copyOfRange(tlv.refArray, tlv.offset, tlv.offset + tlv.length));
+                list.add(Arrays.copyOfRange(tlv.mRefArray, tlv.offset, tlv.offset + tlv.length));
             }
 
             return list;
@@ -516,7 +590,7 @@
                     if (mTypeSize == 1) {
                         type = mArray[mOffset];
                     } else if (mTypeSize == 2) {
-                        type = Memory.peekShort(mArray, mOffset, ByteOrder.BIG_ENDIAN);
+                        type = Memory.peekShort(mArray, mOffset, mByteOrder);
                     }
                     mOffset += mTypeSize;
 
@@ -524,11 +598,12 @@
                     if (mLengthSize == 1) {
                         length = mArray[mOffset];
                     } else if (mLengthSize == 2) {
-                        length = Memory.peekShort(mArray, mOffset, ByteOrder.BIG_ENDIAN);
+                        length = Memory.peekShort(mArray, mOffset, mByteOrder);
                     }
                     mOffset += mLengthSize;
 
                     TlvElement tlv = new TlvElement(type, length, mArray, mOffset);
+                    tlv.byteOrder = mByteOrder;
                     mOffset += length;
                     return tlv;
                 }
@@ -543,7 +618,8 @@
 
     /**
      * Validates that a (T)LV array is constructed correctly. I.e. that its specified Length
-     * fields correctly fill the specified length (and do not overshoot).
+     * fields correctly fill the specified length (and do not overshoot). Uses big-endian
+     * byte ordering.
      *
      * @param array The (T)LV array to verify.
      * @param typeSize The size (in bytes) of the type field. Valid values are 0, 1, or 2.
@@ -551,6 +627,22 @@
      * @return A boolean indicating whether the array is valid (true) or invalid (false).
      */
     public static boolean isValid(@Nullable byte[] array, int typeSize, int lengthSize) {
+        return isValidEndian(array, typeSize, lengthSize, ByteOrder.BIG_ENDIAN);
+    }
+
+    /**
+     * Validates that a (T)LV array is constructed correctly. I.e. that its specified Length
+     * fields correctly fill the specified length (and do not overshoot).
+     *
+     * @param array The (T)LV array to verify.
+     * @param typeSize The size (in bytes) of the type field. Valid values are 0, 1, or 2.
+     * @param lengthSize The size (in bytes) of the length field. Valid values are 1 or 2.
+     * @param byteOrder The endianness of the byte array: {@link ByteOrder#BIG_ENDIAN} or
+     *                  {@link ByteOrder#LITTLE_ENDIAN}.
+     * @return A boolean indicating whether the array is valid (true) or invalid (false).
+     */
+    public static boolean isValidEndian(@Nullable byte[] array, int typeSize, int lengthSize,
+            ByteOrder byteOrder) {
         if (typeSize < 0 || typeSize > 2) {
             throw new IllegalArgumentException(
                     "Invalid arguments - typeSize must be 0, 1, or 2: typeSize=" + typeSize);
@@ -569,8 +661,7 @@
             if (lengthSize == 1) {
                 nextTlvIndex += lengthSize + array[nextTlvIndex];
             } else {
-                nextTlvIndex += lengthSize + Memory.peekShort(array, nextTlvIndex,
-                        ByteOrder.BIG_ENDIAN);
+                nextTlvIndex += lengthSize + Memory.peekShort(array, nextTlvIndex, byteOrder);
             }
         }
 
diff --git a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
index 83affed..971aa8e 100644
--- a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
@@ -28,6 +28,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+
 /**
  * Unit test harness for TlvBufferUtils class.
  */
@@ -69,6 +70,24 @@
     }
 
     /**
+     * Validate that re-using a TLV by any of the reallocation method resets it completely.
+     */
+    @Test
+    public void testTlvReuse() {
+        TlvBufferUtils.TlvConstructor tlv = new TlvBufferUtils.TlvConstructor(1, 1);
+
+        tlv.allocate(10);
+        tlv.putByte(0, (byte) 2);
+        tlv.putByte(1, (byte) 104);
+
+        collector.checkThat("initial", tlv.getArray(), equalTo(new byte[]{0, 1, 2, 1, 1, 104}));
+
+        tlv.allocate(8);
+        tlv.putByte(5, (byte) 7);
+        collector.checkThat("re-alloc", tlv.getArray(), equalTo(new byte[]{5, 1, 7}));
+    }
+
+    /**
      * Verify that can build a valid TLV from a List of byte[].
      */
     @Test
@@ -121,6 +140,23 @@
         List<byte[]> data = new TlvBufferUtils.TlvIterable(0, 1, invalidTlv01).toList();
     }
 
+    /**
+     * Validate the API which places raw bytes into the TLV (without a TL structure).
+     */
+    @Test
+    public void testRawPuts() {
+        TlvBufferUtils.TlvConstructor tlv = new TlvBufferUtils.TlvConstructor(1, 1);
+
+        tlv.allocate(10);
+        tlv.putByte(0, (byte) 2);
+        tlv.putRawByte((byte) 55);
+        tlv.putByte(1, (byte) 104);
+        tlv.putRawByteArray(new byte[]{66, 77});
+
+        collector.checkThat("data", tlv.getArray(),
+                equalTo(new byte[]{0, 1, 2, 55, 1, 1, 104, 66, 77}));
+    }
+
     @Test
     public void testTlvIterate() {
         final String ascii = "ABC";
@@ -163,6 +199,7 @@
         tlv02.putByte(0, (byte) 2);
         tlv02.putString(0, ascii);
         tlv02.putString(0, nonAscii);
+        tlv02.putByteArray(0, new byte[]{5, 4, 3, 2, 1});
 
         TlvBufferUtils.TlvIterable tlv02It = new TlvBufferUtils.TlvIterable(0, 2, tlv02.getArray());
         count = 0;
@@ -181,6 +218,11 @@
                         equalTo(nonAscii.getBytes().length));
                 collector.checkThat("tlv02-correct-iteration-DATA",
                         tlv.getString().equals(nonAscii), equalTo(true));
+            } else if (count == 3) {
+                collector.checkThat("tlv02-correct-iteration-mLength", tlv.length,
+                        equalTo(5));
+                collector.checkThat("tlv02-correct-iteration-DATA", tlv.getRawData(),
+                        equalTo(new byte[]{5, 4, 3, 2, 1}));
             } else {
                 collector.checkThat("Invalid number of iterations in loop - tlv02", true,
                         equalTo(false));
@@ -188,7 +230,7 @@
             ++count;
         }
         collector.checkThat("Invalid number of iterations outside loop - tlv02", count,
-                equalTo(3));
+                equalTo(4));
 
         collector.checkThat("tlv22-valid",
                 TlvBufferUtils.isValid(tlv22.getArray(), 2, 2),