Merge "Fix issue #62390590:  SecurityException in JobIntentService$..." into oc-dev
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index e686a89..ee89ca8 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1469,24 +1469,21 @@
         if (!mSelfPulse) {
             return;
         }
-        AnimationHandler handler = AnimationHandler.getInstance();
-        handler.addOneShotCommitCallback(this);
+        getAnimationHandler().addOneShotCommitCallback(this);
     }
 
     private void removeAnimationCallback() {
         if (!mSelfPulse) {
             return;
         }
-        AnimationHandler handler = AnimationHandler.getInstance();
-        handler.removeCallback(this);
+        getAnimationHandler().removeCallback(this);
     }
 
     private void addAnimationCallback(long delay) {
         if (!mSelfPulse) {
             return;
         }
-        AnimationHandler handler = AnimationHandler.getInstance();
-        handler.addAnimationFrameCallback(this, delay);
+        getAnimationHandler().addAnimationFrameCallback(this, delay);
     }
 
     /**
@@ -1643,4 +1640,12 @@
     public void setAllowRunningAsynchronously(boolean mayRunAsync) {
         // It is up to subclasses to support this, if they can.
     }
+
+    /**
+     * @return The {@link AnimationHandler} that will be used to schedule updates for this animator.
+     * @hide
+     */
+    public AnimationHandler getAnimationHandler() {
+        return AnimationHandler.getInstance();
+    }
 }
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 9d46da1..64e464c 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -44,6 +44,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.storage.StorageManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.MathUtils;
@@ -1376,6 +1377,12 @@
      * android.os.Handler, android.os.ParcelFileDescriptor.OnCloseListener)},
      * {@link ParcelFileDescriptor#createReliablePipe()}, or
      * {@link ParcelFileDescriptor#createReliableSocketPair()}.
+     * <p>
+     * If you need to return a large file that isn't backed by a real file on
+     * disk, such as a file on a network share or cloud storage service,
+     * consider using
+     * {@link StorageManager#openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler)}
+     * which will let you to stream the content on-demand.
      *
      * <p class="note">For use in Intents, you will want to implement {@link #getType}
      * to return the appropriate MIME type for the data returned here with
diff --git a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
index c66a3a4..8296b7a 100644
--- a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
+++ b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
@@ -15,13 +15,13 @@
  */
 package android.hardware.camera2.dispatch;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.hardware.camera2.utils.UncheckedThrow;
 
 import java.lang.reflect.Method;
 import java.util.concurrent.ConcurrentHashMap;
 
-import static com.android.internal.util.Preconditions.*;
-
 /**
  * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time).
  *
@@ -31,6 +31,7 @@
 
     private final Dispatchable<T> mTarget;
     private final Class<T> mTargetClass;
+    private final Method[] mTargetClassMethods;
     private final ConcurrentHashMap<String, Method> mMethods =
             new ConcurrentHashMap<>();
 
@@ -42,6 +43,7 @@
      */
     public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) {
         mTargetClass = targetClass;
+        mTargetClassMethods = targetClass.getMethods();
         mTarget = target;
     }
 
@@ -68,7 +70,7 @@
 
         Method targetMethod = mMethods.get(methodName);
         if (targetMethod == null) {
-            for (Method method : mTargetClass.getMethods()) {
+            for (Method method : mTargetClassMethods) {
                 // TODO future: match types of params if possible
                 if (method.getName().equals(methodName) &&
                         (params.length == method.getParameterTypes().length) ) {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 5046735..f42edcd 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -29,6 +29,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.WorkerThread;
+import android.app.Activity;
 import android.app.ActivityThread;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -159,6 +160,12 @@
      * If the sending application has a specific storage device or allocation
      * size in mind, they can optionally define {@link #EXTRA_UUID} or
      * {@link #EXTRA_REQUESTED_BYTES}, respectively.
+     * <p>
+     * This intent should be launched using
+     * {@link Activity#startActivityForResult(Intent, int)} so that the user
+     * knows which app is requesting the storage space. The returned result will
+     * be {@link Activity#RESULT_OK} if the requested space was made available,
+     * or {@link Activity#RESULT_CANCELED} otherwise.
      */
     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
@@ -1467,15 +1474,26 @@
     }
 
     /**
-     * Opens seekable ParcelFileDescriptor that routes file operation requests to
-     * ProxyFileDescriptorCallback.
+     * Opens a seekable {@link ParcelFileDescriptor} that proxies all low-level
+     * I/O requests back to the given {@link ProxyFileDescriptorCallback}.
+     * <p>
+     * This can be useful when you want to provide quick access to a large file
+     * that isn't backed by a real file on disk, such as a file on a network
+     * share, cloud storage service, etc. As an example, you could respond to a
+     * {@link ContentResolver#openFileDescriptor(android.net.Uri, String)}
+     * request by returning a {@link ParcelFileDescriptor} created with this
+     * method, and then stream the content on-demand as requested.
+     * <p>
+     * Another useful example might be where you have an encrypted file that
+     * you're willing to decrypt on-demand, but where you want to avoid
+     * persisting the cleartext version.
      *
      * @param mode The desired access mode, must be one of
-     *     {@link ParcelFileDescriptor#MODE_READ_ONLY},
-     *     {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or
-     *     {@link ParcelFileDescriptor#MODE_READ_WRITE}
-     * @param callback Callback to process file operation requests issued on returned file
-     *     descriptor.
+     *            {@link ParcelFileDescriptor#MODE_READ_ONLY},
+     *            {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or
+     *            {@link ParcelFileDescriptor#MODE_READ_WRITE}
+     * @param callback Callback to process file operation requests issued on
+     *            returned file descriptor.
      * @param handler Handler that invokes callback methods.
      * @return Seekable ParcelFileDescriptor.
      * @throws IOException
@@ -1487,7 +1505,6 @@
         return openProxyFileDescriptor(mode, callback, handler, null);
     }
 
-
     /** {@hide} */
     @VisibleForTesting
     public int getProxyFileDescriptorMountPointId() {
@@ -1660,6 +1677,10 @@
      * persist, you can launch {@link #ACTION_MANAGE_STORAGE} with the
      * {@link #EXTRA_UUID} and {@link #EXTRA_REQUESTED_BYTES} options to help
      * involve the user in freeing up disk space.
+     * <p>
+     * If you're progressively allocating an unbounded amount of storage space
+     * (such as when recording a video) you should avoid calling this method
+     * more than once every 30 seconds.
      * <p class="note">
      * Note: if your app uses the {@code android:sharedUserId} manifest feature,
      * then allocatable space for all packages in your shared UID is tracked
@@ -1677,6 +1698,7 @@
      * @throws IOException when the storage device isn't present, or when it
      *             doesn't support allocating space.
      */
+    @WorkerThread
     public @BytesLong long getAllocatableBytes(@NonNull UUID storageUuid)
             throws IOException {
         return getAllocatableBytes(storageUuid, 0);
@@ -1684,6 +1706,7 @@
 
     /** @hide */
     @SystemApi
+    @WorkerThread
     @SuppressLint("Doclava125")
     public long getAllocatableBytes(@NonNull UUID storageUuid,
             @RequiresPermission @AllocateFlags int flags) throws IOException {
@@ -1699,6 +1722,7 @@
 
     /** @removed */
     @Deprecated
+    @WorkerThread
     @SuppressLint("Doclava125")
     public long getAllocatableBytes(@NonNull File path,
             @RequiresPermission @AllocateFlags int flags) throws IOException {
@@ -1717,6 +1741,10 @@
      * subject to race conditions. If possible, consider using
      * {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee
      * that bytes are allocated to an opened file.
+     * <p>
+     * If you're progressively allocating an unbounded amount of storage space
+     * (such as when recording a video) you should avoid calling this method
+     * more than once every 60 seconds.
      *
      * @param storageUuid the UUID of the storage volume where you'd like to
      *            allocate disk space. The UUID for a specific path can be
@@ -1727,6 +1755,7 @@
      *             trouble allocating the requested space.
      * @see #getAllocatableBytes(UUID, int)
      */
+    @WorkerThread
     public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes)
             throws IOException {
         allocateBytes(storageUuid, bytes, 0);
@@ -1734,6 +1763,7 @@
 
     /** @hide */
     @SystemApi
+    @WorkerThread
     @SuppressLint("Doclava125")
     public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes,
             @RequiresPermission @AllocateFlags int flags) throws IOException {
@@ -1748,6 +1778,7 @@
 
     /** @removed */
     @Deprecated
+    @WorkerThread
     @SuppressLint("Doclava125")
     public void allocateBytes(@NonNull File path, @BytesLong long bytes,
             @RequiresPermission @AllocateFlags int flags) throws IOException {
@@ -1766,6 +1797,10 @@
      * otherwise it will throw if fast allocation is not possible. Fast
      * allocation is typically only supported in private app data directories,
      * and on shared/external storage devices which are emulated.
+     * <p>
+     * If you're progressively allocating an unbounded amount of storage space
+     * (such as when recording a video) you should avoid calling this method
+     * more than once every 60 seconds.
      *
      * @param fd the open file that you'd like to allocate disk space for.
      * @param bytes the number of bytes to allocate. This is the desired final
@@ -1779,12 +1814,14 @@
      * @see #getAllocatableBytes(UUID, int)
      * @see Environment#isExternalStorageEmulated(File)
      */
+    @WorkerThread
     public void allocateBytes(FileDescriptor fd, @BytesLong long bytes) throws IOException {
         allocateBytes(fd, bytes, 0);
     }
 
     /** @hide */
     @SystemApi
+    @WorkerThread
     @SuppressLint("Doclava125")
     public void allocateBytes(FileDescriptor fd, @BytesLong long bytes,
             @RequiresPermission @AllocateFlags int flags) throws IOException {
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index bc96e43..fcf18eb 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -327,17 +327,6 @@
         }
 
         /**
-         * @deprecated Use {@link #setClientState(Bundle)} instead.
-         * @hide
-         */
-        @Deprecated
-        public Builder setExtras(@Nullable Bundle extras) {
-            throwIfDestroyed();
-            mCLientState = extras;
-            return this;
-        }
-
-        /**
          * Sets a {@link Bundle state} that will be passed to subsequent APIs that
          * manipulate this response. For example, they are passed to subsequent
          * calls to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index ef78559..679a9cd 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1196,6 +1196,12 @@
             mBackgroundControl.deferTransactionUntil(handle, frame);
         }
 
+        @Override
+        public void deferTransactionUntil(Surface barrier, long frame) {
+            super.deferTransactionUntil(barrier, frame);
+            mBackgroundControl.deferTransactionUntil(barrier, frame);
+        }
+
         void updateBackgroundVisibility() {
             if (mOpaque && mVisible) {
                 mBackgroundControl.show();
diff --git a/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java b/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java
new file mode 100644
index 0000000..931eb99
--- /dev/null
+++ b/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics;
+
+import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
+import android.view.Choreographer;
+
+/**
+ * Provider of timing pulse that uses SurfaceFlinger Vsync Choreographer for frame callbacks.
+ *
+ * @hide
+ */
+public final class SfVsyncFrameCallbackProvider implements AnimationFrameCallbackProvider {
+
+    private final Choreographer mChoreographer = Choreographer.getSfInstance();
+
+    @Override
+    public void postFrameCallback(Choreographer.FrameCallback callback) {
+        mChoreographer.postFrameCallback(callback);
+    }
+
+    @Override
+    public void postCommitCallback(Runnable runnable) {
+        mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
+    }
+
+    @Override
+    public long getFrameTime() {
+        return mChoreographer.getFrameTime();
+    }
+
+    @Override
+    public long getFrameDelay() {
+        return Choreographer.getFrameDelay();
+    }
+
+    @Override
+    public void setFrameDelay(long delay) {
+        Choreographer.setFrameDelay(delay);
+    }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/TooltipPopup.java b/core/java/com/android/internal/view/TooltipPopup.java
index d834e63..52357ac 100644
--- a/core/java/com/android/internal/view/TooltipPopup.java
+++ b/core/java/com/android/internal/view/TooltipPopup.java
@@ -25,7 +25,6 @@
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
-import android.widget.PopupWindow;
 import android.widget.TextView;
 
 public class TooltipPopup {
@@ -33,7 +32,6 @@
 
     private final Context mContext;
 
-    private final PopupWindow mPopupWindow;
     private final View mContentView;
     private final TextView mMessageView;
 
@@ -45,8 +43,6 @@
     public TooltipPopup(Context context) {
         mContext = context;
 
-        mPopupWindow = new PopupWindow(context);
-        mPopupWindow.setBackgroundDrawable(null);
         mContentView = LayoutInflater.from(mContext).inflate(
                 com.android.internal.R.layout.tooltip, null);
         mMessageView = (TextView) mContentView.findViewById(
@@ -74,16 +70,17 @@
 
         computePosition(anchorView, anchorX, anchorY, fromTouch, mLayoutParams);
 
-        mPopupWindow.setContentView(mContentView);
-        mPopupWindow.showAtLocation(
-            anchorView, mLayoutParams.gravity, mLayoutParams.x, mLayoutParams.y);
+        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+        wm.addView(mContentView, mLayoutParams);
     }
 
     public void hide() {
         if (!isShowing()) {
             return;
         }
-        mPopupWindow.dismiss();
+
+        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+        wm.removeView(mContentView);
     }
 
     public View getContentView() {
@@ -91,7 +88,7 @@
     }
 
     public boolean isShowing() {
-        return mPopupWindow.isShowing();
+        return mContentView.getParent() != null;
     }
 
     public void updateContent(CharSequence tooltipText) {
@@ -100,6 +97,8 @@
 
     private void computePosition(View anchorView, int anchorX, int anchorY, boolean fromTouch,
             WindowManager.LayoutParams outParams) {
+        outParams.token = anchorView.getWindowToken();
+
         final int tooltipPreciseAnchorThreshold = mContext.getResources().getDimensionPixelOffset(
                 com.android.internal.R.dimen.tooltip_precise_anchor_threshold);
 
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index de67c50..26b0034 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -29,6 +29,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/stringprintf.h>
 #include <binder/IInterface.h>
 #include <binder/IServiceManager.h>
 #include <binder/IPCThreadState.h>
@@ -194,10 +195,34 @@
         /*
          * It's an Error: Reraise the exception and ask the runtime to abort.
          */
+
+        // Try to get the exception string. Sometimes logcat isn't available,
+        // so try to add it to the abort message.
+        std::string exc_msg = "(Unknown exception message)";
+        {
+            ScopedLocalRef<jclass> exc_class(env, env->GetObjectClass(excep));
+            jmethodID method_id = env->GetMethodID(exc_class.get(),
+                                                   "toString",
+                                                   "()Ljava/lang/String;");
+            ScopedLocalRef<jstring> jstr(
+                    env,
+                    reinterpret_cast<jstring>(
+                            env->CallObjectMethod(excep, method_id)));
+            env->ExceptionClear();  // Just for good measure.
+            if (jstr.get() != nullptr) {
+                ScopedUtfChars jstr_utf(env, jstr.get());
+                exc_msg = jstr_utf.c_str();
+            }
+        }
+
         env->Throw(excep);
         ALOGE("java.lang.Error thrown during binder transaction (stack trace follows) : ");
         env->ExceptionDescribe();
-        env->FatalError("java.lang.Error thrown during binder transaction.");
+
+        std::string error_msg = base::StringPrintf(
+                "java.lang.Error thrown during binder transaction: %s",
+                exc_msg.c_str());
+        env->FatalError(error_msg.c_str());
     }
 
 bail:
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f8044e2..794d4f8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -453,6 +453,7 @@
     <protected-broadcast android:name="com.android.server.NetworkTimeUpdateService.action.POLL" />
     <protected-broadcast android:name="com.android.server.telecom.intent.action.CALLS_ADD_ENTRY" />
     <protected-broadcast android:name="com.android.settings.location.MODE_CHANGING" />
+    <protected-broadcast android:name="com.android.settings.bluetooth.ACTION_DISMISS_PAIRING" />
 
     <protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
     <protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 7e8bc00..236c185 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -811,7 +811,7 @@
     <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"Confirmació de la navegació"</string>
     <string name="js_dialog_before_unload_positive_button" msgid="3112752010600484130">"Surt d\'aquesta pàgina"</string>
     <string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Queda\'t en aquesta pàgina"</string>
-    <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nEstàs segur que vols sortir d\'aquesta pàgina?"</string>
+    <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nConfirmes que vols sortir d\'aquesta pàgina?"</string>
     <string name="save_password_label" msgid="6860261758665825069">"Confirma"</string>
     <string name="double_tap_toast" msgid="4595046515400268881">"Consell: Pica dos cops per ampliar i per reduir."</string>
     <string name="autofill_this_form" msgid="4616758841157816676">"Em. aut."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 320ffcb..3251771 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1575,8 +1575,8 @@
     <string name="mediasize_japanese_kahu" msgid="6872696027560065173">"Kahu"</string>
     <string name="mediasize_japanese_kaku2" msgid="2359077233775455405">"Kaku2"</string>
     <string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
-    <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"Ukendt portrætformat"</string>
-    <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"Ukendt landskabsformat"</string>
+    <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"Ukendt stående format"</string>
+    <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"Ukendt liggende format"</string>
     <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"Annulleret"</string>
     <string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"Fejl ved skrivning af indhold"</string>
     <string name="reason_unknown" msgid="6048913880184628119">"ukendt"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 82c0f63..26f3f1d 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -90,7 +90,7 @@
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"Η υπηρεσία δεν προβλέπεται."</string>
     <string name="CLIRPermanent" msgid="3377371145926835671">"Δεν μπορείτε να αλλάξετε τη ρύθμιση του αναγνωριστικού καλούντος."</string>
     <string name="RestrictedOnDataTitle" msgid="1322504692764166532">"Δεν υπάρχει υπηρεσία δεδομένων"</string>
-    <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"Αδυναμία πραγματοποίησης κλήσεων έκτακτης ανάγκης"</string>
+    <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"Δεν επιτρέπονται οι κλήσεις έκτακτης ανάγκης"</string>
     <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Δεν υπάρχει φωνητική υπηρεσία"</string>
     <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"Δεν υπάρχει φωνητική υπηρεσία/υπηρεσία έκτακτης ανάγκης"</string>
     <string name="RestrictedStateContent" msgid="4278821484643362350">"Δεν προσφέρεται προσωρινά από το δίκτυο κινητής τηλεφωνίας στην τοποθεσία σας"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index a2b4499..b9b202f 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -90,7 +90,7 @@
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"Ce service n\'est pas pris en charge."</string>
     <string name="CLIRPermanent" msgid="3377371145926835671">"Impossible de modifier le paramètre relatif au numéro de l\'appelant."</string>
     <string name="RestrictedOnDataTitle" msgid="1322504692764166532">"Aucun service de données"</string>
-    <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"Aucune appel d\'urgence"</string>
+    <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"Aucun appel d\'urgence"</string>
     <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Aucun service vocal"</string>
     <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"Aucun service vocal ou d\'urgence"</string>
     <string name="RestrictedStateContent" msgid="4278821484643362350">"Ce service est temporairement non offert par le réseau cellulaire à l\'endroit où vous êtes"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b03d6cc..bf04d8a 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1201,7 +1201,7 @@
     <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"भाषा और लेआउट चुनने के लिए टैप करें"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-    <string name="alert_windows_notification_channel_group_name" msgid="1463953341148606396">"दूसरे ऐप्लिकेशन पर प्रदर्शित करें"</string>
+    <string name="alert_windows_notification_channel_group_name" msgid="1463953341148606396">"दूसरे ऐप्लिकेशन के ऊपर दिखाएं"</string>
     <string name="alert_windows_notification_channel_name" msgid="3116610965549449803">"<xliff:g id="NAME">%s</xliff:g> अन्य ऐप्लिकेशन के ऊपर दिखाई दे रहा है"</string>
     <string name="alert_windows_notification_title" msgid="3697657294867638947">"<xliff:g id="NAME">%s</xliff:g> अन्य ऐप पर दिखाई दे रहा है"</string>
     <string name="alert_windows_notification_message" msgid="8917232109522912560">"अगर आप नहीं चाहते कि <xliff:g id="NAME">%s</xliff:g> इस सुविधा का उपयोग करे, तो सेटिंग खोलने और उसे बंद करने के लिए टैप करें."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 7135060..ef34aa5 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -93,7 +93,7 @@
     <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"긴급 전화 차단됨"</string>
     <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"음성 서비스를 이용할 수 없음"</string>
     <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"음성/긴급 서비스를 이용할 수 없음"</string>
-    <string name="RestrictedStateContent" msgid="4278821484643362350">"현재 위치에서 모바일 네트워크가 긴급 서비스 제공을 일시적으로 중단했습니다."</string>
+    <string name="RestrictedStateContent" msgid="4278821484643362350">"현재 위치에서 모바일 네트워크가 서비스 제공을 일시적으로 중단했습니다."</string>
     <string name="NetworkPreferenceSwitchTitle" msgid="4008877505368566980">"네트워크에 연결할 수 없습니다."</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="4164230263214915351">"수신 상태를 개선하려면 시스템 &gt; 네트워크 및 인터넷 &gt; 모바일 네트워크 &gt; 기본 네트워크 유형에서 선택된 유형을 변경해 보세요."</string>
     <string name="notification_channel_network_alert" msgid="4427736684338074967">"알림"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 6daa7a7..db4d8fc 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1202,7 +1202,7 @@
     <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"ဘာသာစကားနှင့် အသွင်အပြင်ရွေးချယ်ရန် တို့ပါ"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-    <string name="alert_windows_notification_channel_group_name" msgid="1463953341148606396">"အခြားအက်ပ်များအပေါ်တွင် ပြသခွင့် ပြုခြင်း"</string>
+    <string name="alert_windows_notification_channel_group_name" msgid="1463953341148606396">"အခြားအက်ပ်များအပေါ်တွင် ပြသခြင်း"</string>
     <string name="alert_windows_notification_channel_name" msgid="3116610965549449803">"<xliff:g id="NAME">%s</xliff:g> သည် အခြားအက်ပ်များအပေါ်တွင် ပြပါသည်"</string>
     <string name="alert_windows_notification_title" msgid="3697657294867638947">"<xliff:g id="NAME">%s</xliff:g> ကို အခြားအက်ပ်များပေါ်တွင် မြင်နေရပါသည်။"</string>
     <string name="alert_windows_notification_message" msgid="8917232109522912560">"<xliff:g id="NAME">%s</xliff:g> ကို ဤဝန်ဆောင်မှုအား အသုံးမပြုစေလိုလျှင် ဆက်တင်ကို တို့၍ ဖွင့်ပြီး ၎င်းကို ပိတ်လိုက်ပါ။"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index df8d6d2..f64719c 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -90,7 +90,7 @@
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"ਸੇਵਾ ਪ੍ਰਬੰਧਿਤ ਨਹੀਂ ਹੈ।"</string>
     <string name="CLIRPermanent" msgid="3377371145926835671">"ਤੁਸੀਂ ਕਾਲਰ ID ਸੈਟਿੰਗ ਨਹੀਂ ਬਦਲ ਸਕਦੇ।"</string>
     <string name="RestrictedOnDataTitle" msgid="1322504692764166532">"ਕੋਈ ਡੈਟਾ ਸੇਵਾ ਨਹੀਂ"</string>
-    <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"ਕੋਈ ਸੰਕਟਕਾਲੀਨ ਕਾਲਿੰਗ ਨਹੀਂ"</string>
+    <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"ਸੰਕਟਕਾਲ ਵਿੱਚ ਕੋਈ ਕਾਲ ਨਹੀਂ"</string>
     <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"ਕੋਈ ਆਵਾਜ਼ੀ ਸੇਵਾ ਨਹੀਂ"</string>
     <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"ਕੋਈ ਆਵਾਜ਼ੀ/ਸੰਕਟਕਾਲੀਨ ਸੇਵਾ ਨਹੀਂ"</string>
     <string name="RestrictedStateContent" msgid="4278821484643362350">"ਤੁਹਾਡੇ ਟਿਕਾਣੇ \'ਤੇ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਵੱਲੋਂ ਉਪਲਬਧ ਨਹੀਂ ਕਰਵਾਈ ਗਈ"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 844dceb..ba1963f 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -90,7 +90,7 @@
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"సేవ కేటాయించబడలేదు."</string>
     <string name="CLIRPermanent" msgid="3377371145926835671">"మీరు కాలర్ ID సెట్టింగ్‌ను మార్చలేరు."</string>
     <string name="RestrictedOnDataTitle" msgid="1322504692764166532">"డేటా సేవ లేదు"</string>
-    <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"అత్యవసర కాలింగ్ లేదు"</string>
+    <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"అత్యవసర కాలింగ్ సదుపాయం లేదు"</string>
     <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"వాయిస్ సేవ లేదు"</string>
     <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"వాయిస్/అత్యవసర సేవ లేదు"</string>
     <string name="RestrictedStateContent" msgid="4278821484643362350">"మీ స్థానంలో మొబైల్ నెట్‌వర్క్ ద్వారా తాత్కాలికంగా అందించబడదు"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 6df02eb..526a8d0 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -329,7 +329,7 @@
     <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى يكتمل الشحن"</string>
     <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="battery_info_status_unknown" msgid="196130600938058547">"غير معروف"</string>
-    <string name="battery_info_status_charging" msgid="1705179948350365604">"جاري الشحن"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"جارٍ الشحن"</string>
     <string name="battery_info_status_charging_lower" msgid="8689770213898117994">"جارٍ الشحن"</string>
     <string name="battery_info_status_discharging" msgid="310932812698268588">"لا يتم الشحن"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"لا يتم الشحن"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 35a341a..48bf180 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -272,7 +272,7 @@
     <string name="app_process_limit_title" msgid="4280600650253107163">"Límita processos en segon pla"</string>
     <string name="show_all_anrs" msgid="28462979638729082">"Tots els errors sense resposta"</string>
     <string name="show_all_anrs_summary" msgid="641908614413544127">"Informa que una aplicació en segon pla no respon"</string>
-    <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Avisos del canal de notificacions"</string>
+    <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Mostra avisos del canal de notificacions"</string>
     <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra un avís a la pantalla quan una app publica una notificació sense canal vàlid"</string>
     <string name="force_allow_on_external" msgid="3215759785081916381">"Força permís d\'aplicacions a l\'emmagatzem. extern"</string>
     <string name="force_allow_on_external_summary" msgid="3640752408258034689">"Permet que qualsevol aplicació es pugui escriure en un dispositiu d’emmagatzematge extern, independentment dels valors definits"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f745956..1ec52e7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1864,12 +1864,7 @@
                         + " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
             }
 
-            if (!(mContext instanceof Activity)) {
-                final int finalFlags = flags;
-                mUiOffloadThread.submit(() -> {
-                    mStatusBarManager.disable(finalFlags);
-                });
-            }
+            mStatusBarManager.disable(flags);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
index 867c15c..6733421 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
@@ -21,12 +21,15 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.BatchedInputEventReceiver;
+import android.view.Choreographer;
 import android.view.InputChannel;
 import android.view.InputEvent;
-import android.view.InputEventReceiver;
 import android.view.IWindowManager;
 import android.view.MotionEvent;
 
+import com.android.systemui.recents.misc.Utilities;
+
 import java.io.PrintWriter;
 
 /**
@@ -52,12 +55,13 @@
     }
 
     /**
-     * Input handler used for the PiP input consumer.
+     * Input handler used for the PiP input consumer. Input events are batched and consumed with the
+     * SurfaceFlinger vsync.
      */
-    private final class PipInputEventReceiver extends InputEventReceiver {
+    private final class PipInputEventReceiver extends BatchedInputEventReceiver {
 
         public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
-            super(inputChannel, looper);
+            super(inputChannel, looper, Choreographer.getSfInstance());
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 013b9ac..0f69f47 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -573,13 +573,11 @@
     }
 
     private void cancelDelayedFinish() {
-        View v = getWindow().getDecorView();
-        v.removeCallbacks(mFinishRunnable);
+        mHandler.removeCallbacks(mFinishRunnable);
     }
 
     private void repostDelayedFinish(long delay) {
-        View v = getWindow().getDecorView();
-        v.removeCallbacks(mFinishRunnable);
-        v.postDelayed(mFinishRunnable, delay);
+        mHandler.removeCallbacks(mFinishRunnable);
+        mHandler.postDelayed(mFinishRunnable, delay);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 9fa7ff6..b8771d7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -22,6 +22,7 @@
 import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN;
 
+import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
@@ -36,14 +37,15 @@
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.Handler;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
-import android.view.Choreographer;
 import android.view.animation.Interpolator;
 
-import com.android.internal.os.BackgroundThread;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.policy.PipSnapAlgorithm;
-import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
+import com.android.systemui.recents.misc.ForegroundThread;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 
@@ -52,7 +54,7 @@
 /**
  * A helper to animate and manipulate the PiP.
  */
-public class PipMotionHelper {
+public class PipMotionHelper implements Handler.Callback {
 
     private static final String TAG = "PipMotionHelper";
     private static final boolean DEBUG = false;
@@ -74,38 +76,34 @@
     // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
     private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f;
 
+    private static final int MSG_RESIZE_IMMEDIATE = 1;
+    private static final int MSG_RESIZE_ANIMATE = 2;
+
     private Context mContext;
     private IActivityManager mActivityManager;
-    private SurfaceFlingerVsyncChoreographer mVsyncChoreographer;
     private Handler mHandler;
 
     private PipMenuActivityController mMenuController;
     private PipSnapAlgorithm mSnapAlgorithm;
     private FlingAnimationUtils mFlingAnimationUtils;
+    private AnimationHandler mAnimationHandler;
 
     private final Rect mBounds = new Rect();
     private final Rect mStableInsets = new Rect();
 
     private ValueAnimator mBoundsAnimator = null;
-    private ValueAnimator.AnimatorUpdateListener mUpdateBoundsListener =
-            new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    mBounds.set((Rect) animation.getAnimatedValue());
-                }
-            };
 
     public PipMotionHelper(Context context, IActivityManager activityManager,
             PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm,
             FlingAnimationUtils flingAnimationUtils) {
         mContext = context;
-        mHandler = BackgroundThread.getHandler();
+        mHandler = new Handler(ForegroundThread.get().getLooper(), this);
         mActivityManager = activityManager;
         mMenuController = menuController;
         mSnapAlgorithm = snapAlgorithm;
         mFlingAnimationUtils = flingAnimationUtils;
-        mVsyncChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, mContext.getDisplay(),
-                Choreographer.getInstance());
+        mAnimationHandler = new AnimationHandler();
+        mAnimationHandler.setProvider(new SfVsyncFrameCallbackProvider());
         onConfigurationChanged();
     }
 
@@ -252,8 +250,7 @@
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
                 0 /* velocityX */, velocityY);
         if (!mBounds.equals(toBounds)) {
-            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN,
-                    mUpdateBoundsListener);
+            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN);
             mFlingAnimationUtils.apply(mBoundsAnimator, 0,
                     distanceBetweenRectOffsets(mBounds, toBounds),
                     velocityY);
@@ -271,7 +268,7 @@
         Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);
         if (!mBounds.equals(toBounds)) {
             mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
-                    MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN, mUpdateBoundsListener);
+                    MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN);
             if (updateListener != null) {
                 mBoundsAnimator.addUpdateListener(updateListener);
             }
@@ -289,8 +286,7 @@
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
                 velocityX, velocityY);
         if (!mBounds.equals(toBounds)) {
-            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN,
-                    mUpdateBoundsListener);
+            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN);
             mFlingAnimationUtils.apply(mBoundsAnimator, 0,
                     distanceBetweenRectOffsets(mBounds, toBounds),
                     velocity);
@@ -314,7 +310,7 @@
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds);
         if (!mBounds.equals(toBounds)) {
             mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, SNAP_STACK_DURATION,
-                    FAST_OUT_SLOW_IN, mUpdateBoundsListener);
+                    FAST_OUT_SLOW_IN);
             if (updateListener != null) {
                 mBoundsAnimator.addUpdateListener(updateListener);
             }
@@ -379,7 +375,7 @@
         Rect toBounds = new Rect(pipBounds);
         toBounds.offsetTo(p.x, p.y);
         mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, DRAG_TO_DISMISS_STACK_DURATION,
-                FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
+                FAST_OUT_LINEAR_IN);
         mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -411,16 +407,20 @@
      * Creates an animation to move the PiP to give given {@param toBounds}.
      */
     private ValueAnimator createAnimationToBounds(Rect fromBounds, Rect toBounds, int duration,
-            Interpolator interpolator, ValueAnimator.AnimatorUpdateListener updateListener) {
-        ValueAnimator anim = ValueAnimator.ofObject(RECT_EVALUATOR, fromBounds, toBounds);
+            Interpolator interpolator) {
+        ValueAnimator anim = new ValueAnimator() {
+            @Override
+            public AnimationHandler getAnimationHandler() {
+                return mAnimationHandler;
+            }
+        };
+        anim.setObjectValues(fromBounds, toBounds);
+        anim.setEvaluator(RECT_EVALUATOR);
         anim.setDuration(duration);
         anim.setInterpolator(interpolator);
         anim.addUpdateListener((ValueAnimator animation) -> {
             resizePipUnchecked((Rect) animation.getAnimatedValue());
         });
-        if (updateListener != null) {
-            anim.addUpdateListener(updateListener);
-        }
         return anim;
     }
 
@@ -433,14 +433,9 @@
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
         if (!toBounds.equals(mBounds)) {
-            mVsyncChoreographer.scheduleAtSfVsync(() -> {
-                try {
-                    mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
-                    mBounds.set(toBounds);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e);
-                }
-            });
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = toBounds;
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args));
         }
     }
 
@@ -453,23 +448,10 @@
                     + " duration=" + duration + " callers=\n" + Debug.getCallers(5, "    "));
         }
         if (!toBounds.equals(mBounds)) {
-            mHandler.post(() -> {
-                try {
-                    StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
-                    if (stackInfo == null) {
-                        // In the case where we've already re-expanded or dismissed the PiP, then
-                        // just skip the resize
-                        return;
-                    }
-
-                    mActivityManager.resizeStack(PINNED_STACK_ID, toBounds,
-                            false /* allowResizeInDockedMode */, true /* preserveWindows */,
-                            true /* animate */, duration);
-                    mBounds.set(toBounds);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
-                }
-            });
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = toBounds;
+            args.argi1 = duration;
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
         }
     }
 
@@ -524,6 +506,50 @@
         return PointF.length(r1.left - r2.left, r1.top - r2.top);
     }
 
+    /**
+     * Handles messages to be processed on the background thread.
+     */
+    public boolean handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_RESIZE_IMMEDIATE: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                Rect toBounds = (Rect) args.arg1;
+                try {
+                    mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
+                    mBounds.set(toBounds);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e);
+                }
+                return true;
+            }
+
+            case MSG_RESIZE_ANIMATE: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                Rect toBounds = (Rect) args.arg1;
+                int duration = args.argi1;
+                try {
+                    StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+                    if (stackInfo == null) {
+                        // In the case where we've already re-expanded or dismissed the PiP, then
+                        // just skip the resize
+                        return true;
+                    }
+
+                    mActivityManager.resizeStack(PINNED_STACK_ID, toBounds,
+                            false /* allowResizeInDockedMode */, true /* preserveWindows */,
+                            true /* animate */, duration);
+                    mBounds.set(toBounds);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
+                }
+                return true;
+            }
+
+            default:
+                return false;
+        }
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 9b20a7a..79fb5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -179,12 +179,6 @@
 
         @Override
         public void onBluetoothDevicesChanged() {
-            mUiHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mDetailAdapter.updateItems();
-                }
-            });
             refreshState();
             if (isShowingDetail()) {
                 mDetailAdapter.updateItems();
@@ -198,6 +192,9 @@
     }
 
     protected class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
