Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-dev
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 5b259f7..5cfba3d 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -25,6 +25,7 @@
 import android.hardware.camera2.extension.IAdvancedExtenderImpl;
 import android.hardware.camera2.extension.ICameraExtensionsProxyService;
 import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
+import android.hardware.camera2.extension.IInitializeSessionCallback;
 import android.hardware.camera2.extension.IPreviewExtenderImpl;
 import android.hardware.camera2.extension.LatencyRange;
 import android.hardware.camera2.extension.SizeList;
@@ -357,6 +358,27 @@
             }
         }
 
+        public void initializeSession(IInitializeSessionCallback cb) throws RemoteException {
+            synchronized (mLock) {
+                if (mProxy != null) {
+                    mProxy.initializeSession(cb);
+                }
+            }
+        }
+
+        public void releaseSession() {
+            synchronized (mLock) {
+                if (mProxy != null) {
+                    try {
+                        mProxy.releaseSession();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Failed to release session! Extension service does"
+                                + " not respond!");
+                    }
+                }
+            }
+        }
+
         public boolean areAdvancedExtensionsSupported() {
             return mSupportsAdvancedExtensions;
         }
@@ -412,6 +434,20 @@
     /**
      * @hide
      */
+    public static void initializeSession(IInitializeSessionCallback cb) throws RemoteException {
+        CameraExtensionManagerGlobal.get().initializeSession(cb);
+    }
+
+    /**
+     * @hide
+     */
+    public static void releaseSession() {
+        CameraExtensionManagerGlobal.get().releaseSession();
+    }
+
+    /**
+     * @hide
+     */
     public static boolean areAdvancedExtensionsSupported() {
         return CameraExtensionManagerGlobal.get().areAdvancedExtensionsSupported();
     }
diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java
index e1b8177..5892f68 100644
--- a/core/java/android/hardware/camera2/CameraExtensionSession.java
+++ b/core/java/android/hardware/camera2/CameraExtensionSession.java
@@ -195,8 +195,9 @@
          * This method is called if the session cannot be configured as requested.
          *
          * <p>This can happen if the set of requested outputs contains unsupported sizes,
-         * too many outputs are requested at once or the camera device encounters an
-         * unrecoverable error during configuration.</p>
+         * too many outputs are requested at once or when trying to initialize multiple
+         * concurrent extension sessions from two (or more) separate camera devices
+         * or the camera device encounters an unrecoverable error during configuration.</p>
          *
          * <p>The session is considered to be closed, and all methods called on it after this
          * callback is invoked will throw an IllegalStateException.</p>
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index a1c8d29..5833b3d 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -162,6 +162,9 @@
      * <p>The set of combinations may include camera devices that may be in use by other camera API
      * clients.</p>
      *
+     * <p>Concurrent camera extension sessions {@link CameraExtensionSession} are not currently
+     * supported.</p>
+     *
      * <p>The set of combinations doesn't contain physical cameras that can only be used as
      * part of a logical multi-camera device.</p>
      *
diff --git a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
index bc29e9a..b52c6500 100644
--- a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
+++ b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
@@ -18,6 +18,7 @@
 import android.hardware.camera2.extension.IAdvancedExtenderImpl;
 import android.hardware.camera2.extension.IPreviewExtenderImpl;
 import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
+import android.hardware.camera2.extension.IInitializeSessionCallback;
 
 /** @hide */
 interface ICameraExtensionsProxyService
@@ -25,6 +26,8 @@
     long registerClient();
     void unregisterClient(long clientId);
     boolean advancedExtensionsSupported();
+    void initializeSession(in IInitializeSessionCallback cb);
+    void releaseSession();
     @nullable IPreviewExtenderImpl initializePreviewExtension(int extensionType);
     @nullable IImageCaptureExtenderImpl initializeImageExtension(int extensionType);
     @nullable IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType);
