Merge "Support batched requests < GNSS batched size" into sc-qpr1-dev
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
index ced2fb5..73f02d3 100644
--- a/apex/media/OWNERS
+++ b/apex/media/OWNERS
@@ -1,10 +1,10 @@
-andrewlewis@google.com
-aquilescanta@google.com
-chz@google.com
+# Bug component: 1344
 hdmoon@google.com
 hkuang@google.com
 jinpark@google.com
 klhyun@google.com
 lnilsson@google.com
-marcone@google.com
 sungsoo@google.com
+
+# go/android-fwk-media-solutions for info on areas of ownership.
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/core/java/android/service/translation/ITranslationService.aidl b/core/java/android/service/translation/ITranslationService.aidl
index e9dd2c3b..4cc732a 100644
--- a/core/java/android/service/translation/ITranslationService.aidl
+++ b/core/java/android/service/translation/ITranslationService.aidl
@@ -24,7 +24,7 @@
 /**
  * System-wide on-device translation service.
  *
- * <p>Services requests to translate text between different languages. The primary use case for this
+ * <p>Services requests to translate data between different languages. The primary use case for this
  * service is automatic translation of text and web views, when the auto Translate feature is
  * enabled.
  *
diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java
index 93c006a..d454c39 100644
--- a/core/java/android/service/translation/TranslationService.java
+++ b/core/java/android/service/translation/TranslationService.java
@@ -48,6 +48,7 @@
 import android.view.translation.TranslationRequest;
 import android.view.translation.TranslationResponse;
 import android.view.translation.TranslationSpec;
+import android.view.translation.Translator;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -81,7 +82,10 @@
      * android.R.styleable#TranslationService translation-service}&gt;</code> tag.
      *
      * <p>Here's an example of how to use it on {@code AndroidManifest.xml}:
-     * TODO: fill in doc example (check CCService/AFService).
+     * <pre> &lt;translation-service
+     *     android:settingsActivity="foo.bar.SettingsActivity"
+     *     . . .
+     * /&gt;</pre>
      */
     public static final String SERVICE_META_DATA = "android.translation_service";
 
@@ -148,7 +152,6 @@
         void onTranslationSuccess(@NonNull TranslationResponse response);
 
         /**
-         * TODO: implement javadoc
          * @removed use {@link #onTranslationSuccess} with an error response instead.
          */
         @Deprecated
@@ -225,7 +228,7 @@
      * should call back with {@code false}.</p>
      *
      * @param translationContext the {@link TranslationContext} of the session being created.
-     * @param sessionId the int id of the session.
+     * @param sessionId the id of the session.
      * @param callback {@link Consumer} to notify whether the session was successfully created.
      */
     // TODO(b/176464808): the session id won't be unique cross client/server process. Need to find
@@ -234,8 +237,6 @@
             int sessionId, @NonNull Consumer<Boolean> callback);
 
     /**
-     * TODO: fill in javadoc.
-     *
      * @removed use {@link #onCreateTranslationSession(TranslationContext, int, Consumer)}
      * instead.
      */
@@ -246,19 +247,16 @@
     }
 
     /**
-     * TODO: fill in javadoc.
+     * Called when a translation session is finished.
      *
-     * @param sessionId
+     * <p>The translation session is finished when the client calls {@link Translator#destroy()} on
+     * the corresponding translator.
+     *
+     * @param sessionId id of the session that finished.
      */
     public abstract void onFinishTranslationSession(int sessionId);
 
     /**
-     * TODO: fill in javadoc.
-     *
-     * @param request
-     * @param sessionId
-     * @param callback
-     * @param cancellationSignal
      * @removed use
      * {@link #onTranslationRequest(TranslationRequest, int, CancellationSignal, Consumer)} instead.
      */