+        // We probably won't ever have space in the UI for more than 20 devices, so don't
+        // get info for them.
+        private static final int MAX_DEVICES = 20;
         private QSDetailItems mItems;
 
         @Override
@@ -260,13 +257,14 @@
             final Collection<CachedBluetoothDevice> devices = mController.getDevices();
             if (devices != null) {
                 int connectedDevices = 0;
+                int count = 0;
                 for (CachedBluetoothDevice device : devices) {
-                    if (device.getBondState() == BluetoothDevice.BOND_NONE) continue;
+                    if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
                     final Item item = new Item();
                     item.icon = R.drawable.ic_qs_bluetooth_on;
                     item.line1 = device.getName();
                     item.tag = device;
-                    int state = device.getMaxConnectionState();
+                    int state = mController.getMaxConnectionState(device);
                     if (state == BluetoothProfile.STATE_CONNECTED) {
                         item.icon = R.drawable.ic_qs_bluetooth_connected;
                         item.line2 = mContext.getString(R.string.quick_settings_connected);
@@ -280,6 +278,9 @@
                     } else {
                         items.add(item);
                     }
+                    if (++count == MAX_DEVICES) {
+                        break;
+                    }
                 }
             }
             mItems.setItems(items.toArray(new Item[items.size()]));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 1f13830..c66b2dd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -154,6 +154,13 @@
     Canvas mBgProtectionCanvas;
 
     private final Handler mHandler = new H();