diff --git a/core/java/android/hardware/camera2/extension/IInitializeSessionCallback.aidl b/core/java/android/hardware/camera2/extension/IInitializeSessionCallback.aidl
new file mode 100644
index 0000000..1747760
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IInitializeSessionCallback.aidl
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+/** @hide */
+interface IInitializeSessionCallback
+{
+    void onSuccess();
+    void onFailure();
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 5cf50a2..bfc1f27 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -35,9 +35,11 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.extension.CameraOutputConfig;
 import android.hardware.camera2.extension.CameraSessionConfig;
+import android.hardware.camera2.extension.CaptureStageImpl;
 import android.hardware.camera2.extension.IAdvancedExtenderImpl;
 import android.hardware.camera2.extension.ICaptureCallback;
 import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.IInitializeSessionCallback;
 import android.hardware.camera2.extension.IRequestCallback;
 import android.hardware.camera2.extension.IRequestProcessorImpl;
 import android.hardware.camera2.extension.ISessionProcessorImpl;
@@ -89,6 +91,7 @@
     private Surface mClientCaptureSurface;
     private CameraCaptureSession mCaptureSession = null;
     private ISessionProcessorImpl mSessionProcessor = null;
+    private final InitializeSessionHandler mInitializeHandler;
 
     private boolean mInitialized;
 
@@ -181,6 +184,7 @@
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
         mInitialized = false;
+        mInitializeHandler = new InitializeSessionHandler();
     }
 
     /**
@@ -444,7 +448,6 @@
 
     public void release() {
         synchronized (mInterfaceLock) {
-            mInitialized = false;
             mHandlerThread.quitSafely();
 
             if (mSessionProcessor != null) {
@@ -459,7 +462,11 @@
 
             if (mExtensionClientId >= 0) {
                 CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
+                if (mInitialized) {
+                    CameraExtensionCharacteristics.releaseSession();
+                }
             }
+            mInitialized = false;
 
             for (ImageReader reader : mReaderMap.values()) {
                 reader.close();
@@ -512,17 +519,32 @@
 
         @Override
         public void onConfigured(@NonNull CameraCaptureSession session) {
-            boolean status = true;
             synchronized (mInterfaceLock) {
                 mCaptureSession = session;
                 try {
+                    CameraExtensionCharacteristics.initializeSession(mInitializeHandler);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to initialize session! Extension service does"
+                            + " not respond!");
+                    notifyConfigurationFailure();
+                }
+            }
+        }
+    }
+
+    private class InitializeSessionHandler extends IInitializeSessionCallback.Stub {
+        @Override
+        public void onSuccess() {
+            boolean status = true;
+            synchronized (mInterfaceLock) {
+                try {
                     mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
                     mInitialized = true;
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to start capture session,"
                             + " extension service does not respond!");
                     status = false;
-                    session.close();
+                    mCaptureSession.close();
                 }
             }
 
@@ -538,6 +560,15 @@
                 notifyConfigurationFailure();
             }
         }
+
+        @Override
+        public void onFailure() {
+            mCaptureSession.close();
+            Log.e(TAG, "Failed to initialize proxy service session!"
+                    + " This can happen when trying to configure multiple "
+                    + "concurrent extension sessions!");
+            notifyConfigurationFailure();
+        }
     }
 
     private final class RequestCallbackHandler extends ICaptureCallback.Stub {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 4bcc494..537b894 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -34,6 +34,7 @@
 import android.hardware.camera2.extension.CaptureStageImpl;
 import android.hardware.camera2.extension.ICaptureProcessorImpl;
 import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
+import android.hardware.camera2.extension.IInitializeSessionCallback;
 import android.hardware.camera2.extension.IPreviewExtenderImpl;
 import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
 import android.hardware.camera2.extension.ParcelImage;
@@ -48,6 +49,8 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.IInterface;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.annotation.NonNull;
@@ -80,6 +83,7 @@
     private final HandlerThread mHandlerThread;
     private final StateCallback mCallbacks;
     private final List<Size> mSupportedPreviewSizes;
+    private final InitializeSessionHandler mInitializeHandler;
 
     private CameraCaptureSession mCaptureSession = null;
     private Surface mCameraRepeatingSurface, mClientRepeatingRequestSurface;
@@ -216,6 +220,7 @@
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
         mInitialized = false;
+        mInitializeHandler = new InitializeSessionHandler();
     }
 
     private void initializeRepeatingRequestPipeline() throws RemoteException {
@@ -621,7 +626,6 @@
     public void release() {
         synchronized (mInterfaceLock) {
             mInternalRepeatingRequestEnabled = false;
-            mInitialized = false;
             mHandlerThread.quitSafely();
 
             try {
@@ -634,7 +638,11 @@
 
             if (mExtensionClientId >= 0) {
                 CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
+                if (mInitialized) {
+                    CameraExtensionCharacteristics.releaseSession();
+                }
             }
+            mInitialized = false;
 
             if (mRepeatingRequestImageCallback != null) {
                 mRepeatingRequestImageCallback.close();
@@ -739,36 +747,63 @@
 
         @Override
         public void onConfigured(@NonNull CameraCaptureSession session) {
-            boolean status = true;
             synchronized (mInterfaceLock) {
                 mCaptureSession = session;
-
-                ArrayList<CaptureStageImpl> initialRequestList = compileInitialRequestList();
-                if (!initialRequestList.isEmpty()) {
-                    try {
-                        setInitialCaptureRequest(initialRequestList,
-                                new InitialRequestHandler(mRepeatingRequestImageCallback));
-                    } catch (CameraAccessException e) {
-                        Log.e(TAG, "Failed to initialize the initial capture request!");
-                        status = false;
-                    }
-                } else {
-                    try {
-                        setRepeatingRequest(mPreviewExtender.getCaptureStage(),
-                                new RepeatingRequestHandler(null, null, null,
-                                        mRepeatingRequestImageCallback));
-                    } catch (CameraAccessException | RemoteException e) {
-                        Log.e(TAG, "Failed to initialize internal repeating request!");
-                        status = false;
-                    }
-
+                try {
+                    CameraExtensionCharacteristics.initializeSession(mInitializeHandler);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to initialize session! Extension service does"
+                            + " not respond!");
+                    notifyConfigurationFailure();
                 }
             }
+        }
+    }
+
+    private class InitializeSessionHandler extends IInitializeSessionCallback.Stub {
+        @Override
+        public void onSuccess() {
+            boolean status = true;
+            ArrayList<CaptureStageImpl> initialRequestList =
+                    compileInitialRequestList();
+            if (!initialRequestList.isEmpty()) {
+                try {
+                    setInitialCaptureRequest(initialRequestList,
+                            new InitialRequestHandler(
+                                    mRepeatingRequestImageCallback));
+                } catch (CameraAccessException e) {
+                    Log.e(TAG,
+                            "Failed to initialize the initial capture "
+                                    + "request!");
+                    status = false;
+                }
+            } else {
+                try {
+                    setRepeatingRequest(mPreviewExtender.getCaptureStage(),
+                            new RepeatingRequestHandler(null, null, null,
+                                    mRepeatingRequestImageCallback));
+                } catch (CameraAccessException | RemoteException e) {
+                    Log.e(TAG,
+                            "Failed to initialize internal repeating "
+                                    + "request!");
+                    status = false;
+                }
+
+            }
 
             if (!status) {
                 notifyConfigurationFailure();
             }
         }
+
+        @Override
+        public void onFailure() {
+            mCaptureSession.close();
+            Log.e(TAG, "Failed to initialize proxy service session!"
+                    + " This can happen when trying to configure multiple "
+                    + "concurrent extension sessions!");
+            notifyConfigurationFailure();
+        }
     }
 
     private class BurstRequestHandler extends CameraCaptureSession.CaptureCallback {
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index f2827f3..472e3e7 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -98,12 +98,28 @@
     private static final double VELOCITY_THRESHOLD = 0.01;
 
     /**
+     * The speed at which we should start linearly interpolating to the destination.
+     * When using a spring, as it gets closer to the destination, the speed drops off exponentially.
+     * Instead of landing very slowly, a better experience is achieved if the final
+     * destination is arrived at quicker.
+     */
+    private static final float LINEAR_VELOCITY_TAKE_OVER = 200f;
+
+    /**
      * The value threshold before the spring animation is considered close enough to
      * the destination to be settled. This should be around 0.01 pixel.
      */
     private static final double VALUE_THRESHOLD = 0.001;
 
     /**
+     * The maximum distance at which we should start linearly interpolating to the destination.
+     * When using a spring, as it gets closer to the destination, the speed drops off exponentially.
+     * Instead of landing very slowly, a better experience is achieved if the final
+     * destination is arrived at quicker.
+     */
+    private static final double LINEAR_DISTANCE_TAKE_OVER = 8.0;
+
+    /**
      * The natural frequency of the stretch spring.
      */
     private static final double NATURAL_FREQUENCY = 24.657;