@@ -276,23 +274,29 @@
      * {@link TranslationRequest#FLAG_PARTIAL_RESPONSES} was set, the service may call
      * {@code callback.accept()} multiple times with partial responses.</p>
      *
-     * @param request
-     * @param sessionId
-     * @param callback
-     * @param cancellationSignal
+     * @param request The translation request containing the data to be translated.
+     * @param sessionId id of the session that sent the translation request.
+     * @param cancellationSignal A {@link CancellationSignal} that notifies when a client has
+     *                           cancelled the operation in progress.
+     * @param callback {@link Consumer} to pass back the translation response.
      */
     public abstract void onTranslationRequest(@NonNull TranslationRequest request, int sessionId,
             @Nullable CancellationSignal cancellationSignal,
             @NonNull Consumer<TranslationResponse> callback);
 
     /**
-     * TODO: fill in javadoc
+     * Called to request a set of {@link TranslationCapability}s that are supported by the service.
+     *
+     * <p>The set of translation capabilities are limited to those supporting the source and target
+     * {@link TranslationSpec.DataFormat}. e.g. Calling this with
+     * {@link TranslationSpec#DATA_FORMAT_TEXT} as source and target returns only capabilities that
+     * translates text to text.</p>
      *
      * <p>Must call {@code callback.accept} to pass back the set of translation capabilities.</p>
      *
-     * @param sourceFormat
-     * @param targetFormat
-     * @param callback
+     * @param sourceFormat data format restriction of the translation source spec.
+     * @param targetFormat data format restriction of the translation target spec.
+     * @param callback {@link Consumer} to pass back the set of translation capabilities.
      */
     public abstract void onTranslationCapabilitiesRequest(
             @TranslationSpec.DataFormat int sourceFormat,
diff --git a/core/java/android/view/translation/TranslationCapability.java b/core/java/android/view/translation/TranslationCapability.java
index 65b749a..b7e13dd 100644
--- a/core/java/android/view/translation/TranslationCapability.java
+++ b/core/java/android/view/translation/TranslationCapability.java
@@ -40,15 +40,18 @@
 public final class TranslationCapability implements Parcelable {
 
     /**
-     * TODO: fill in javadoc
+     * The translation service supports translation between the source and target specs, and it is
+     * ready to be downloaded onto the device.
      */
     public static final @ModelState int STATE_AVAILABLE_TO_DOWNLOAD = 1;
     /**
-     * TODO: fill in javadoc
+     * The translation service supports translation between the source and target specs, and it is
+     * being downloaded onto the device currently.
      */
     public static final @ModelState int STATE_DOWNLOADING = 2;
     /**
-     * TODO: fill in javadoc
+     * The translation service supports translation between the source and target specs, and it is
+     * downloaded and ready to use on device.
      */
     public static final @ModelState int STATE_ON_DEVICE = 3;
     /**
@@ -305,7 +308,7 @@
     };
 
     @DataClass.Generated(
-            time = 1624307114468L,
+            time = 1629158466039L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/translation/TranslationCapability.java",
             inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_NOT_AVAILABLE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_REMOVED_AND_AVAILABLE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final  boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)")
diff --git a/core/java/android/view/translation/TranslationRequest.java b/core/java/android/view/translation/TranslationRequest.java
index df4836e..0d41851 100644
--- a/core/java/android/view/translation/TranslationRequest.java
+++ b/core/java/android/view/translation/TranslationRequest.java
@@ -39,12 +39,16 @@
     public static final @RequestFlags int FLAG_TRANSLATION_RESULT = 0x1;
     /**
      * Indicates this request wants to receive the dictionary result.
-     * TODO: describe the structure of the result.
+     *
+     * <p>See {@link TranslationResponseValue#EXTRA_DEFINITIONS} for more detail on the structure
+     * of the returned data.
      */
     public static final @RequestFlags int FLAG_DICTIONARY_RESULT = 0x2;
     /**
      * Indicates this request wants to receive the transliteration result.
-     * TODO: describe the structure of the result.
+     *
+     * <p>This returns a CharSequence representation of the transliteration of the translated text.
+     *  See {@link TranslationResponseValue#getTransliteration()}.
      */
     public static final @RequestFlags int FLAG_TRANSLITERATION_RESULT = 0x4;
     /**
@@ -327,7 +331,8 @@
             return this;
         }
 
-        /** @see #setTranslationRequestValues
+        /**
+         * @see #setTranslationRequestValues
          * @removed
          */
         @DataClass.Generated.Member
@@ -352,7 +357,8 @@
             return this;
         }
 
-        /** @see #setViewTranslationRequests
+        /**
+         * @see #setViewTranslationRequests
          * @removed
          */
         @DataClass.Generated.Member
@@ -394,7 +400,7 @@
     }
 
     @DataClass.Generated(
-            time = 1620429997487L,
+            time = 1629159107226L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/translation/TranslationRequest.java",
             inputSignatures = "public static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_DICTIONARY_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLITERATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_PARTIAL_RESPONSES\nprivate final @android.view.translation.TranslationRequest.RequestFlags int mFlags\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationRequestValue\") java.util.List<android.view.translation.TranslationRequestValue> mTranslationRequestValues\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"viewTranslationRequest\") java.util.List<android.view.translation.ViewTranslationRequest> mViewTranslationRequests\nprivate static  int defaultFlags()\nprivate static  java.util.List<android.view.translation.TranslationRequestValue> defaultTranslationRequestValues()\nprivate static  java.util.List<android.view.translation.ViewTranslationRequest> defaultViewTranslationRequests()\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addTranslationRequestValue(android.view.translation.TranslationRequestValue)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addViewTranslationRequest(android.view.translation.ViewTranslationRequest)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genBuilder=true)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addTranslationRequestValue(android.view.translation.TranslationRequestValue)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addViewTranslationRequest(android.view.translation.ViewTranslationRequest)\nclass BaseBuilder extends java.lang.Object implements []")
diff --git a/core/java/android/view/translation/TranslationResponseValue.java b/core/java/android/view/translation/TranslationResponseValue.java
index a24dbc3..9dff2d5 100644
--- a/core/java/android/view/translation/TranslationResponseValue.java
+++ b/core/java/android/view/translation/TranslationResponseValue.java
@@ -93,9 +93,11 @@
     @NonNull
     private final Bundle mExtras;
 
+    // TODO: Add example of transliteration.
     /**
      * The transliteration result of the translated text.
-     * TODO: Describe the result structure.
+     *
+     * <p>This returns a CharSequence representation of the transliteration of the translated text.
      */
     @Nullable
     private final CharSequence mTransliteration;
@@ -223,7 +225,8 @@
 
     /**
      * The transliteration result of the translated text.
-     * TODO: Describe the result structure.
+     *
+     * <p>This returns a CharSequence representation of the transliteration of the translated text.
      */
     @DataClass.Generated.Member
     public @Nullable CharSequence getTransliteration() {
@@ -407,7 +410,8 @@
 
         /**
          * The transliteration result of the translated text.
-         * TODO: Describe the result structure.
+         *
+         * <p>This returns a CharSequence representation of the transliteration of the translated text.
          */
         @DataClass.Generated.Member
         public @NonNull Builder setTransliteration(@NonNull CharSequence value) {
@@ -448,7 +452,7 @@
     }
 
     @DataClass.Generated(
-            time = 1622133051937L,
+            time = 1631057245846L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/translation/TranslationResponseValue.java",
             inputSignatures = "public static final  int STATUS_SUCCESS\npublic static final  int STATUS_ERROR\npublic static final  java.lang.String EXTRA_DEFINITIONS\nprivate final @android.view.translation.TranslationResponseValue.Status int mStatusCode\nprivate final @android.annotation.Nullable java.lang.CharSequence mText\nprivate final @android.annotation.NonNull android.os.Bundle mExtras\nprivate final @android.annotation.Nullable java.lang.CharSequence mTransliteration\npublic static @android.annotation.NonNull android.view.translation.TranslationResponseValue forError()\nprivate static  java.lang.CharSequence defaultText()\nprivate static  android.os.Bundle defaultExtras()\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate static  java.lang.CharSequence defaultTransliteration()\nclass TranslationResponseValue extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)\nclass BaseBuilder extends java.lang.Object implements []")
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 8e7fae7..d12c870 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -50,6 +50,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A class that allows the app to get the frame metrics from HardwareRendererObserver.
@@ -103,6 +104,7 @@
     private boolean mCancelled = false;
     private FrameTrackerListener mListener;
     private boolean mTracingStarted = false;
+    private Runnable mWaitForFinishTimedOut;
 
     private static class JankInfo {
         long frameVsyncId;
@@ -263,10 +265,16 @@
             if (mListener != null) {
                 mListener.onCujEvents(mSession, ACTION_SESSION_END);
             }
+            // We don't remove observer here,
+            // will remove it when all the frame metrics in this duration are called back.
+            // See onFrameMetricsAvailable for the logic of removing the observer.
+            // Waiting at most 10 seconds for all callbacks to finish.
+            mWaitForFinishTimedOut = () -> {
+                Log.e(TAG, "force finish cuj because of time out:" + mSession.getName());
+                finish(mJankInfos.size() - 1);
+            };
+            mHandler.postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10));
         }