+    private final Runnable mGcRunnable = new Runnable() {
+        @Override
+        public void run() {
+            System.gc();
+            System.runFinalization();
+        }
+    };
 
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
 
@@ -365,13 +372,7 @@
      * Requests a gc() from the background thread.
      */
     public void gc() {
-        BackgroundThread.getHandler().post(new Runnable() {
-            @Override
-            public void run() {
-                System.gc();
-                System.runFinalization();
-            }
-        });
+        BackgroundThread.getHandler().post(mGcRunnable);
     }
 
     /**
@@ -799,11 +800,8 @@
         if (RecentsDebugFlags.Static.EnableMockTasks) return;
 
         // Remove the task.
-        BackgroundThread.getHandler().post(new Runnable() {
-            @Override
-            public void run() {
-                mAm.removeTask(taskId);
-            }
+        mUiOffloadThread.submit(() -> {
+            mAm.removeTask(taskId);
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index 1f82c16..308cece 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -20,9 +20,9 @@
 import android.os.UserHandle;
 
 import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.BackgroundThread;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
+import com.android.systemui.recents.misc.ForegroundThread;
 
 /**
  * The package monitor listens for changes from PackageManager to update the contents of the
@@ -36,7 +36,7 @@
             // We register for events from all users, but will cross-reference them with
             // packages for the current user and any profiles they have.  Ensure that events are
             // handled in a background thread.
-            register(context, BackgroundThread.get().getLooper(), UserHandle.ALL, true);
+            register(context, ForegroundThread.get().getLooper(), UserHandle.ALL, true);
         } catch (IllegalStateException e) {
             e.printStackTrace();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 3b37437..0232911 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -25,6 +25,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -56,6 +58,7 @@
         SystemServicesProxy ssp = Recents.getSystemServices();
         ssp.registerDockedStackListener(mDockDividerVisibilityListener);
         mForcedResizableController = new ForcedResizableInfoActivityController(mContext);
+        EventBus.getDefault().register(this);
     }
 
     @Override
@@ -153,6 +156,18 @@
         mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme);
     }
 
+    /**
+     * Workaround for b/62528361, at the time RecentsDrawnEvent is sent, it may happen before a
+     * configuration change to the Divider, and internally, the event will be posted to the
+     * subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
+     * register the event handler here and proxy the event to the current DividerView.
+     */
+    public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
+        if (mView != null) {
+            mView.onRecentsDrawn();
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print("  mVisible="); pw.println(mVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 6dc7870..7691652 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -1222,7 +1222,7 @@
                 mSnapAlgorithm.getMiddleTarget());
     }
 