@@ -587,55 +603,57 @@
             if (mState == STATE_RECEDE) {
                 updateSpring();
             }
-            RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
-            if (mTmpMatrix == null) {
-                mTmpMatrix = new Matrix();
-                mTmpPoints = new float[12];
-            }
-            //noinspection deprecation
-            recordingCanvas.getMatrix(mTmpMatrix);
+            if (mDistance != 0f) {
+                RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+                if (mTmpMatrix == null) {
+                    mTmpMatrix = new Matrix();
+                    mTmpPoints = new float[12];
+                }
+                //noinspection deprecation
+                recordingCanvas.getMatrix(mTmpMatrix);
 
-            mTmpPoints[0] = 0;
-            mTmpPoints[1] = 0; // top-left
-            mTmpPoints[2] = mWidth;
-            mTmpPoints[3] = 0; // top-right
-            mTmpPoints[4] = mWidth;
-            mTmpPoints[5] = mHeight; // bottom-right
-            mTmpPoints[6] = 0;
-            mTmpPoints[7] = mHeight; // bottom-left
-            mTmpPoints[8] = mWidth * mDisplacement;
-            mTmpPoints[9] = 0; // drag start point
-            mTmpPoints[10] = mWidth * mDisplacement;
-            mTmpPoints[11] = mHeight * mDistance; // drag point
-            mTmpMatrix.mapPoints(mTmpPoints);
+                mTmpPoints[0] = 0;
+                mTmpPoints[1] = 0; // top-left
+                mTmpPoints[2] = mWidth;
+                mTmpPoints[3] = 0; // top-right
+                mTmpPoints[4] = mWidth;
+                mTmpPoints[5] = mHeight; // bottom-right
+                mTmpPoints[6] = 0;
+                mTmpPoints[7] = mHeight; // bottom-left
+                mTmpPoints[8] = mWidth * mDisplacement;
+                mTmpPoints[9] = 0; // drag start point
+                mTmpPoints[10] = mWidth * mDisplacement;
+                mTmpPoints[11] = mHeight * mDistance; // drag point
+                mTmpMatrix.mapPoints(mTmpPoints);
 
-            RenderNode renderNode = recordingCanvas.mNode;
+                RenderNode renderNode = recordingCanvas.mNode;
 
-            float left = renderNode.getLeft()
+                float left = renderNode.getLeft()
                     + min(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]);