-        // We don't remove observer here,
-        // will remove it when all the frame metrics in this duration are called back.
-        // See onFrameMetricsAvailable for the logic of removing the observer.
     }
 
     /**
@@ -396,7 +404,8 @@
     }
 
     private void finish(int indexOnOrAfterEnd) {
-
+        mHandler.removeCallbacks(mWaitForFinishTimedOut);
+        mWaitForFinishTimedOut = null;
         mMetricsFinalized = true;
 
         // The tracing has been ended, remove the observer, see if need to trigger perfetto.
@@ -481,7 +490,7 @@
             }
         }
         if (DEBUG) {
-            Log.i(TAG, "FrameTracker: CUJ=" + mSession.getName()
+            Log.i(TAG, "finish: CUJ=" + mSession.getName()
                     + " (" + mBeginVsyncId + "," + mEndVsyncId + ")"
                     + " totalFrames=" + totalFramesCount
                     + " missedAppFrames=" + missedAppFramesCount
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index aabcd7f..610cd73 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -103,7 +103,7 @@
     private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName();
 
     private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
-    private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
+    private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2L);
     private static final String SETTINGS_ENABLED_KEY = "enabled";
     private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
     private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY =
diff --git a/media/OWNERS b/media/OWNERS
index abfc8bf..0aff43e 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,8 +1,7 @@
-chz@google.com
+# Bug component: 1344
 elaurent@google.com
 essick@google.com
 etalvala@google.com
-gkasten@google.com
 hdmoon@google.com
 hkuang@google.com
 hunga@google.com
@@ -13,16 +12,13 @@
 jsharkey@android.com
 klhyun@google.com
 lajos@google.com
-marcone@google.com
 nchalko@google.com
 philburk@google.com
 quxiangfang@google.com
 wonsik@google.com
 
-# LON
-andrewlewis@google.com
-aquilescanta@google.com
-olly@google.com
+# go/android-fwk-media-solutions for info on areas of ownership.
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
 
 # SEO
 sungsoo@google.com
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index cf06fad..813dee3 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -1,9 +1,9 @@
 # Bug component: 1344
-
 fgoldfain@google.com
 elaurent@google.com
 lajos@google.com
-olly@google.com
-andrewlewis@google.com
 sungsoo@google.com
 jmtrivi@google.com
+
+# go/android-fwk-media-solutions for info on areas of ownership.
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index c1a0a9a..b4cafd8 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -93,9 +93,9 @@
             final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
             setTitle(Html.fromHtml(getString(
                     R.string.confirmation_title,
-                    getCallingAppName(),
-                    profileName,
-                    selectedDevice.getDisplayName()), 0));
+                    Html.escapeHtml(getCallingAppName()),
+                    Html.escapeHtml(selectedDevice.getDisplayName())), 0));
+
             mPairButton = findViewById(R.id.button_pair);
             mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice));
             getService().mSelectedDevice = selectedDevice;
@@ -108,8 +108,8 @@
             mPairButton = findViewById(R.id.button_pair);
             mPairButton.setVisibility(View.GONE);
             setTitle(Html.fromHtml(getString(R.string.chooser_title,
-                    profileName,
-                    getCallingAppName()), 0));
+                    Html.escapeHtml(profileName),
+                    Html.escapeHtml(getCallingAppName())), 0));
             mDeviceListView = findViewById(R.id.device_list);
             mDevicesAdapter = new DevicesAdapter();
             mDeviceListView.setAdapter(mDevicesAdapter);
diff --git a/packages/SystemUI/res/layout/smart_action_button.xml b/packages/SystemUI/res/layout/smart_action_button.xml
index 4e5785d..488be3a 100644
--- a/packages/SystemUI/res/layout/smart_action_button.xml
+++ b/packages/SystemUI/res/layout/smart_action_button.xml
@@ -29,8 +29,8 @@
         android:textSize="@dimen/smart_reply_button_font_size"
         android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
         android:textColor="@color/smart_reply_button_text"
-        android:paddingStart="@dimen/smart_reply_button_action_padding_left"
-        android:paddingEnd="@dimen/smart_reply_button_padding_horizontal"
+        android:paddingLeft="@dimen/smart_reply_button_action_padding_left"
+        android:paddingRight="@dimen/smart_reply_button_padding_horizontal"
         android:drawablePadding="@dimen/smart_action_button_icon_padding"
         android:textStyle="normal"
         android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml
index b24362f..ddf16e0 100644
--- a/packages/SystemUI/res/layout/smart_reply_button.xml
+++ b/packages/SystemUI/res/layout/smart_reply_button.xml
@@ -31,7 +31,7 @@
         android:textSize="@dimen/smart_reply_button_font_size"
         android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
         android:textColor="@color/smart_reply_button_text"
-        android:paddingStart="@dimen/smart_reply_button_padding_horizontal"
-        android:paddingEnd="@dimen/smart_reply_button_padding_horizontal"
+        android:paddingLeft="@dimen/smart_reply_button_padding_horizontal"
+        android:paddingRight="@dimen/smart_reply_button_padding_horizontal"
         android:textStyle="normal"
         android:ellipsize="none"/>
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 0a28b47..ba99f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -212,8 +212,8 @@
         mediaDataCombineLatest.addListener(mediaDataFilter)
 
         // Set up links back into the pipeline for listeners that need to send events upstream.
-        mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean ->
-            setTimedOut(token, timedOut) }
+        mediaTimeoutListener.timeoutCallback = { key: String, timedOut: Boolean ->
+            setTimedOut(key, timedOut) }
         mediaResumeListener.setManager(this)
         mediaDataFilter.mediaDataManager = this
 
@@ -414,14 +414,18 @@
      * This will make the player not active anymore, hiding it from QQS and Keyguard.
      * @see MediaData.active
      */