-    public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
+    public void onRecentsDrawn() {
         if (mState.animateAfterRecentsDrawn) {
             mState.animateAfterRecentsDrawn = false;
             updateDockSide();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index bb302bb..47c4692 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -92,6 +92,7 @@
     private boolean mLastBouncerShowing;
     private boolean mLastBouncerDismissible;
     protected boolean mLastRemoteInputActive;
+    private boolean mLastDeferScrimFadeOut;
 
     private OnDismissAction mAfterKeyguardGoneAction;
     private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
@@ -367,7 +368,6 @@
             mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
             mFingerprintUnlockController.startKeyguardFadingAway();
             mBouncer.hide(true /* destroyView */);
-            updateStates();
             if (wakeUnlockPulsing) {
                 mStatusBarWindowManager.setKeyguardFadingAway(true);
                 mStatusBar.fadeKeyguardWhilePulsing();
@@ -399,6 +399,7 @@
                     mFingerprintUnlockController.finishKeyguardFadingAway();
                 }
             }
+            updateStates();
             mStatusBarWindowManager.setKeyguardShowing(false);
             mViewMediatorCallback.keyguardGone();
         }
@@ -569,7 +570,7 @@
         mLastBouncerShowing = bouncerShowing;
         mLastBouncerDismissible = bouncerDismissible;
         mLastRemoteInputActive = remoteInputActive;