-            float top = renderNode.getTop()
+                float top = renderNode.getTop()
                     + min(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]);
-            float right = renderNode.getLeft()
+                float right = renderNode.getLeft()
                     + max(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]);
-            float bottom = renderNode.getTop()
+                float bottom = renderNode.getTop()
                     + max(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]);
-            // assume rotations of increments of 90 degrees
-            float x = mTmpPoints[10] - mTmpPoints[8];
-            float width = right - left;
-            float vecX = dampStretchVector(Math.max(-1f, Math.min(1f, x / width)));
+                // assume rotations of increments of 90 degrees
+                float x = mTmpPoints[10] - mTmpPoints[8];
+                float width = right - left;
+                float vecX = dampStretchVector(Math.max(-1f, Math.min(1f, x / width)));
 
-            float y = mTmpPoints[11] - mTmpPoints[9];
-            float height = bottom - top;
-            float vecY = dampStretchVector(Math.max(-1f, Math.min(1f, y / height)));
+                float y = mTmpPoints[11] - mTmpPoints[9];
+                float height = bottom - top;
+                float vecY = dampStretchVector(Math.max(-1f, Math.min(1f, y / height)));
 
-            boolean hasValidVectors = Float.isFinite(vecX) && Float.isFinite(vecY);
-            if (right > left && bottom > top && mWidth > 0 && mHeight > 0 && hasValidVectors) {
-                renderNode.stretch(
+                boolean hasValidVectors = Float.isFinite(vecX) && Float.isFinite(vecY);
+                if (right > left && bottom > top && mWidth > 0 && mHeight > 0 && hasValidVectors) {
+                    renderNode.stretch(
                         vecX, // horizontal stretch intensity
                         vecY, // vertical stretch intensity
                         mWidth, // max horizontal stretch in pixels
                         mHeight // max vertical stretch in pixels
-                );
+                    );
+                }
             }
         } else {
             // Animations have been disabled or this is TYPE_STRETCH and drawing into a Canvas
@@ -730,6 +748,26 @@
         if (deltaT < 0.001f) {
             return; // Must have at least 1 ms difference
         }
+        mStartTime = time;
+
+        if (Math.abs(mVelocity) <= LINEAR_VELOCITY_TAKE_OVER
+                && Math.abs(mDistance * mHeight) < LINEAR_DISTANCE_TAKE_OVER
+                && Math.signum(mVelocity) == -Math.signum(mDistance)
+        ) {
+            // This is close. The spring will slowly reach the destination. Instead, we
+            // will interpolate linearly so that it arrives at its destination quicker.
+            mVelocity = Math.signum(mVelocity) * LINEAR_VELOCITY_TAKE_OVER;
+
+            float targetDistance = mDistance + (mVelocity * deltaT / mHeight);
+            if (Math.signum(targetDistance) != Math.signum(mDistance)) {
+                // We have arrived
+                mDistance = 0;
+                mVelocity = 0;
+            } else {
+                mDistance = targetDistance;
+            }
+            return;
+        }
         final double mDampedFreq = NATURAL_FREQUENCY * Math.sqrt(1 - DAMPING_RATIO * DAMPING_RATIO);
 
         // We're always underdamped, so we can use only those equations:
@@ -745,9 +783,7 @@
                 + mDampedFreq * sinCoeff * Math.cos(mDampedFreq * deltaT));
         mDistance = (float) distance / mHeight;
         mVelocity = (float) velocity;
-        mStartTime = time;
         if (isAtEquilibrium()) {
-            mState = STATE_IDLE;
             mDistance = 0;
             mVelocity = 0;
         }
diff --git a/core/java/com/android/server/backup/AccountManagerBackupHelper.java b/core/java/com/android/server/backup/AccountManagerBackupHelper.java
index 39b18c0..f76dd09 100644
--- a/core/java/com/android/server/backup/AccountManagerBackupHelper.java
+++ b/core/java/com/android/server/backup/AccountManagerBackupHelper.java
@@ -56,7 +56,7 @@
                 }
             }
         } catch (Exception e) {
-            Slog.e(TAG, "Unable to store payload " + key);
+            Slog.e(TAG, "Unable to store payload " + key, e);
         }
 
         return new byte[0];
@@ -79,7 +79,7 @@
                 }
             }
         } catch (Exception e) {
-            Slog.w(TAG, "Unable to restore key " + key);
+            Slog.e(TAG, "Unable to restore key " + key, e);
         }
     }
 }
diff --git a/core/java/com/android/server/backup/NotificationBackupHelper.java b/core/java/com/android/server/backup/NotificationBackupHelper.java
index 7d4f8f7..faa0509 100644
--- a/core/java/com/android/server/backup/NotificationBackupHelper.java
+++ b/core/java/com/android/server/backup/NotificationBackupHelper.java
@@ -49,7 +49,7 @@
                 newPayload = nm.getBackupPayload(mUserId);
             } catch (Exception e) {
                 // Treat as no data
-                Slog.e(TAG, "Couldn't communicate with notification manager");
+                Slog.e(TAG, "Couldn't communicate with notification manager", e);
                 newPayload = null;
             }
         }