-    internal fun setTimedOut(token: String, timedOut: Boolean, forceUpdate: Boolean = false) {
-        mediaEntries[token]?.let {
+    internal fun setTimedOut(key: String, timedOut: Boolean, forceUpdate: Boolean = false) {
+        mediaEntries[key]?.let {
             if (it.active == !timedOut && !forceUpdate) {
+                if (it.resumption) {
+                    if (DEBUG) Log.d(TAG, "timing out resume player $key")
+                    dismissMediaData(key, 0L /* delay */)
+                }
                 return
             }
             it.active = !timedOut
-            if (DEBUG) Log.d(TAG, "Updating $token timedOut: $timedOut")
-            onMediaDataLoaded(token, token, it)
+            if (DEBUG) Log.d(TAG, "Updating $key timedOut: $timedOut")
+            onMediaDataLoaded(key, key, it)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index ab568c8..608c784 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.Utils
+import com.android.systemui.util.time.SystemClock
 import java.io.FileDescriptor
 import java.io.PrintWriter
 import java.util.concurrent.ConcurrentLinkedQueue
@@ -53,11 +54,13 @@
     @Background private val backgroundExecutor: Executor,
     private val tunerService: TunerService,
     private val mediaBrowserFactory: ResumeMediaBrowserFactory,
-    dumpManager: DumpManager
+    dumpManager: DumpManager,
+    private val systemClock: SystemClock
 ) : MediaDataManager.Listener, Dumpable {
 
     private var useMediaResumption: Boolean = Utils.useMediaResumption(context)
-    private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue()
+    private val resumeComponents: ConcurrentLinkedQueue<Pair<ComponentName, Long>> =
+            ConcurrentLinkedQueue()
 
     private lateinit var mediaDataManager: MediaDataManager
 
@@ -131,14 +134,32 @@
         val listString = prefs.getString(MEDIA_PREFERENCE_KEY + currentUserId, null)
         val components = listString?.split(ResumeMediaBrowser.DELIMITER.toRegex())
             ?.dropLastWhile { it.isEmpty() }
+        var needsUpdate = false
         components?.forEach {
             val info = it.split("/")
             val packageName = info[0]
             val className = info[1]
             val component = ComponentName(packageName, className)
-            resumeComponents.add(component)
+
+            val lastPlayed = if (info.size == 3) {
+                try {
+                    info[2].toLong()
+                } catch (e: NumberFormatException) {
+                    needsUpdate = true
+                    systemClock.currentTimeMillis()
+                }
+            } else {
+                needsUpdate = true
+                systemClock.currentTimeMillis()
+            }
+            resumeComponents.add(component to lastPlayed)
         }
         Log.d(TAG, "loaded resume components ${resumeComponents.toArray().contentToString()}")
+
+        if (needsUpdate) {
+            // Save any missing times that we had to fill in
+            writeSharedPrefs()
+        }
     }
 
     /**
@@ -149,9 +170,12 @@
             return
         }
 
+        val now = systemClock.currentTimeMillis()
         resumeComponents.forEach {
-            val browser = mediaBrowserFactory.create(mediaBrowserCallback, it)
-            browser.findRecentMedia()
+            if (now.minus(it.second) <= RESUME_MEDIA_TIMEOUT) {
+                val browser = mediaBrowserFactory.create(mediaBrowserCallback, it.first)
+                browser.findRecentMedia()
+            }
         }
     }
 
@@ -234,18 +258,24 @@
      */
     private fun updateResumptionList(componentName: ComponentName) {
         // Remove if exists
-        resumeComponents.remove(componentName)
+        resumeComponents.remove(resumeComponents.find { it.first.equals(componentName) })
         // Insert at front of queue
-        resumeComponents.add(componentName)
+        val currentTime = systemClock.currentTimeMillis()
+        resumeComponents.add(componentName to currentTime)
         // Remove old components if over the limit
         if (resumeComponents.size > ResumeMediaBrowser.MAX_RESUMPTION_CONTROLS) {
             resumeComponents.remove()
         }
 
-        // Save changes
+        writeSharedPrefs()
+    }
+
+    private fun writeSharedPrefs() {
         val sb = StringBuilder()
         resumeComponents.forEach {
-            sb.append(it.flattenToString())
+            sb.append(it.first.flattenToString())
+            sb.append("/")
+            sb.append(it.second)
             sb.append(ResumeMediaBrowser.DELIMITER)
         }
         val prefs = context.getSharedPreferences(MEDIA_PREFERENCES, Context.MODE_PRIVATE)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 9a39193..6f04771 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -20,6 +20,7 @@
 import android.media.session.PlaybackState
 import android.os.SystemProperties
 import android.util.Log
+import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
@@ -29,9 +30,15 @@
 
 private const val DEBUG = true
 private const val TAG = "MediaTimeout"
-private val PAUSED_MEDIA_TIMEOUT = SystemProperties
+
+@VisibleForTesting
+val PAUSED_MEDIA_TIMEOUT = SystemProperties
         .getLong("debug.sysui.media_timeout", TimeUnit.MINUTES.toMillis(10))
 
+@VisibleForTesting
+val RESUME_MEDIA_TIMEOUT = SystemProperties
+        .getLong("debug.sysui.media_timeout_resume", TimeUnit.DAYS.toMillis(3))
+
 /**
  * Controller responsible for keeping track of playback states and expiring inactive streams.
  */
@@ -45,8 +52,9 @@
 
     /**
      * Callback representing that a media object is now expired:
-     * @param token Media session unique identifier
-     * @param pauseTimeout True when expired for {@code PAUSED_MEDIA_TIMEOUT}
+     * @param key Media control unique identifier
+     * @param timedOut True when expired for {@code PAUSED_MEDIA_TIMEOUT} for active media,
+     *                 or {@code RESUME_MEDIA_TIMEOUT} for resume media
      */
     lateinit var timeoutCallback: (String, Boolean) -> Unit
 
@@ -122,6 +130,7 @@
 
         var timedOut = false
         var playing: Boolean? = null
+        var resumption: Boolean? = null
         var destroyed = false
 
         var mediaData: MediaData = data
@@ -159,12 +168,19 @@
         }
 
         override fun onSessionDestroyed() {
-            // If the session is destroyed, the controller is no longer valid, and we will need to
-            // recreate it if this key is updated later
             if (DEBUG) {
                 Log.d(TAG, "Session destroyed for $key")
             }
-            destroy()
+
+            if (resumption == true) {
+                // Some apps create a session when MBS is queried. We should unregister the
+                // controller since it will no longer be valid, but don't cancel the timeout
+                mediaController?.unregisterCallback(this)
+            } else {
+                // For active controls, if the session is destroyed, clean up everything since we
+                // will need to recreate it if this key is updated later
+                destroy()
+            }
         }
 
         private fun processState(state: PlaybackState?, dispatchEvents: Boolean) {
@@ -173,20 +189,28 @@
             }
 
             val isPlaying = state != null && isPlayingState(state.state)
-            if (playing == isPlaying && playing != null) {
+            val resumptionChanged = resumption != mediaData.resumption
+            if (playing == isPlaying && playing != null && !resumptionChanged) {
                 return
             }
             playing = isPlaying
+            resumption = mediaData.resumption
 
             if (!isPlaying) {
                 if (DEBUG) {
-                    Log.v(TAG, "schedule timeout for $key")
+                    Log.v(TAG, "schedule timeout for $key playing $isPlaying, $resumption")
                 }
-                if (cancellation != null) {
+                if (cancellation != null && !resumptionChanged) {
+                    // if the media changed resume state, we'll need to adjust the timeout length
                     if (DEBUG) Log.d(TAG, "cancellation already exists, continuing.")
                     return
                 }
-                expireMediaTimeout(key, "PLAYBACK STATE CHANGED - $state")
+                expireMediaTimeout(key, "PLAYBACK STATE CHANGED - $state, $resumption")
+                val timeout = if (mediaData.resumption) {
+                    RESUME_MEDIA_TIMEOUT
+                } else {
+                    PAUSED_MEDIA_TIMEOUT
+                }
                 cancellation = mainExecutor.executeDelayed({
                     cancellation = null
                     if (DEBUG) {
@@ -195,7 +219,7 @@
                     timedOut = true
                     // this event is async, so it's safe even when `dispatchEvents` is false
                     timeoutCallback(key, timedOut)
-                }, PAUSED_MEDIA_TIMEOUT)
+                }, timeout)
             } else {
                 expireMediaTimeout(key, "playback started - $state, $key")
                 timedOut = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index eb5f82c..445715e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -723,15 +723,13 @@
     }
 
     protected String computePowerIndication() {
-        if (mPowerCharged) {
-            return mContext.getResources().getString(R.string.keyguard_charged);
-        }
-
         int chargingId;
-        String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f);
         if (mBatteryOverheated) {
             chargingId = R.string.keyguard_plugged_in_charging_limited;
+            String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f);
             return mContext.getResources().getString(chargingId, percentage);
+        } else if (mPowerCharged) {
+            return mContext.getResources().getString(R.string.keyguard_charged);
         }
 
         final boolean hasChargingTime = mChargingTimeRemaining > 0;
@@ -759,6 +757,7 @@
                     : R.string.keyguard_plugged_in_wireless;
         }
 
+        String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f);
         if (hasChargingTime) {
             String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
                     mContext, mChargingTimeRemaining);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 71546ae..d432f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.util.concurrency.Execution
 import com.android.systemui.util.settings.SecureSettings
 import java.lang.RuntimeException
@@ -67,6 +68,7 @@
     private val contentResolver: ContentResolver,
     private val configurationController: ConfigurationController,
     private val statusBarStateController: StatusBarStateController,
+    private val deviceProvisionedController: DeviceProvisionedController,
     private val execution: Execution,
     @Main private val uiExecutor: Executor,
     @Main private val handler: Handler,
@@ -83,6 +85,55 @@
     private var showSensitiveContentForManagedUser = false
     private var managedUserHandle: UserHandle? = null
 
+    private val deviceProvisionedListener =
+        object : DeviceProvisionedController.DeviceProvisionedListener {
+            override fun onDeviceProvisionedChanged() {
+                connectSession()
+            }
+
+            override fun onUserSetupChanged() {
+                connectSession()
+            }
+        }
+
+    private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
+        execution.assertIsMainThread()
+        val filteredTargets = targets.filter(::filterSmartspaceTarget)
+        plugin?.onTargetsAvailable(filteredTargets)
+    }
+
+    private val userTrackerCallback = object : UserTracker.Callback {
+        override fun onUserChanged(newUser: Int, userContext: Context) {
+            execution.assertIsMainThread()
+            reloadSmartspace()
+        }
+    }
+
+    private val settingsObserver = object : ContentObserver(handler) {
+        override fun onChange(selfChange: Boolean, uri: Uri?) {
+            execution.assertIsMainThread()
+            reloadSmartspace()
+        }
+    }
+
+    private val configChangeListener = object : ConfigurationController.ConfigurationListener {
+        override fun onThemeChanged() {
+            execution.assertIsMainThread()
+            updateTextColorFromWallpaper()
+        }
+    }
+
+    private val statusBarStateListener = object : StatusBarStateController.StateListener {
+        override fun onDozeAmountChanged(linear: Float, eased: Float) {
+            execution.assertIsMainThread()
+            smartspaceView.setDozeAmount(eased)
+        }
+    }
+
+    init {
+        deviceProvisionedController.addCallback(deviceProvisionedListener)
+    }
+
     fun isEnabled(): Boolean {
         execution.assertIsMainThread()
 
@@ -144,10 +195,20 @@
         if (plugin == null || session != null) {
             return
         }
-        val session = smartspaceManager.createSmartspaceSession(
-                SmartspaceConfig.Builder(context, "lockscreen").build())
-        session.addOnTargetsAvailableListener(uiExecutor, sessionListener)
 
+        // Only connect after the device is fully provisioned to avoid connection caching
+        // issues
+        if (!deviceProvisionedController.isDeviceProvisioned() ||
+                !deviceProvisionedController.isCurrentUserSetup()) {
+            return
+        }
+
+        val newSession = smartspaceManager.createSmartspaceSession(
+                SmartspaceConfig.Builder(context, "lockscreen").build())
+        newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+        this.session = newSession
+
+        deviceProvisionedController.removeCallback(deviceProvisionedListener)
         userTracker.addCallback(userTrackerCallback, uiExecutor)
         contentResolver.registerContentObserver(
                 secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -158,8 +219,6 @@
         configurationController.addCallback(configChangeListener)
         statusBarStateController.addCallback(statusBarStateListener)
 
-        this.session = session
-
         reloadSmartspace()
     }
 
@@ -198,43 +257,6 @@
         plugin?.unregisterListener(listener)
     }
 