-
+        mLastDeferScrimFadeOut = mDeferScrimFadeOut;
         mStatusBar.onKeyguardViewManagerStatesUpdated();
     }
 
@@ -577,14 +578,16 @@
      * @return Whether the navigation bar should be made visible based on the current state.
      */
     protected boolean isNavBarVisible() {
-        return !(mShowing && !mOccluded) || mBouncer.isShowing() || mRemoteInputActive;
+        return (!(mShowing && !mOccluded) || mBouncer.isShowing() || mRemoteInputActive)
+                && !mDeferScrimFadeOut;
     }
 
     /**
      * @return Whether the navigation bar was made visible based on the last known state.
      */
     protected boolean getLastNavBarVisible() {
-        return !(mLastShowing && !mLastOccluded) || mLastBouncerShowing || mLastRemoteInputActive;
+        return (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing || mLastRemoteInputActive)
+                && !mLastDeferScrimFadeOut;
     }
 
     public boolean shouldDismissOnMenuPressed() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index df30e20..9daa199 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -37,6 +37,9 @@
     void disconnect(CachedBluetoothDevice device);
     boolean canConfigBluetooth();
 
+    int getMaxConnectionState(CachedBluetoothDevice device);
+    int getBondState(CachedBluetoothDevice device);
+
     public interface Callback {
         void onBluetoothStateChange(boolean enabled);
         void onBluetoothDevicesChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 36d24b3..dc4b115 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -18,6 +18,8 @@
 
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
@@ -33,8 +35,10 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.WeakHashMap;
 
 public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
         CachedBluetoothDevice.Callback {
@@ -44,18 +48,22 @@
     private final LocalBluetoothManager mLocalBluetoothManager;
     private final UserManager mUserManager;
     private final int mCurrentUser;
+    private final WeakHashMap<CachedBluetoothDevice, ActuallyCachedState> mCachedState =
+            new WeakHashMap<>();
+    private final Handler mBgHandler;
 
     private boolean mEnabled;
     private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
     private CachedBluetoothDevice mLastDevice;
 
-    private final H mHandler = new H();
+    private final H mHandler = new H(Looper.getMainLooper());
     private int mState;
 
     public BluetoothControllerImpl(Context context, Looper bgLooper) {
         mLocalBluetoothManager = Dependency.get(LocalBluetoothManager.class);
+        mBgHandler = new Handler(bgLooper);
         if (mLocalBluetoothManager != null) {
-            mLocalBluetoothManager.getEventManager().setReceiverHandler(new Handler(bgLooper));
+            mLocalBluetoothManager.getEventManager().setReceiverHandler(mBgHandler);
             mLocalBluetoothManager.getEventManager().registerCallback(this);
             onBluetoothStateChanged(
                     mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState());
@@ -106,6 +114,16 @@
     }
 
     @Override
+    public int getBondState(CachedBluetoothDevice device) {
+        return getCachedState(device).mBondState;
+    }
+
+    @Override
+    public int getMaxConnectionState(CachedBluetoothDevice device) {
+        return getCachedState(device).mMaxConnectionState;
+    }
+
+    @Override
     public void addCallback(Callback cb) {
         mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget();
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
@@ -225,12 +243,14 @@
 
     @Override
     public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
+        mCachedState.remove(cachedDevice);
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
     }
 
     @Override
     public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+        mCachedState.remove(cachedDevice);
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
     }
@@ -243,11 +263,44 @@
 
     @Override
     public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+        mCachedState.remove(cachedDevice);
         mLastDevice = cachedDevice;
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
     }
 
+    private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
+        ActuallyCachedState state = mCachedState.get(device);
+        if (state == null) {
+            state = new ActuallyCachedState(device, mHandler);
+            mBgHandler.post(state);
+            mCachedState.put(device, state);
+            return state;
+        }
+        return state;
+    }
+
+    private static class ActuallyCachedState implements Runnable {
+
+        private final WeakReference<CachedBluetoothDevice> mDevice;
+        private final Handler mUiHandler;
+        private int mBondState = BluetoothDevice.BOND_NONE;
+        private int mMaxConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+
+        private ActuallyCachedState(CachedBluetoothDevice device, Handler uiHandler) {
+            mDevice = new WeakReference<>(device);
+            mUiHandler = uiHandler;
+        }
+
+        @Override
+        public void run() {
+            mBondState = mDevice.get().getBondState();
+            mMaxConnectionState = mDevice.get().getMaxConnectionState();
+            mUiHandler.removeMessages(H.MSG_PAIRED_DEVICES_CHANGED);
+            mUiHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
+        }
+    }
+
     private final class H extends Handler {
         private final ArrayList<BluetoothController.Callback> mCallbacks = new ArrayList<>();
 
@@ -256,6 +309,10 @@
         private static final int MSG_ADD_CALLBACK = 3;
         private static final int MSG_REMOVE_CALLBACK = 4;
 
+        public H(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 2eb9560..4cc8bca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -14,16 +14,21 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
+import android.os.Looper;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.util.Log;
 
 import com.android.settingslib.bluetooth.BluetoothEventManager;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -80,4 +85,56 @@
                 BluetoothAdapter.STATE_DISCONNECTED);
         assertTrue(mBluetoothControllerImpl.isBluetoothConnected());
     }