@@ -68,7 +68,7 @@
                         ServiceManager.getService("notification"));
                 nm.applyRestore(payload, mUserId);
             } catch (Exception e) {
-                Slog.e(TAG, "Couldn't communicate with notification manager");
+                Slog.e(TAG, "Couldn't communicate with notification manager", e);
             }
         }
     }
diff --git a/core/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java
index 4d1949e..ec5e251 100644
--- a/core/java/com/android/server/backup/PermissionBackupHelper.java
+++ b/core/java/com/android/server/backup/PermissionBackupHelper.java
@@ -59,7 +59,7 @@
                     Slog.w(TAG, "Unexpected backup key " + key);
             }
         } catch (Exception e) {
-            Slog.e(TAG, "Unable to store payload " + key);
+            Slog.e(TAG, "Unable to store payload " + key, e);
         }
         return null;
     }
@@ -79,7 +79,7 @@
                     Slog.w(TAG, "Unexpected restore key " + key);
             }
         } catch (Exception e) {
-            Slog.w(TAG, "Unable to restore key " + key);
+            Slog.e(TAG, "Unable to restore key " + key, e);
         }
     }
 }
diff --git a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
index 503c719..47e0c07 100644
--- a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
+++ b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
@@ -95,7 +95,7 @@
                     Slog.w(TAG, "Unexpected backup key " + key);
             }
         } catch (Exception e) {
-            Slog.e(TAG, "Unable to store payload " + key);
+            Slog.e(TAG, "Unable to store payload " + key, e);
         }
         return null;
     }
@@ -124,7 +124,7 @@
                     Slog.w(TAG, "Unexpected restore key " + key);
             }
         } catch (Exception e) {
-            Slog.w(TAG, "Unable to restore key " + key);
+            Slog.e(TAG, "Unable to restore key " + key, e);
         }
     }
 }
diff --git a/core/java/com/android/server/backup/SliceBackupHelper.java b/core/java/com/android/server/backup/SliceBackupHelper.java
index 8e5a5ee..77517b3 100644
--- a/core/java/com/android/server/backup/SliceBackupHelper.java
+++ b/core/java/com/android/server/backup/SliceBackupHelper.java
@@ -48,7 +48,7 @@
                 newPayload = sm.getBackupPayload(UserHandle.USER_SYSTEM);
             } catch (Exception e) {
                 // Treat as no data
-                Slog.e(TAG, "Couldn't communicate with slice manager");
+                Slog.e(TAG, "Couldn't communicate with slice manager", e);
                 newPayload = null;
             }
         }
@@ -66,7 +66,7 @@
                 // TODO: http://b/22388012
                 sm.applyRestore(payload, UserHandle.USER_SYSTEM);
             } catch (Exception e) {
-                Slog.e(TAG, "Couldn't communicate with slice manager");
+                Slog.e(TAG, "Couldn't communicate with slice manager", e);
             }
         }
     }
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 3eab9f0..7eb6404 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -40,9 +40,7 @@
 
     StretchEffect() {}
 
-    bool isEmpty() const {
-        return MathUtils::isZero(mStretchDirection.x()) && MathUtils::isZero(mStretchDirection.y());
-    }
+    bool isEmpty() const { return isZero(mStretchDirection.x()) && isZero(mStretchDirection.y()); }
 
     void setEmpty() {
         *this = StretchEffect{};
@@ -118,6 +116,18 @@
     }
 
 private:
+    // The epsilon for StretchEffect is less than in MathUtils because
+    // the range is 0-1 for an entire screen and should be significantly
+    // less than 1 pixel for a smooth stretch animation.
+    inline static bool isZero(float value) {
+        // Using fabsf is more performant as ARM computes
+        // fabsf in a single instruction.
+        return fabsf(value) <= NON_ZERO_EPSILON;
+    }
+    // This should be good for 1/25,000 of a screen and should be good for
+    // screens with less than ~8000 pixels in one dimension with only 1/4 pixel
+    // cut-off.
+    static constexpr float NON_ZERO_EPSILON = 0.00004f;
     static sk_sp<SkRuntimeEffect> getStretchEffect();
     mutable SkVector mStretchDirection{0, 0};
     mutable std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index fa4806d..395a9a7 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -41,6 +41,7 @@
 
     private static final int TOOLBAR_MAX_LINE_NUMBER = 2;
     private static final int FULLY_EXPANDED_OFFSET = 0;
+    private static final float TOOLBAR_LINE_SPACING_MULTIPLIER = 1.1f;
     private static final String KEY_IS_TOOLBAR_COLLAPSED = "is_toolbar_collapsed";
 
     @Nullable
@@ -194,6 +195,8 @@
                             getResources().getDimensionPixelSize(
                                     R.dimen.scrim_visible_height_trigger_three_lines));
                     mCollapsingToolbarLayout.setLayoutParams(lp);