-    private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
-        execution.assertIsMainThread()
-        val filteredTargets = targets.filter(::filterSmartspaceTarget)
-        plugin?.onTargetsAvailable(filteredTargets)
-    }
-
-    private val userTrackerCallback = object : UserTracker.Callback {
-        override fun onUserChanged(newUser: Int, userContext: Context) {
-            execution.assertIsMainThread()
-            reloadSmartspace()
-        }
-
-        override fun onProfilesChanged(profiles: List<UserInfo>) {
-        }
-    }
-
-    private val settingsObserver = object : ContentObserver(handler) {
-        override fun onChange(selfChange: Boolean, uri: Uri?) {
-            execution.assertIsMainThread()
-            reloadSmartspace()
-        }
-    }
-
-    private val configChangeListener = object : ConfigurationController.ConfigurationListener {
-        override fun onThemeChanged() {
-            execution.assertIsMainThread()
-            updateTextColorFromWallpaper()
-        }
-    }
-
-    private val statusBarStateListener = object : StatusBarStateController.StateListener {
-        override fun onDozeAmountChanged(linear: Float, eased: Float) {
-            execution.assertIsMainThread()
-            smartspaceView.setDozeAmount(eased)
-        }
-    }
-
     private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
         return when (t.userHandle) {
             userTracker.userHandle -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
index 21d0338..b563d86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
@@ -311,7 +311,7 @@
                             setBounds(0, 0, newIconSize, newIconSize)
                         }
                 // Add the action icon to the Smart Action button.
-                setCompoundDrawablesRelative(iconDrawable, null, null, null)
+                setCompoundDrawables(iconDrawable, null, null, null)
 
                 val onClickListener = View.OnClickListener {
                     onSmartActionClick(entry, smartActions, actionIndex, action)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index a3e27be..4e33529 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -503,15 +503,15 @@
     }
 
     /**
-     * Returns the combined width of the start drawable (the action icon) and the padding between
-     * the drawable and the button text.
+     * Returns the combined width of the left drawable (the action icon) and the padding between the
+     * drawable and the button text.
      */
-    private int getStartCompoundDrawableWidthWithPadding(Button button) {
-        Drawable[] drawables = button.getCompoundDrawablesRelative();
-        Drawable startDrawable = drawables[0];
-        if (startDrawable == null) return 0;
+    private int getLeftCompoundDrawableWidthWithPadding(Button button) {
+        Drawable[] drawables = button.getCompoundDrawables();
+        Drawable leftDrawable = drawables[0];
+        if (leftDrawable == null) return 0;
 
-        return startDrawable.getBounds().width() + button.getCompoundDrawablePadding();
+        return leftDrawable.getBounds().width() + button.getCompoundDrawablePadding();
     }
 
     private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) {
@@ -520,8 +520,8 @@
         // Re-measure the squeezed smart reply button.
         clearLayoutLineCount(button);
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                button.getPaddingStart() + button.getPaddingEnd() + textWidth
-                      + getStartCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
+                button.getPaddingLeft() + button.getPaddingRight() + textWidth
+                      + getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
         button.measure(widthMeasureSpec, heightMeasureSpec);
 
         final int newWidth = button.getMeasuredWidth();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index ba6dfd3..5c3108c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -163,7 +163,7 @@
     }
 
     @Test