+
+    @Test
+    public void testDefaultConnectionState() {
+        CachedBluetoothDevice device = mock(CachedBluetoothDevice.class);
+        assertEquals(BluetoothDevice.BOND_NONE, mBluetoothControllerImpl.getBondState(device));
+        assertEquals(BluetoothProfile.STATE_DISCONNECTED,
+                mBluetoothControllerImpl.getMaxConnectionState(device));
+    }
+
+    @Test
+    public void testAsyncBondState() throws Exception {
+        CachedBluetoothDevice device = mock(CachedBluetoothDevice.class);
+        when(device.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        BluetoothController.Callback callback = mock(BluetoothController.Callback.class);
+        mBluetoothControllerImpl.addCallback(callback);
+
+        // Grab the main looper, we'll need it later.
+        TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper());
+
+        // Trigger the state getting.
+        assertEquals(BluetoothDevice.BOND_NONE, mBluetoothControllerImpl.getBondState(device));
+
+        mTestableLooper.processMessages(1);
+        mainLooper.processAllMessages();
+
+        assertEquals(BluetoothDevice.BOND_BONDED, mBluetoothControllerImpl.getBondState(device));
+        verify(callback).onBluetoothDevicesChanged();
+        mainLooper.destroy();
+    }
+
+    @Test
+    public void testAsyncConnectionState() throws Exception {
+        CachedBluetoothDevice device = mock(CachedBluetoothDevice.class);
+        when(device.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_CONNECTED);
+        BluetoothController.Callback callback = mock(BluetoothController.Callback.class);
+        mBluetoothControllerImpl.addCallback(callback);
+
+        // Grab the main looper, we'll need it later.
+        TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper());
+
+        // Trigger the state getting.
+        assertEquals(BluetoothProfile.STATE_DISCONNECTED,
+                mBluetoothControllerImpl.getMaxConnectionState(device));
+
+        mTestableLooper.processMessages(1);
+        mainLooper.processAllMessages();
+
+        assertEquals(BluetoothProfile.STATE_CONNECTED,
+                mBluetoothControllerImpl.getMaxConnectionState(device));
+        verify(callback).onBluetoothDevicesChanged();
+        mainLooper.destroy();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
index 0ba0319..9ec096a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -83,4 +83,14 @@
     public boolean canConfigBluetooth() {
         return false;
     }