+                    mCollapsingToolbarLayout
+                            .setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
                 } else if (count == TOOLBAR_MAX_LINE_NUMBER) {
                     final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams();
                     lp.height = getResources()
@@ -202,6 +205,8 @@
                             getResources().getDimensionPixelSize(
                                     R.dimen.scrim_visible_height_trigger_two_lines));
                     mCollapsingToolbarLayout.setLayoutParams(lp);
+                    mCollapsingToolbarLayout
+                            .setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
                 }
             }
         });
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 3f4baaf..50f38b6 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -78,6 +78,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:scaleType="matrix"
+        android:visibility="invisible"
         app:layout_constraintTop_toTopOf="@id/preview"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index 9314685..892f64b 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -29,7 +29,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
         <include layout="@layout/people_tile_punctuation_background_medium" />
-        <include layout="@layout/people_tile_punctuation_background_medium" />
+        <include layout="@layout/people_tile_emoji_background_medium" />
         <include layout="@layout/people_status_scrim_layout" />
         <LinearLayout
             android:id="@+id/content"
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 221bb15..844a8c6 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -121,6 +121,8 @@
 
     private static final int MESSAGES_COUNT_OVERFLOW = 6;
 
+    private static final CharSequence EMOJI_CAKE = "\ud83c\udf82";
+
     private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
     private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
     private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
@@ -704,6 +706,10 @@
         views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
         views.setTextViewText(R.id.text_content, statusText);
 
+        if (status.getActivity() == ACTIVITY_BIRTHDAY) {
+            setEmojiBackground(views, EMOJI_CAKE);
+        }
+
         Icon statusIcon = status.getIcon();
         if (statusIcon != null) {
             // No text content styled text on medium or large.
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 07f6d36..741dddc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -200,7 +200,6 @@
                         / (float) mLongScreenshot.getHeight());
 
         mEnterTransitionView.setImageDrawable(drawable);
-
         mEnterTransitionView.getViewTreeObserver().addOnPreDrawListener(
                 new ViewTreeObserver.OnPreDrawListener() {
                     @Override
@@ -220,7 +219,6 @@
                                         mCropView.animateEntrance();
                                         mCropView.setVisibility(View.VISIBLE);
                                         setButtonsEnabled(true);
-                                        mEnterTransitionView.setVisibility(View.GONE);
                                     });
                         });
                         return true;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 24cca91..63ecc0b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -33,6 +33,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ExitTransitionCoordinator;
 import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks;
@@ -253,6 +254,7 @@
     private final DisplayManager mDisplayManager;
     private final ScrollCaptureController mScrollCaptureController;
     private final LongScreenshotData mLongScreenshotHolder;
+    private final boolean mIsLowRamDevice;
 
     private ScreenshotView mScreenshotView;
     private Bitmap mScreenBitmap;
@@ -297,7 +299,8 @@
             ImageExporter imageExporter,
             @Main Executor mainExecutor,
             ScrollCaptureController scrollCaptureController,