-    fun testSetTimedOut_deactivatesMedia() {
+    fun testSetTimedOut_active_deactivatesMedia() {
         val data = MediaData(userId = USER_ID, initialized = true, backgroundColor = 0, app = null,
                 appIcon = null, artist = null, song = null, artwork = null, actions = emptyList(),
                 actionsToShowInCompact = emptyList(), packageName = "INVALID", token = null,
@@ -176,6 +176,25 @@
     }
 
     @Test
+    fun testSetTimedOut_resume_dismissesMedia() {
+        // WHEN resume controls are present, and time out
+        val desc = MediaDescription.Builder().run {
+            setTitle(SESSION_TITLE)
+            build()
+        }
+        mediaDataManager.addResumptionControls(USER_ID, desc, Runnable {}, session.sessionToken,
+                APP_NAME, pendingIntent, PACKAGE_NAME)
+        backgroundExecutor.runAllReady()
+        foregroundExecutor.runAllReady()
+        mediaDataManager.setTimedOut(PACKAGE_NAME, timedOut = true)
+
+        // THEN it is removed and listeners are informed
+        foregroundExecutor.advanceClockToLast()
+        foregroundExecutor.runAllReady()
+        verify(listener).onMediaDataRemoved(PACKAGE_NAME)
+    }
+
+    @Test
     fun testLoadsMetadataOnBackground() {
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
         assertThat(backgroundExecutor.numPending()).isEqualTo(1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index 150f4545..359746b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -91,10 +91,12 @@
 
     @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback>
     @Captor lateinit var actionCaptor: ArgumentCaptor<Runnable>
+    @Captor lateinit var componentCaptor: ArgumentCaptor<String>
 
     private lateinit var executor: FakeExecutor
     private lateinit var data: MediaData
     private lateinit var resumeListener: MediaResumeListener
+    private val clock = FakeSystemClock()
 
     private var originalQsSetting = Settings.Global.getInt(context.contentResolver,
         Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1)
@@ -122,9 +124,9 @@
         whenever(mockContext.packageManager).thenReturn(context.packageManager)
         whenever(mockContext.contentResolver).thenReturn(context.contentResolver)
 
-        executor = FakeExecutor(FakeSystemClock())
+        executor = FakeExecutor(clock)
         resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor,
-                tunerService, resumeBrowserFactory, dumpManager)
+                tunerService, resumeBrowserFactory, dumpManager, clock)
         resumeListener.setManager(mediaDataManager)
         mediaDataManager.addListener(resumeListener)
 
@@ -163,7 +165,7 @@
 
         // When listener is created, we do NOT register a user change listener
         val listener = MediaResumeListener(context, broadcastDispatcher, executor, tunerService,
-                resumeBrowserFactory, dumpManager)
+                resumeBrowserFactory, dumpManager, clock)
         listener.setManager(mediaDataManager)
         verify(broadcastDispatcher, never()).registerReceiver(eq(listener.userChangeReceiver),
             any(), any(), any())
@@ -328,4 +330,109 @@
         // Then we call restart
         verify(resumeBrowser).restart()
     }
+
+    @Test
+    fun testOnUserUnlock_missingTime_saves() {
+        val currentTime = clock.currentTimeMillis()
+
+        // When resume components without a last played time are loaded
+        testOnUserUnlock_loadsTracks()
+
+        // Then we save an update with the current time
+        verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
+        componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex())
+                ?.dropLastWhile { it.isEmpty() }.forEach {
+            val result = it.split("/")
+            assertThat(result.size).isEqualTo(3)
+            assertThat(result[2].toLong()).isEqualTo(currentTime)
+        }
+        verify(sharedPrefsEditor, times(1)).apply()
+    }
+
+    @Test
+    fun testLoadComponents_recentlyPlayed_adds() {
+        // Set up browser to return successfully
+        val description = MediaDescription.Builder().setTitle(TITLE).build()
+        val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+        whenever(resumeBrowser.token).thenReturn(token)
+        whenever(resumeBrowser.appIntent).thenReturn(pendingIntent)
+        whenever(resumeBrowser.findRecentMedia()).thenAnswer {
+            callbackCaptor.value.addTrack(description, component, resumeBrowser)
+        }
+
+        // Set up shared preferences to have a component with a recent lastplayed time
+        val lastPlayed = clock.currentTimeMillis()
+        val componentsString = "$PACKAGE_NAME/$CLASS_NAME/$lastPlayed:"
+        whenever(sharedPrefs.getString(any(), any())).thenReturn(componentsString)
+        val resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor,
+                tunerService, resumeBrowserFactory, dumpManager, clock)
+        resumeListener.setManager(mediaDataManager)
+        mediaDataManager.addListener(resumeListener)
+
+        // When we load a component that was played recently
+        val intent = Intent(Intent.ACTION_USER_UNLOCKED)
+        resumeListener.userChangeReceiver.onReceive(mockContext, intent)
+
+        // We add its resume controls
+        verify(resumeBrowser, times(1)).findRecentMedia()
+        verify(mediaDataManager, times(1)).addResumptionControls(anyInt(),
+                any(), any(), any(), any(), any(), eq(PACKAGE_NAME))
+    }
+
+    @Test
+    fun testLoadComponents_old_ignores() {
+        // Set up shared preferences to have a component with an old lastplayed time
+        val lastPlayed = clock.currentTimeMillis() - RESUME_MEDIA_TIMEOUT - 100
+        val componentsString = "$PACKAGE_NAME/$CLASS_NAME/$lastPlayed:"
+        whenever(sharedPrefs.getString(any(), any())).thenReturn(componentsString)
+        val resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor,
+                tunerService, resumeBrowserFactory, dumpManager, clock)
+        resumeListener.setManager(mediaDataManager)
+        mediaDataManager.addListener(resumeListener)
+
+        // When we load a component that is not recent
+        val intent = Intent(Intent.ACTION_USER_UNLOCKED)
+        resumeListener.userChangeReceiver.onReceive(mockContext, intent)
+
+        // We do not try to add resume controls
+        verify(resumeBrowser, times(0)).findRecentMedia()
+        verify(mediaDataManager, times(0)).addResumptionControls(anyInt(),
+                any(), any(), any(), any(), any(), any())
+    }
+
+    @Test
+    fun testOnLoad_hasService_updatesLastPlayed() {
+        // Set up browser to return successfully
+        val description = MediaDescription.Builder().setTitle(TITLE).build()
+        val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+        whenever(resumeBrowser.token).thenReturn(token)
+        whenever(resumeBrowser.appIntent).thenReturn(pendingIntent)
+        whenever(resumeBrowser.findRecentMedia()).thenAnswer {
+            callbackCaptor.value.addTrack(description, component, resumeBrowser)
+        }
+
+        // Set up shared preferences to have a component with a lastplayed time
+        val currentTime = clock.currentTimeMillis()
+        val lastPlayed = currentTime - 1000
+        val componentsString = "$PACKAGE_NAME/$CLASS_NAME/$lastPlayed:"
+        whenever(sharedPrefs.getString(any(), any())).thenReturn(componentsString)
+        val resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor,
+                tunerService, resumeBrowserFactory, dumpManager, clock)
+        resumeListener.setManager(mediaDataManager)
+        mediaDataManager.addListener(resumeListener)
+
+        // When media data is loaded that has not been checked yet, and does have a MBS
+        val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
+        resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+
+        // Then we store the new lastPlayed time
+        verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
+        componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex())
+                ?.dropLastWhile { it.isEmpty() }.forEach {
+                    val result = it.split("/")
+                    assertThat(result.size).isEqualTo(3)
+                    assertThat(result[2].toLong()).isEqualTo(currentTime)
+                }
+        verify(sharedPrefsEditor, times(1)).apply()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index 0a573cd6..de2235d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -71,6 +71,7 @@
     private lateinit var playbackBuilder: PlaybackState.Builder
     private lateinit var session: MediaSession
     private lateinit var mediaData: MediaData
+    private lateinit var resumeData: MediaData
     private lateinit var mediaTimeoutListener: MediaTimeoutListener
 
     @Before
@@ -97,6 +98,10 @@
         mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null,
             emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null,
             device = null, active = true, resumeAction = null)