+
+    @Override
+    public int getMaxConnectionState(CachedBluetoothDevice device) {
+        return 0;
+    }
+
+    @Override
+    public int getBondState(CachedBluetoothDevice device) {
+        return 0;
+    }
 }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 6a310f2..eeab605 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -599,6 +599,7 @@
     void finishUserStopping(final int userId, final UserState uss) {
         // On to the next.
         final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
+        shutdownIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         // This is the result receiver for the final shutdown broadcast.
         final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
             @Override
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 34cc6e3..d0d306c 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -121,8 +121,11 @@
                 resolutionStatus = RESOLUTION_FAILURE;
             }
         }
-        logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token,
-                resolutionStatus);
+        // Only log successful instant application resolution
+        if (resolutionStatus == RESOLUTION_SUCCESS) {
+            logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token,
+                    resolutionStatus);
+        }
         if (DEBUG_EPHEMERAL && resolveInfo == null) {
             if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) {
                 Log.d(TAG, "[" + token + "] Phase1; bind timed out");
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 75e7a1e..62b563a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -81,8 +81,10 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.Xml;
 
+import java.io.CharArrayWriter;
 import libcore.io.IoUtils;
 
 import com.android.internal.R;
@@ -195,7 +197,10 @@
 
     /** Historical sessions kept around for debugging purposes */
     @GuardedBy("mSessions")
-    private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
+    private final List<String> mHistoricalSessions = new ArrayList<>();
+
+    @GuardedBy("mSessions")
+    private final SparseIntArray mHistoricalSessionsByInstaller = new SparseIntArray();
 
     /** Sessions allocated to legacy users */
     @GuardedBy("mSessions")
@@ -371,7 +376,7 @@
                             // Since this is early during boot we don't send
                             // any observer events about the session, but we
                             // keep details around for dumpsys.
-                            mHistoricalSessions.put(session.sessionId, session);
+                            addHistoricalSessionLocked(session);
                         }
                         mAllocatedSessions.put(session.sessionId, true);
                     }
@@ -386,6 +391,18 @@
         }
     }
 
+    private void addHistoricalSessionLocked(PackageInstallerSession session) {
+        CharArrayWriter writer = new CharArrayWriter();
+        IndentingPrintWriter pw = new IndentingPrintWriter(writer, "    ");
+        session.dump(pw);
+        mHistoricalSessions.add(writer.toString());
+
+        // Increment the number of sessions by this installerUid.
+        mHistoricalSessionsByInstaller.put(
+                session.installerUid,
+                mHistoricalSessionsByInstaller.get(session.installerUid) + 1);
+    }
+
     private PackageInstallerSession readSessionLocked(XmlPullParser in) throws IOException,
             XmlPullParserException {
         final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
@@ -676,7 +693,7 @@
                 throw new IllegalStateException(
                         "Too many active sessions for UID " + callingUid);
             }
-            final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
+            final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
             if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
                 throw new IllegalStateException(
                         "Too many historical sessions for UID " + callingUid);
@@ -1228,8 +1245,7 @@
             pw.increaseIndent();
             N = mHistoricalSessions.size();
             for (int i = 0; i < N; i++) {
-                final PackageInstallerSession session = mHistoricalSessions.valueAt(i);
-                session.dump(pw);
+                pw.print(mHistoricalSessions.get(i));
                 pw.println();
             }
             pw.println();