-            LongScreenshotData longScreenshotHolder) {
+            LongScreenshotData longScreenshotHolder,
+            ActivityManager activityManager) {
         mScreenshotSmartActions = screenshotSmartActions;
         mNotificationsController = screenshotNotificationsController;
         mScrollCaptureClient = scrollCaptureClient;
@@ -306,6 +309,7 @@
         mMainExecutor = mainExecutor;
         mScrollCaptureController = scrollCaptureController;
         mLongScreenshotHolder = longScreenshotHolder;
+        mIsLowRamDevice = activityManager.isLowRamDevice();
         mBgExecutor = Executors.newSingleThreadExecutor();
 
         mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
@@ -621,6 +625,10 @@
     }
 
     private void requestScrollCapture() {
+        if (!allowLongScreenshots()) {
+            Log.d(TAG, "Long screenshots not supported on this device");
+            return;
+        }
         mScrollCaptureClient.setHostWindowToken(mWindow.getDecorView().getWindowToken());
         if (mLastScrollCaptureRequest != null) {
             mLastScrollCaptureRequest.cancel(true);
@@ -682,12 +690,8 @@
                         intent.setFlags(
                                 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
 
-                        Pair<ActivityOptions, ExitTransitionCoordinator> transition =
-                                ActivityOptions.startSharedElementAnimation(mWindow,
-                                        new ScreenshotExitTransitionCallbacksSupplier(false).get(),
-                                        null);
-                        transition.second.startExit();
-                        mContext.startActivity(intent, transition.first.toBundle());
+                        mContext.startActivity(intent,
+                                ActivityOptions.makeCustomAnimation(mContext, 0, 0).toBundle());
                         RemoteAnimationAdapter runner = new RemoteAnimationAdapter(
                                 SCREENSHOT_REMOTE_RUNNER, 0, 0);
                         try {
@@ -982,6 +986,10 @@
         return mDisplayManager.getDisplay(DEFAULT_DISPLAY);
     }
 
+    private boolean allowLongScreenshots() {
+        return !mIsLowRamDevice;
+    }
+
     /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
     private static boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets,
             Rect screenBounds) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 41af80e..cfe95e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -71,6 +71,7 @@
     private final DozeParameters mDozeParameters;
     private final Optional<Bubbles> mBubblesOptional;
     private final StatusBarWindowController mStatusBarWindowController;
+    private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
 
     private int mIconSize;
     private int mIconHPadding;
@@ -119,7 +120,8 @@
             Optional<Bubbles> bubblesOptional,
             DemoModeController demoModeController,
             DarkIconDispatcher darkIconDispatcher,
-            StatusBarWindowController statusBarWindowController) {
+            StatusBarWindowController statusBarWindowController,
+            UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
         mContrastColorUtil = ContrastColorUtil.getInstance(context);
         mContext = context;
         mStatusBarStateController = statusBarStateController;
@@ -133,6 +135,7 @@
         mDemoModeController = demoModeController;
         mDemoModeController.addCallback(this);
         mStatusBarWindowController = statusBarWindowController;
+        mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         notificationListener.addNotificationSettingsListener(mSettingsListener);
 
         initializeNotificationAreaViews(context);
@@ -677,7 +680,12 @@
         }
         boolean visible = mBypassController.getBypassEnabled()
                 || mWakeUpCoordinator.getNotificationsFullyHidden();
-        if (mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
+
+        // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off animation is
+        // playing, in which case we want them to be visible since we're animating in the AOD UI and
+        // will be switching to KEYGUARD shortly.
+        if (mStatusBarStateController.getState() != StatusBarState.KEYGUARD
+                && !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()) {
             visible = false;
         }
         if (visible && mWakeUpCoordinator.isPulseExpanding()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index c958796..98fb6f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -99,6 +99,7 @@
             mCallbacks = Lists.newArrayList();
 
     private final SysuiColorExtractor mColorExtractor;
+    private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     private float mFaceAuthDisplayBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
 
     @Inject
@@ -110,7 +111,8 @@
             KeyguardBypassController keyguardBypassController,
             SysuiColorExtractor colorExtractor,
             DumpManager dumpManager,
-            KeyguardStateController keyguardStateController) {
+            KeyguardStateController keyguardStateController,
+            UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
         mContext = context;
         mWindowManager = windowManager;
         mActivityManager = activityManager;
@@ -121,6 +123,7 @@
         mKeyguardViewMediator = keyguardViewMediator;
         mKeyguardBypassController = keyguardBypassController;
         mColorExtractor = colorExtractor;
+        mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         dumpManager.registerDumpable(getClass().getName(), this);
 
         mLockScreenDisplayTimeout = context.getResources()
@@ -300,7 +303,11 @@
     private void applyFocusableFlag(State state) {
         boolean panelFocusable = state.mNotificationShadeFocusable && state.mPanelExpanded;
         if (state.mBouncerShowing && (state.mKeyguardOccluded || state.mKeyguardNeedsInput)
-                || ENABLE_REMOTE_INPUT && state.mRemoteInputActive) {
+                || ENABLE_REMOTE_INPUT && state.mRemoteInputActive
+                // Make the panel focusable if we're doing the screen off animation, since the light
+                // reveal scrim is drawing in the panel and should consume touch events so that they
+                // don't go to the app behind.
+                || mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()) {
             mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
             mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 407b248..5de7846a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -1127,7 +1127,12 @@
                     .alpha(0.f)
                     .setStartDelay(0)
                     .setDuration(mDialogHideAnimationDurationMs)
-                    .withEndAction(() -> mODICaptionsTooltipView.setVisibility(INVISIBLE))
+                    .withEndAction(() -> {
+                        // It might have been nulled out by tryToRemoveCaptionsTooltip.
+                        if (mODICaptionsTooltipView != null) {
+                            mODICaptionsTooltipView.setVisibility(INVISIBLE);
+                        }
+                    })
                     .start();
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
index 5f8eb9e..25aa93a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -66,6 +66,8 @@
     DarkIconDispatcher mDarkIconDispatcher;
     @Mock
     StatusBarWindowController mStatusBarWindowController;
+    @Mock
+    UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     private NotificationIconAreaController mController;
     @Mock
     private Bubbles mBubbles;
@@ -87,7 +89,8 @@
                 Optional.of(mBubbles),
                 mDemoModeController,
                 mDarkIconDispatcher,
-                mStatusBarWindowController);
+                mStatusBarWindowController,
+                mUnlockedScreenOffAnimationController);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index 9fe47ec..b03712c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -72,6 +72,7 @@
     @Mock ColorExtractor.GradientColors mGradientColors;
     @Mock private DumpManager mDumpManager;
     @Mock private KeyguardStateController mKeyguardStateController;
+    @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
 
     private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
@@ -85,7 +86,8 @@
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
                 mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
-                mColorExtractor, mDumpManager, mKeyguardStateController);
+                mColorExtractor, mDumpManager, mKeyguardStateController,
+                mUnlockedScreenOffAnimationController);
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
 
         mNotificationShadeWindowController.attach();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 9fa35f8..eb9206d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -98,6 +98,7 @@
 import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -233,6 +234,8 @@
     private ShellTaskOrganizer mShellTaskOrganizer;
     @Mock
     private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
 
     private TestableBubblePositioner mPositioner;
 
@@ -255,7 +258,8 @@
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
                 mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
-                mColorExtractor, mDumpManager, mKeyguardStateController);
+                mColorExtractor, mDumpManager, mKeyguardStateController,
+                mUnlockedScreenOffAnimationController);
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
         mNotificationShadeWindowController.attach();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 55cc8bb..24a5b3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -81,6 +81,7 @@
 import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -197,6 +198,8 @@
     private ShellTaskOrganizer mShellTaskOrganizer;
     @Mock
     private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
 
     private TestableBubblePositioner mPositioner;
 