+
+        resumeData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null,
+                emptyList(), emptyList(), PACKAGE, null, clickIntent = null,
+                device = null, active = false, resumeAction = null, resumption = true)
     }
 
     @Test
@@ -120,6 +125,7 @@
         verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
         assertThat(executor.numPending()).isEqualTo(1)
         verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
+        assertThat(executor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT)
     }
 
     @Test
@@ -188,6 +194,7 @@
         mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder()
                 .setState(PlaybackState.STATE_PAUSED, 0L, 0f).build())
         assertThat(executor.numPending()).isEqualTo(1)
+        assertThat(executor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT)
     }
 
     @Test
@@ -229,7 +236,7 @@
     }
 
     @Test
-    fun testOnSessionDestroyed_clearsTimeout() {
+    fun testOnSessionDestroyed_active_clearsTimeout() {
         // GIVEN media that is paused
         val mediaPaused = mediaData.copy(isPlaying = false)
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaPaused)
@@ -247,7 +254,7 @@
     @Test
     fun testSessionDestroyed_thenRestarts_resetsTimeout() {
         // Assuming we have previously destroyed the session
-        testOnSessionDestroyed_clearsTimeout()
+        testOnSessionDestroyed_active_clearsTimeout()
 
         // WHEN we get an update with media playing
         val playingState = mock(android.media.session.PlaybackState::class.java)
@@ -264,4 +271,91 @@
         }
         verify(timeoutCallback).invoke(eq(KEY), eq(false))
     }
+
+    @Test
+    fun testOnSessionDestroyed_resume_continuesTimeout() {
+        // GIVEN resume media with session info
+        val resumeWithSession = resumeData.copy(token = session.sessionToken)
+        mediaTimeoutListener.onMediaDataLoaded(PACKAGE, null, resumeWithSession)
+        verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
+        assertThat(executor.numPending()).isEqualTo(1)
+
+        // WHEN the session is destroyed
+        mediaCallbackCaptor.value.onSessionDestroyed()
+
+        // THEN the controller is unregistered, but the timeout is still scheduled
+        verify(mediaController).unregisterCallback(anyObject())
+        assertThat(executor.numPending()).isEqualTo(1)
+    }
+
+    @Test
+    fun testOnMediaDataLoaded_activeToResume_registersTimeout() {
+        // WHEN a regular media is loaded
+        mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+
+        // AND it turns into a resume control
+        mediaTimeoutListener.onMediaDataLoaded(PACKAGE, KEY, resumeData)
+
+        // THEN we register a timeout
+        assertThat(executor.numPending()).isEqualTo(1)
+        verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
+        assertThat(executor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT)
+    }
+
+    @Test
+    fun testOnMediaDataLoaded_pausedToResume_updatesTimeout() {
+        // WHEN regular media is paused
+        val pausedState = PlaybackState.Builder()
+                .setState(PlaybackState.STATE_PAUSED, 0L, 0f)
+                .build()
+        `when`(mediaController.playbackState).thenReturn(pausedState)
+        mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+        assertThat(executor.numPending()).isEqualTo(1)
+
+        // AND it turns into a resume control
+        mediaTimeoutListener.onMediaDataLoaded(PACKAGE, KEY, resumeData)
+
+        // THEN we update the timeout length
+        assertThat(executor.numPending()).isEqualTo(1)
+        verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
+        assertThat(executor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT)
+    }
+
+    @Test
+    fun testOnMediaDataLoaded_resumption_registersTimeout() {
+        // WHEN a resume media is loaded
+        mediaTimeoutListener.onMediaDataLoaded(PACKAGE, null, resumeData)
+
+        // THEN we register a timeout
+        assertThat(executor.numPending()).isEqualTo(1)
+        verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
+        assertThat(executor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT)
+    }
+
+    @Test
+    fun testOnMediaDataLoaded_resumeToActive_updatesTimeout() {
+        // WHEN we have a resume control
+        mediaTimeoutListener.onMediaDataLoaded(PACKAGE, null, resumeData)
+
+        // AND that media is resumed
+        val playingState = PlaybackState.Builder()
+                .setState(PlaybackState.STATE_PAUSED, 0L, 0f)
+                .build()
+        `when`(mediaController.playbackState).thenReturn(playingState)
+        mediaTimeoutListener.onMediaDataLoaded(KEY, PACKAGE, mediaData)
+
+        // THEN the timeout length is changed to a regular media control
+        assertThat(executor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT)
+    }
+
+    @Test
+    fun testOnMediaDataRemoved_resume_timeoutCancelled() {
+        // WHEN we have a resume control
+        testOnMediaDataLoaded_resumption_registersTimeout()
+        // AND the media is removed
+        mediaTimeoutListener.onMediaDataRemoved(PACKAGE)
+
+        // THEN the timeout runnable is cancelled
+        assertThat(executor.numPending()).isEqualTo(0)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 4c90063..09a3d35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -639,7 +639,7 @@
     }
 
     @Test