@@ -1264,7 +1280,7 @@
                 public void run() {
                     synchronized (mSessions) {
                         mSessions.remove(session.sessionId);
-                        mHistoricalSessions.put(session.sessionId, session);
+                        addHistoricalSessionLocked(session);
 
                         final File appIconFile = buildAppIconFile(session.sessionId);
                         if (appIconFile.exists()) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e62b107..27107af 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -48,6 +48,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
@@ -17837,16 +17838,17 @@
 
         // Instant apps must have target SDK >= O and have targetSanboxVersion >= 2
         if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
-            Slog.w(TAG, "Instant app package " + pkg.packageName
-                    + " does not target O, this will be a fatal error.");
-            // STOPSHIP: Make this a fatal error
-            pkg.applicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
+            Slog.w(TAG, "Instant app package " + pkg.packageName + " does not target O");
+            res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
+                    "Instant app package must target O");
+            return;
         }
         if (instantApp && pkg.applicationInfo.targetSandboxVersion != 2) {
             Slog.w(TAG, "Instant app package " + pkg.packageName
-                    + " does not target targetSandboxVersion 2, this will be a fatal error.");
-            // STOPSHIP: Make this a fatal error
-            pkg.applicationInfo.targetSandboxVersion = 2;
+                    + " does not target targetSandboxVersion 2");
+            res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
+                    "Instant app package must use targetSanboxVersion 2");
+            return;
         }
 
         if (pkg.applicationInfo.isStaticSharedLibrary()) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c2c9123..01eabd8 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3072,13 +3072,18 @@
         if (PRINT_ANIM) Log.i(TAG, "selectAnimation in " + win
               + ": transit=" + transit);
         if (win == mStatusBar) {
-            boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
+            final boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
+            final boolean expanded = win.getAttrs().height == MATCH_PARENT
+                    && win.getAttrs().width == MATCH_PARENT;
+            if (isKeyguard || expanded) {
+                return -1;
+            }
             if (transit == TRANSIT_EXIT
                     || transit == TRANSIT_HIDE) {
-                return isKeyguard ? -1 : R.anim.dock_top_exit;
+                return R.anim.dock_top_exit;
             } else if (transit == TRANSIT_ENTER
                     || transit == TRANSIT_SHOW) {
-                return isKeyguard ? -1 : R.anim.dock_top_enter;
+                return R.anim.dock_top_enter;
             }
         } else if (win == mNavigationBar) {
             if (win.getAttrs().windowAnimations != 0) {
@@ -6803,7 +6808,9 @@
 
     @Override
     public boolean isScreenOn() {
-        return mScreenOnFully;
+        synchronized (mLock) {
+            return mScreenOnEarly;
+        }
     }
 
     /** {@inheritDoc} */
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 864e83e..02f2afc 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -256,7 +256,7 @@
         ProgressDialog pd = new ProgressDialog(context);
 
         // Path 1: Reboot to recovery for update
-        //   Condition: mReason == REBOOT_RECOVERY_UPDATE
+        //   Condition: mReason startswith REBOOT_RECOVERY_UPDATE
         //
         //  Path 1a: uncrypt needed
         //   Condition: if /cache/recovery/uncrypt_file exists but
@@ -276,7 +276,9 @@
         // Path 3: Regular reboot / shutdown
         //   Condition: Otherwise
         //   UI: spinning circle only (no progress bar)
-        if (PowerManager.REBOOT_RECOVERY_UPDATE.equals(mReason)) {
+
+        // mReason could be "recovery-update" or "recovery-update,quiescent".
+        if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
             // We need the progress bar if uncrypt will be invoked during the
             // reboot, which might be time-consuming.
             mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
@@ -295,7 +297,7 @@
                 pd.setMessage(context.getText(
                             com.android.internal.R.string.reboot_to_update_reboot));
             }
-        } else if (PowerManager.REBOOT_RECOVERY.equals(mReason)) {
+        } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
             // Factory reset path. Set the dialog message accordingly.
             pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
             pd.setMessage(context.getText(
@@ -389,7 +391,8 @@
         // First send the high-level shut down broadcast.
         mActionDone = false;
         Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
-        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         mContext.sendOrderedBroadcastAsUser(intent,
                 UserHandle.ALL, null, br, mHandler, 0, null, null);
 
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 410efcd..7d13889 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -20,6 +20,8 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.animation.AnimationHandler;
+import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
@@ -30,11 +32,13 @@
 import android.os.Debug;
 import android.util.ArrayMap;
 import android.util.Slog;
+import android.view.Choreographer;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.WindowManagerInternal;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -49,7 +53,7 @@
  *
  * The object that is resized needs to implement {@link BoundsAnimationTarget} interface.
  *
- * NOTE: All calls to methods in this class should be done on the UI thread
+ * NOTE: All calls to methods in this class should be done on the Animation thread
  */
 public class BoundsAnimationController {
     private static final boolean DEBUG_LOCAL = false;
@@ -111,20 +115,24 @@
     private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier();
     private final Interpolator mFastOutSlowInInterpolator;
     private boolean mFinishAnimationAfterTransition = false;
+    private final AnimationHandler mAnimationHandler;
 
     private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
 
-    BoundsAnimationController(Context context, AppTransition transition, Handler handler) {
+    BoundsAnimationController(Context context, AppTransition transition, Handler handler,
+            AnimationHandler animationHandler) {
         mHandler = handler;
         mAppTransition = transition;
         mAppTransition.registerListenerLocked(mAppTransitionNotifier);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
+        mAnimationHandler = animationHandler;
     }
 
     @VisibleForTesting
     final class BoundsAnimator extends ValueAnimator
             implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
+
         private final BoundsAnimationTarget mTarget;
         private final Rect mFrom = new Rect();
         private final Rect mTo = new Rect();
@@ -350,6 +358,14 @@
         public void onAnimationRepeat(Animator animation) {
             // Do nothing
         }
+
+        @Override
+        public AnimationHandler getAnimationHandler() {
+            if (mAnimationHandler != null) {
+                return mAnimationHandler;
+            }
+            return super.getAnimationHandler();
+        }
     }
 
     public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ccc8f63..54983c8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -314,6 +314,9 @@
     // the display's direct children should be allowed.
     private boolean mRemovingDisplay = false;
 
+    // {@code false} if this display is in the processing of being created.
+    private boolean mDisplayReady = false;
+
     private final WindowLayersController mLayersController;
     WallpaperController mWallpaperController;
     int mInputMethodAnimLayerAdjustment;
@@ -720,7 +723,6 @@
      */
     DisplayContent(Display display, WindowManagerService service,
             WindowLayersController layersController, WallpaperController wallpaperController) {
-
         if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
             throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
                     + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -748,6 +750,15 @@
 
         // Add itself as a child to the root container.
         mService.mRoot.addChild(this, null);
+
+        // TODO(b/62541591): evaluate whether this is the best spot to declare the
+        // {@link DisplayContent} ready for use.
+        mDisplayReady = true;
+    }
+
+    boolean isReady() {
+        // The display is ready when the system and the individual display are both ready.
+        return mService.mDisplayReady && mDisplayReady;
     }
 
     int getDisplayId() {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 03b5b827..aabf2be 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -108,7 +108,8 @@
     }
 
     void addDisplayLocked(final int displayId) {
-        // Create the DisplayContentsAnimator object by retrieving it.
+        // Create the DisplayContentsAnimator object by retrieving it if the associated
+        // {@link DisplayContent} exists.
         getDisplayContentsAnimatorLocked(displayId);
         if (displayId == DEFAULT_DISPLAY) {
             mInitialized = true;
@@ -356,8 +357,16 @@
     }
 
     private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
+        if (displayId < 0) {
+            return null;
+        }
+
         DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
-        if (displayAnimator == null) {
+
+        // It is possible that this underlying {@link DisplayContent} has been removed. In this
+        // case, we do not want to create an animator associated with it as {link #animate} will
+        // fail.
+        if (displayAnimator == null && mService.mRoot.getDisplayContent(displayId) != null) {
             displayAnimator = new DisplayContentsAnimator();
             mDisplayContentsAnimators.put(displayId, displayAnimator);
         }
@@ -365,8 +374,10 @@
     }
 
     void setScreenRotationAnimationLocked(int displayId, ScreenRotationAnimation animation) {
-        if (displayId >= 0) {
-            getDisplayContentsAnimatorLocked(displayId).mScreenRotationAnimation = animation;
+        final DisplayContentsAnimator animator = getDisplayContentsAnimatorLocked(displayId);
+
+        if (animator != null) {
+            animator.mScreenRotationAnimation = animation;
         }
     }
 
@@ -374,7 +385,9 @@
         if (displayId < 0) {
             return null;
         }
-        return getDisplayContentsAnimatorLocked(displayId).mScreenRotationAnimation;
+
+        DisplayContentsAnimator animator = getDisplayContentsAnimatorLocked(displayId);
+        return animator != null? animator.mScreenRotationAnimation : null;
     }
 
     void requestRemovalOfReplacedWindows(WindowState win) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 768b03a..9e3edef 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -103,6 +103,7 @@
 
 import android.Manifest;
 import android.Manifest.permission;
+import android.animation.AnimationHandler;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -211,6 +212,7 @@
 
 import com.android.internal.R;
 import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
@@ -1046,8 +1048,10 @@
         mAppTransition = new AppTransition(context, this);
         mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
 
+        final AnimationHandler animationHandler = new AnimationHandler();
+        animationHandler.setProvider(new SfVsyncFrameCallbackProvider());
         mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition,
-                UiThread.getHandler());
+                AnimationThread.getHandler(), animationHandler);
 
         mActivityManager = ActivityManager.getService();
         mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -5886,8 +5890,8 @@
             return;
         }
 
-        if (!mDisplayReady || !mPolicy.isScreenOn()) {
-            // No need to freeze the screen before the system is ready or if
+        if (!displayContent.isReady() || !mPolicy.isScreenOn()) {
+            // No need to freeze the screen before the display is ready, system is ready, or if
             // the screen is off.
             return;
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6f53099..cc39271 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -449,7 +449,7 @@
             // If '/cache/recovery/block.map' hasn't been created, stop the
             // reboot which will fail for sure, and get a chance to capture a
             // bugreport when that's still feasible. (Bug: 26444951)
-            if (PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
+            if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
                 File packageFile = new File(UNCRYPT_PACKAGE_FILE);
                 if (packageFile.exists()) {
                     String filename = null;
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index ee09f4b..9d32496 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -395,7 +395,7 @@
         mMockAppTransition = new MockAppTransition(context);
         mMockAnimator = new MockValueAnimator();
         mTarget = new TestBoundsAnimationTarget();
-        mController = new BoundsAnimationController(context, mMockAppTransition, handler);
+        mController = new BoundsAnimationController(context, mMockAppTransition, handler, null);
         mDriver = new BoundsAnimationDriver(mController, mTarget);
     }