@@ -218,7 +221,8 @@
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
                 mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
-                mColorExtractor, mDumpManager, mKeyguardStateController);
+                mColorExtractor, mDumpManager, mKeyguardStateController,
+                mUnlockedScreenOffAnimationController);
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
         mNotificationShadeWindowController.attach();
 
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 69874f8..ab3643c 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -18,6 +18,7 @@
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.hardware.camera2.CameraAccessException;
@@ -42,6 +43,7 @@
 import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
 import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
 import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.IInitializeSessionCallback;
 import android.hardware.camera2.extension.ISessionProcessorImpl;
 import android.hardware.camera2.extension.LatencyRange;
 import android.hardware.camera2.extension.OutputConfigId;
@@ -57,6 +59,7 @@
 import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
 import android.media.Image;
 import android.media.ImageReader;
+import android.os.Binder;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerExecutor;
@@ -176,6 +179,7 @@
 
         private long mCurrentClientCount = 0;
         private ArraySet<Long> mActiveClients = new ArraySet<>();
+        private IInitializeSessionCallback mInitializeCb = null;
 
         // Singleton instance
         private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER =
@@ -328,6 +332,40 @@
                 }
             }
         }
+
+        private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+            @Override
+            public void binderDied() {
+                synchronized (mLock) {
+                    mInitializeCb = null;
+                }
+            }
+        };
+
+        public boolean initializeSession(IInitializeSessionCallback cb) {
+            synchronized (mLock) {
+                if (mInitializeCb == null) {
+                    mInitializeCb = cb;
+                    try {
+                        mInitializeCb.asBinder().linkToDeath(mDeathRecipient, 0);
+                    } catch (RemoteException e) {
+                        e.printStackTrace();
+                    }
+                } else {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public void releaseSession() {
+            synchronized (mLock) {
+                if (mInitializeCb != null) {
+                    mInitializeCb.asBinder().unlinkToDeath(mDeathRecipient, 0);
+                    mInitializeCb = null;
+                }
+            }
+        }
     }
 
     /**
@@ -353,6 +391,26 @@
     /**
      * @hide
      */
+    public static boolean initializeSession(IInitializeSessionCallback cb) {
+        if (!EXTENSIONS_PRESENT) {
+            return false;
+        }
+        return CameraExtensionManagerGlobal.get().initializeSession(cb);
+    }
+
+    /**
+     * @hide
+     */
+    public static void releaseSession() {
+        if (!EXTENSIONS_PRESENT) {
+            return;
+        }
+        CameraExtensionManagerGlobal.get().releaseSession();
+    }
+
+    /**
+     * @hide
+     */
     public static Pair<PreviewExtenderImpl, ImageCaptureExtenderImpl> initializeExtension(
             int extensionType) {
         switch (extensionType) {
@@ -538,6 +596,39 @@
             CameraExtensionsProxyService.unregisterClient(clientId);
         }
 
+        private boolean checkCameraPermission() {
+            int allowed = CameraExtensionsProxyService.this.checkPermission(
+                    android.Manifest.permission.CAMERA, Binder.getCallingPid(),
+                    Binder.getCallingUid());
+            return (PackageManager.PERMISSION_GRANTED == allowed);
+        }
+
+        @Override
+        public void initializeSession(IInitializeSessionCallback cb) {
+            try {
+                if (!checkCameraPermission()) {
+                    Log.i(TAG, "Camera permission required for initializing capture session");
+                    cb.onFailure();
+                    return;
+                }
+
+                if (CameraExtensionsProxyService.initializeSession(cb)) {
+                    cb.onSuccess();
+                } else {
+                    cb.onFailure();
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Client doesn't respond!");
+            }
+        }
+
+        @Override
+        public void releaseSession() {
+            if (checkCameraPermission()) {
+                CameraExtensionsProxyService.releaseSession();
+            }
+        }
+
         @Override
         public boolean advancedExtensionsSupported() {
             return ADVANCED_API_SUPPORTED;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index bba50a7..c3fc995 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5477,8 +5477,8 @@
             getPendingTransaction().setMatrix(getSurfaceControl(),
                 newHScale, 0, 0, newVScale);
             mLastGlobalScale = mGlobalScale;
-            mLastHScale = mHScale;
-            mLastVScale = mVScale;
+            mLastHScale = newHScale;
+            mLastVScale = newVScale;
         }
     }