-    public void onRefreshBatteryInfo_fullChargedWithOverheat_presentCharged() {
+    public void onRefreshBatteryInfo_fullChargedWithOverheat_presentChargingLimited() {
         createController();
         BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
                 100 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
@@ -651,6 +651,24 @@
 
         verifyIndicationMessage(
                 INDICATION_TYPE_BATTERY,
+                mContext.getString(
+                        R.string.keyguard_plugged_in_charging_limited,
+                        NumberFormat.getPercentInstance().format(100 / 100f)));
+    }
+
+    @Test
+    public void onRefreshBatteryInfo_fullChargedWithoutOverheat_presentCharged() {
+        createController();
+        BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
+                100 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
+                BatteryManager.BATTERY_HEALTH_GOOD, 0 /* maxChargingWattage */,
+                true /* present */);
+
+        mController.getKeyguardCallback().onRefreshBatteryInfo(status);
+        mController.setVisible(true);
+
+        verifyIndicationMessage(
+                INDICATION_TYPE_BATTERY,
                 mContext.getString(R.string.keyguard_charged));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 116f807..efe6a31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -44,6 +44,8 @@
 import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener
 import com.android.systemui.util.concurrency.FakeExecution
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -90,6 +92,8 @@
     @Mock
     private lateinit var statusBarStateController: StatusBarStateController
     @Mock
+    private lateinit var deviceProvisionedController: DeviceProvisionedController
+    @Mock
     private lateinit var handler: Handler
 
     @Mock
@@ -107,12 +111,15 @@
     private lateinit var configChangeListenerCaptor: ArgumentCaptor<ConfigurationListener>
     @Captor
     private lateinit var statusBarStateListenerCaptor: ArgumentCaptor<StateListener>
+    @Captor
+    private lateinit var deviceProvisionedCaptor: ArgumentCaptor<DeviceProvisionedListener>
 
     private lateinit var sessionListener: OnTargetsAvailableListener
     private lateinit var userListener: UserTracker.Callback
     private lateinit var settingsObserver: ContentObserver
     private lateinit var configChangeListener: ConfigurationListener
     private lateinit var statusBarStateListener: StateListener
+    private lateinit var deviceProvisionedListener: DeviceProvisionedListener
 
     private val clock = FakeSystemClock()
     private val executor = FakeExecutor(clock)
@@ -144,6 +151,8 @@
         `when`(plugin.getView(any())).thenReturn(fakeSmartspaceView)
         `when`(userTracker.userProfiles).thenReturn(userList)
         `when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
+        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
+        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
 
         setActiveUser(userHandlePrimary)
         setAllowPrivateNotifications(userHandlePrimary, true)
@@ -161,11 +170,15 @@
                 contentResolver,
                 configurationController,
                 statusBarStateController,
+                deviceProvisionedController,
                 execution,
                 executor,
                 handler,
                 Optional.of(plugin)
                 )
+
+        verify(deviceProvisionedController).addCallback(capture(deviceProvisionedCaptor))
+        deviceProvisionedListener = deviceProvisionedCaptor.value
     }
 
     @Test(expected = RuntimeException::class)
@@ -180,6 +193,27 @@
     }
 
     @Test
+    fun connectOnlyAfterDeviceIsProvisioned() {
+        // GIVEN an unprovisioned device and an attempt to connect
+        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(false)
+        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(false)
+
+        // WHEN a connection attempt is made
+        controller.buildAndConnectView(fakeParent)
+
+        // THEN no session is created
+        verify(smartspaceManager, never()).createSmartspaceSession(any())
+
+        // WHEN it does become provisioned
+        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
+        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+        deviceProvisionedListener.onUserSetupChanged()
+
+        // THEN the session is created
+        verify(smartspaceManager).createSmartspaceSession(any())
+    }
+
+    @Test
     fun testListenersAreRegistered() {
         // GIVEN a listener is added after a session is created
         connectSession()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index ac04fa7e..32aee2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -581,6 +581,8 @@
         // devices.
         layout.setBaselineAligned(false);
 
+        final boolean isRtl = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+
         // Add smart replies
         Button previous = null;
         SmartReplyView.SmartReplies smartReplies =
@@ -600,7 +602,11 @@
             if (previous != null) {
                 ViewGroup.MarginLayoutParams lp =
                         (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
-                lp.setMarginEnd(mSpacing);
+                if (isRtl) {
+                    lp.leftMargin = mSpacing;
+                } else {
+                    lp.rightMargin = mSpacing;
+                }
             }
             layout.addView(current);
             previous = current;
@@ -624,7 +630,11 @@
             if (previous != null) {
                 ViewGroup.MarginLayoutParams lp =
                         (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
-                lp.setMarginEnd(mSpacing);
+                if (isRtl) {
+                    lp.leftMargin = mSpacing;
+                } else {
+                    lp.rightMargin = mSpacing;
+                }
             }
             layout.addView(current);
             previous = current;
@@ -923,8 +933,8 @@
                 .collect(Collectors.toList());
         Button singleLineButton = buttons.get(0);
         Button doubleLineButton = buttons.get(1);
-        Drawable singleLineDrawable = singleLineButton.getCompoundDrawablesRelative()[0]; // start
-        Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawablesRelative()[0]; // start
+        Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable
+        Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable
         assertEquals(singleLineDrawable.getBounds().width(),
                      doubleLineDrawable.getBounds().width());
         assertEquals(singleLineDrawable.getBounds().height(),
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index dc7904d..51066dd 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -25,9 +25,9 @@
 import static android.os.UserHandle.USER_SYSTEM;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
-import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -187,8 +187,6 @@
     private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
     private boolean mBinding;
     private boolean mUnbinding;
-    private int mWaitForEnableRetry;
-    private int mWaitForDisableRetry;
 
     private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
 
@@ -982,14 +980,15 @@
         if (mState == BluetoothAdapter.STATE_ON
                 || mState == BluetoothAdapter.STATE_BLE_ON
                 || mState == BluetoothAdapter.STATE_TURNING_ON
-                || mState == BluetoothAdapter.STATE_TURNING_OFF) {
-            Log.d(TAG, "enableBLE(): Bluetooth already enabled");
+                || mState == BluetoothAdapter.STATE_TURNING_OFF
+                || mState == BluetoothAdapter.STATE_BLE_TURNING_ON) {
+            Log.d(TAG, "enableBLE(): Bluetooth is already enabled or is turning on");
             return true;
         }
         synchronized (mReceiver) {
             // waive WRITE_SECURE_SETTINGS permission check
-            sendEnableMsg(false,
-                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
+            sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
+                    packageName, true);
         }
         return true;
     }
@@ -1802,6 +1801,8 @@
 
     private class BluetoothHandler extends Handler {
         boolean mGetNameAddressOnly = false;
+        private int mWaitForEnableRetry;
+        private int mWaitForDisableRetry;
 
         BluetoothHandler(Looper looper) {
             super(looper);
@@ -1852,11 +1853,12 @@
 
                 case MESSAGE_ENABLE:
                     int quietEnable = msg.arg1;
+                    int isBle  = msg.arg2;
                     if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED)
                             || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
                         // We are handling enable or disable right now, wait for it.
                         mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE,
-                                quietEnable, 0), ENABLE_DISABLE_DELAY_MS);
+                                quietEnable, isBle), ENABLE_DISABLE_DELAY_MS);
                         break;
                     }
 
@@ -1871,13 +1873,28 @@
                     try {
                         mBluetoothLock.readLock().lock();
                         if (mBluetooth != null) {
+                            boolean isHandled = true;
                             int state = mBluetooth.getState();
-                            if (state == BluetoothAdapter.STATE_BLE_ON) {
-                                Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
-                                mBluetooth.onLeServiceUp(mContext.getAttributionSource());
-                                persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-                                break;
+                            switch (state) {
+                                case BluetoothAdapter.STATE_BLE_ON:
+                                    if (isBle == 1) {
+                                        Slog.i(TAG, "Already at BLE_ON State");
+                                    } else {
+                                        Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
+                                        mBluetooth.onLeServiceUp(mContext.getAttributionSource());
+                                        persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
+                                    }
+                                    break;
+                                case BluetoothAdapter.STATE_BLE_TURNING_ON:
+                                case BluetoothAdapter.STATE_TURNING_ON:
+                                case BluetoothAdapter.STATE_ON:
+                                    Slog.i(TAG, "MESSAGE_ENABLE: already enabled");
+                                    break;
+                                default:
+                                    isHandled = false;
+                                    break;
                             }
+                            if (isHandled) break;
                         }
                     } catch (RemoteException e) {
                         Slog.e(TAG, "", e);
@@ -2643,7 +2660,12 @@
     }
 
     private void sendEnableMsg(boolean quietMode, int reason, String packageName) {
-        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0));
+        sendEnableMsg(quietMode, reason, packageName, false);
+    }
+
+    private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) {
+        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0,
+                  isBle ? 1 : 0));
         addActiveLog(reason, packageName, true);
         mLastEnabledTime = SystemClock.elapsedRealtime();
     }
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 3019439..5643873 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -215,7 +215,7 @@
     private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH =
             new DefaultCrossProfileIntentFilter.Builder(
                     DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
-                    /* flags= */0,
+                    /* flags= */ ONLY_IF_NO_MATCH_FOUND,
                     /* letsPersonalDataIntoProfile= */ false)
                     .addAction(ACTION_RECOGNIZE_SPEECH)
                     .addCategory(Intent.CATEGORY_DEFAULT)