Merge "Avoid unwanted wakeAndUnLock mode for face" into qt-r1-dev
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index a8b99ce..a527263 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -564,7 +564,8 @@
     int skipped[] = SKIPPED_SECTIONS;
     for (const Section** section = SECTION_LIST; *section; section++) {
         const int id = (*section)->id;
-        if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)) {
+        if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)
+                && !section_requires_specific_mention(id)) {
             incidentArgs.addSection(id);
         }
     }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3f7b291..507e97e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -275,7 +275,6 @@
      * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS}, optional</li>
      * </ul>
      *
      * <p>When device owner provisioning has completed, an intent of the type
@@ -383,7 +382,6 @@
      * <li>{@link #EXTRA_PROVISIONING_ORGANIZATION_NAME}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_USE_MOBILE_DATA}, optional </li>
-     * <li>{@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS}, optional</li>
      * </ul>
      *
      * @hide
@@ -423,7 +421,6 @@
      * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS}, optional</li>
      * </ul>
      *
      * <p>When device owner provisioning has completed, an intent of the type
@@ -1149,11 +1146,11 @@
      * A boolean extra indicating if the education screens from the provisioning flow should be
      * skipped. If unspecified, defaults to {@code false}.
      *
+     * <p>This extra can only be set by the admin app when performing the admin-integrated
+     * provisioning flow as a result of the {@link #ACTION_GET_PROVISIONING_MODE} activity.
+     *
      * <p>If the education screens are skipped, it is the admin application's responsibility
      * to display its own user education screens.
-     *
-     * <p>It can be used when provisioning a fully managed device via
-     * {@link #ACTION_PROVISION_MANAGED_DEVICE}.
      */
     public static final String EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS =
             "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS";
@@ -2263,6 +2260,10 @@
      *     <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li>
      * </ul>
      *
+     * <p>If performing fully-managed device provisioning and the admin app desires to show its
+     * own education screens, the target activity can additionally return
+     * {@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS} set to <code>true</code>.
+     *
      * <p>The target activity may also return the account that needs to be migrated from primary
      * user to managed profile in case of a profile owner provisioning in
      * {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} as result.
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 5ff627f..12edc5e 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -119,12 +119,50 @@
     public static final int TYPE_VEC3_CALIBRATION = 0x10002;
 
     /**
-     * Sensor placement. Describes location and installation angle of the sensor device.
+     * Sensor placement.
      *
-     * Payload:
-     *     floatValues[0..11]: First 3 rows of homogeneous matrix in row major order that describes
-     *     the location and orientation of the sensor. Origin of reference will be the mobile device
-     *     geometric sensor. Reference frame is defined as the same as Android sensor frame.
+     * Provides the orientation and location of the sensor element in terms of the
+     * Android coordinate system. This data is given as a 3x4 matrix consisting of a 3x3 rotation
+     * matrix (R) concatenated with a 3x1 location vector (t). The rotation matrix provides the
+     * orientation of the Android device coordinate frame relative to the local coordinate frame of
+     * the sensor. Note that assuming the axes conventions of the sensor are the same as Android,
+     * this is the inverse of the matrix applied to raw samples read from the sensor to convert them
+     * into the Android representation. The location vector represents the translation from the
+     * origin of the Android sensor coordinate system to the geometric center of the sensor,
+     * specified in millimeters (mm).
+     * <p>
+     * <b>Payload</b>:
+     *     <code>floatValues[0..11]</code>: 3x4 matrix in row major order [R; t]
+     * </p>
+     * <p>
+     * <b>Example</b>:
+     *     This raw buffer: <code>{0, 1, 0, 0, -1, 0, 0, 10, 0, 0, 1, -2.5}</code><br>
+     *     Corresponds to this 3x4 matrix:
+     *     <table>
+     *      <thead>
+     *       <tr><td colspan="3">Orientation</td><td>Location</tr>
+     *      </thead>
+     *      <tbody>
+     *       <tr><td>0</td><td>1</td><td>0</td><td>0</td></tr>
+     *       <tr><td>-1</td><td>0</td><td>0</td><td>10</td></tr>
+     *       <tr><td>0</td><td>0</td><td>1</td><td>-2.5</td></tr>
+     *      </tbody>
+     *     </table>
+     *     The sensor is oriented such that:
+     *     <ul>
+     *        <li>The device X axis corresponds to the sensor's local -Y axis
+     *        <li>The device Y axis corresponds to the sensor's local X axis
+     *        <li>The device Z axis and sensor's local Z axis are equivalent
+     *     </ul>
+     *     In other words, if viewing the origin of the Android coordinate system from the positive
+     *     Z direction, the device coordinate frame is to be rotated 90° counter-clockwise about the
+     *     Z axis to align with the sensor's local coordinate frame. Equivalently, a vector in the
+     *     Android coordinate frame may be multiplied with R to rotate it 90° clockwise (270°
+     *     counter-clockwise), yielding its representation in the sensor's coordinate frame.
+     *     Relative to the origin of the Android coordinate system, the physical center of the
+     *     sensor is located 10mm in the positive Y direction, and 2.5mm in the negative Z
+     *     direction.
+     * </p>
      */
     public static final int TYPE_SENSOR_PLACEMENT = 0x10003;
 
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 035061b..702b41b 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -330,18 +330,25 @@
     @TestApi
     @Nullable
     public static VibrationEffect get(Uri uri, Context context) {
+        final ContentResolver cr = context.getContentResolver();
+        Uri uncanonicalUri = cr.uncanonicalize(uri);
+        if (uncanonicalUri == null) {
+            // If we already had an uncanonical URI, it's possible we'll get null back here. In
+            // this case, just use the URI as passed in since it wasn't canonicalized in the first
+            // place.
+            uncanonicalUri = uri;
+        }
         String[] uris = context.getResources().getStringArray(
                 com.android.internal.R.array.config_ringtoneEffectUris);
         for (int i = 0; i < uris.length && i < RINGTONES.length; i++) {
             if (uris[i] == null) {
                 continue;
             }
-            ContentResolver cr = context.getContentResolver();
             Uri mappedUri = cr.uncanonicalize(Uri.parse(uris[i]));
             if (mappedUri == null) {
                 continue;
             }
-            if (mappedUri.equals(uri)) {
+            if (mappedUri.equals(uncanonicalUri)) {
                 return get(RINGTONES[i]);
             }
         }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 0a01beb..9573ac0 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -398,6 +398,8 @@
                 mSystemWindowInsets.bottom));
         ((ListView) mAdapterView).addFooterView(mFooterSpacer);
 
+        resetButtonBar();
+
         return insets.consumeSystemWindowInsets();
     }
 
diff --git a/core/java/com/android/internal/colorextraction/ColorExtractor.java b/core/java/com/android/internal/colorextraction/ColorExtractor.java
index d9fd3b5..a6286c0 100644
--- a/core/java/com/android/internal/colorextraction/ColorExtractor.java
+++ b/core/java/com/android/internal/colorextraction/ColorExtractor.java
@@ -53,11 +53,13 @@
     protected WallpaperColors mLockColors;
 
     public ColorExtractor(Context context) {
-        this(context, new Tonal(context), true /* immediately */);
+        this(context, new Tonal(context), true /* immediately */,
+                context.getSystemService(WallpaperManager.class));
     }
 
     @VisibleForTesting
-    public ColorExtractor(Context context, ExtractionType extractionType, boolean immediately) {
+    public ColorExtractor(Context context, ExtractionType extractionType, boolean immediately,
+            WallpaperManager wallpaperManager) {
         mContext = context;
         mExtractionType = extractionType;
 
@@ -71,14 +73,8 @@
         }
 
         mOnColorsChangedListeners = new ArrayList<>();
-
-        WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
-        if (wallpaperManager == null) {
-            Log.w(TAG, "Can't listen to color changes!");
-        } else {
-            wallpaperManager.addOnColorsChangedListener(this, null /* handler */);
-            initExtractColors(wallpaperManager, immediately);
-        }
+        wallpaperManager.addOnColorsChangedListener(this, null /* handler */);
+        initExtractColors(wallpaperManager, immediately);
     }
 
     private void initExtractColors(WallpaperManager wallpaperManager, boolean immediately) {
diff --git a/core/java/com/android/internal/os/BinderDeathDispatcher.java b/core/java/com/android/internal/os/BinderDeathDispatcher.java
new file mode 100644
index 0000000..0c93f7f
--- /dev/null
+++ b/core/java/com/android/internal/os/BinderDeathDispatcher.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+/**
+ * Multiplexes multiple binder death recipients on the same binder objects, so that at the native
+ * level, we only need to keep track of one death recipient reference. This will help reduce the
+ * number of JNI strong references.
+ *
+ * test with: atest FrameworksCoreTests:BinderDeathDispatcherTest
+ */
+public class BinderDeathDispatcher<T extends IInterface> {
+    private static final String TAG = "BinderDeathDispatcher";
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final ArrayMap<IBinder, RecipientsInfo> mTargets = new ArrayMap<>();
+
+    @VisibleForTesting
+    class RecipientsInfo implements DeathRecipient {
+        final IBinder mTarget;
+
+        /**
+         * Recipient list. If it's null, {@link #mTarget} has already died, but in that case
+         * this RecipientsInfo instance is removed from {@link #mTargets}.
+         */
+        @GuardedBy("mLock")
+        @Nullable
+        ArraySet<DeathRecipient> mRecipients = new ArraySet<>();
+
+        private RecipientsInfo(IBinder target) {
+            mTarget = target;
+        }
+
+        @Override
+        public void binderDied() {
+            final ArraySet<DeathRecipient> copy;
+            synchronized (mLock) {
+                copy = mRecipients;
+                mRecipients = null;
+
+                // Also remove from the targets.
+                mTargets.remove(mTarget);
+            }
+            if (copy == null) {
+                return;
+            }
+            // Let's call it without holding the lock.
+            final int size = copy.size();
+            for (int i = 0; i < size; i++) {
+                copy.valueAt(i).binderDied();
+            }
+        }
+    }
+
+    /**
+     * Add a {@code recipient} to the death recipient list on {@code target}.
+     *
+     * @return # of recipients in the recipient list, including {@code recipient}. Or, -1
+     * if {@code target} is already dead, in which case recipient's
+     * {@link DeathRecipient#binderDied} won't be called.
+     */
+    public int linkToDeath(@NonNull T target, @NonNull DeathRecipient recipient) {
+        final IBinder ib = target.asBinder();
+        synchronized (mLock) {
+            RecipientsInfo info = mTargets.get(ib);
+            if (info == null) {
+                info = new RecipientsInfo(ib);
+
+                // First recipient; need to link to death.
+                try {
+                    ib.linkToDeath(info, 0);
+                } catch (RemoteException e) {
+                    return -1; // Already dead.
+                }
+                mTargets.put(ib, info);
+            }
+            info.mRecipients.add(recipient);
+            return info.mRecipients.size();
+        }
+    }
+
+    public void unlinkToDeath(@NonNull T target, @NonNull DeathRecipient recipient) {
+        final IBinder ib = target.asBinder();
+
+        synchronized (mLock) {
+            final RecipientsInfo info = mTargets.get(ib);
+            if (info == null) {
+                return;
+            }
+            if (info.mRecipients.remove(recipient) && info.mRecipients.size() == 0) {
+                info.mTarget.unlinkToDeath(info, 0);
+                mTargets.remove(info.mTarget);
+            }
+        }
+    }
+
+    public void dump(PrintWriter pw, String indent) {
+        synchronized (mLock) {
+            pw.print(indent);
+            pw.print("# of watched binders: ");
+            pw.println(mTargets.size());
+
+            pw.print(indent);
+            pw.print("# of death recipients: ");
+            int n = 0;
+            for (RecipientsInfo info : mTargets.values()) {
+                n += info.mRecipients.size();
+            }
+            pw.println(n);
+        }
+    }
+
+    @VisibleForTesting
+    public ArrayMap<IBinder, RecipientsInfo> getTargetsForTest() {
+        return mTargets;
+    }
+}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index e8691fa..b73ecd1 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -756,4 +756,8 @@
             return String.valueOf(value);
         }
     }
+
+    public static @Nullable <T> T firstOrNull(T[] items) {
+        return items.length > 0 ? items[0] : null;
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
new file mode 100644
index 0000000..5914887
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileDescriptor;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BinderDeathDispatcherTest {
+    private static class MyTarget implements IInterface, IBinder {
+        public boolean isAlive = true;
+        public DeathRecipient mRecipient;
+
+        @Override
+        public String getInterfaceDescriptor() throws RemoteException {
+            return null;
+        }
+
+        @Override
+        public boolean pingBinder() {
+            return false;
+        }
+
+        @Override
+        public boolean isBinderAlive() {
+            return isAlive;
+        }
+
+        @Override
+        public IInterface queryLocalInterface(String descriptor) {
+            return null;
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+
+        }
+
+        @Override
+        public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
+
+        }
+
+        @Override
+        public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+                String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver)
+                throws RemoteException {
+
+        }
+
+        @Override
+        public boolean transact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            return false;
+        }
+
+        @Override
+        public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {
+            // In any situation, a single binder object should only have at most one death
+            // recipient.
+            assertThat(mRecipient).isNull();
+
+            if (!isAlive) {
+                throw new DeadObjectException();
+            }
+
+            mRecipient = recipient;
+        }
+
+        @Override
+        public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
+            if (!isAlive) {
+                return false;
+            }
+            assertThat(mRecipient).isSameAs(recipient);
+            mRecipient = null;
+            return true;
+        }
+
+        @Override
+        public IBinder asBinder() {
+            return this;
+        }
+
+        public void die() {
+            isAlive = false;
+            if (mRecipient != null) {
+                mRecipient.binderDied();
+            }
+            mRecipient = null;
+        }
+
+        public boolean hasDeathRecipient() {
+            return mRecipient != null;
+        }
+    }
+
+    @Test
+    public void testRegisterAndUnregister() {
+        BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
+
+        MyTarget t1 = new MyTarget();
+        MyTarget t2 = new MyTarget();
+        MyTarget t3 = new MyTarget();
+
+        DeathRecipient r1 = mock(DeathRecipient.class);
+        DeathRecipient r2 = mock(DeathRecipient.class);
+        DeathRecipient r3 = mock(DeathRecipient.class);
+        DeathRecipient r4 = mock(DeathRecipient.class);
+        DeathRecipient r5 = mock(DeathRecipient.class);
+
+        // Start hooking up.
+
+        // Link 3 recipients to t1 -- only one real recipient will be set.
+        assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
+        assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+        assertThat(d.linkToDeath(t1, r2)).isEqualTo(2);
+        assertThat(d.linkToDeath(t1, r3)).isEqualTo(3);
+        assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+        // Unlink two -- the real recipient is still set.
+        d.unlinkToDeath(t1, r1);
+        d.unlinkToDeath(t1, r2);
+
+        assertThat(t1.hasDeathRecipient()).isTrue();
+        assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+        // Unlink the last one. The real recipient is also unlinked.
+        d.unlinkToDeath(t1, r3);
+        assertThat(t1.hasDeathRecipient()).isFalse();
+        assertThat(d.getTargetsForTest().size()).isEqualTo(0);
+
+        // Set recipients to t1, t2 and t3. t3 has two.
+        assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
+        assertThat(d.linkToDeath(t2, r1)).isEqualTo(1);
+        assertThat(d.linkToDeath(t3, r1)).isEqualTo(1);
+        assertThat(d.linkToDeath(t3, r2)).isEqualTo(2);
+
+
+        // They should all have a real recipient.
+        assertThat(t1.hasDeathRecipient()).isTrue();
+        assertThat(t2.hasDeathRecipient()).isTrue();
+        assertThat(t3.hasDeathRecipient()).isTrue();
+
+        assertThat(d.getTargetsForTest().size()).isEqualTo(3);
+
+        // Unlink r1 from t3. t3 still has r2, so it should still have a real recipient.
+        d.unlinkToDeath(t3, r1);
+        assertThat(t1.hasDeathRecipient()).isTrue();
+        assertThat(t2.hasDeathRecipient()).isTrue();
+        assertThat(t3.hasDeathRecipient()).isTrue();
+        assertThat(d.getTargetsForTest().size()).isEqualTo(3);
+
+        // Unlink r2 from t3. Now t3 has no real recipient.
+        d.unlinkToDeath(t3, r2);
+        assertThat(t3.hasDeathRecipient()).isFalse();
+        assertThat(d.getTargetsForTest().size()).isEqualTo(2);
+    }
+
+    @Test
+    public void testRegisterAndKill() {
+        BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
+
+        MyTarget t1 = new MyTarget();
+        MyTarget t2 = new MyTarget();
+        MyTarget t3 = new MyTarget();
+
+        DeathRecipient r1 = mock(DeathRecipient.class);
+        DeathRecipient r2 = mock(DeathRecipient.class);
+        DeathRecipient r3 = mock(DeathRecipient.class);
+        DeathRecipient r4 = mock(DeathRecipient.class);
+        DeathRecipient r5 = mock(DeathRecipient.class);
+
+        // Hook them up.
+
+        d.linkToDeath(t1, r1);
+        d.linkToDeath(t1, r2);
+        d.linkToDeath(t1, r3);
+
+        // r4 is linked then unlinked. It shouldn't be notified.
+        d.linkToDeath(t1, r4);
+        d.unlinkToDeath(t1, r4);
+
+        d.linkToDeath(t2, r1);
+
+        d.linkToDeath(t3, r3);
+        d.linkToDeath(t3, r5);
+
+        assertThat(d.getTargetsForTest().size()).isEqualTo(3);
+
+        // Kill the targets.
+
+        t1.die();
+        verify(r1, times(1)).binderDied();
+        verify(r2, times(1)).binderDied();
+        verify(r3, times(1)).binderDied();
+        verify(r4, times(0)).binderDied();
+        verify(r5, times(0)).binderDied();
+
+        assertThat(d.getTargetsForTest().size()).isEqualTo(2);
+
+        reset(r1, r2, r3, r4, r5);
+
+        t2.die();
+        verify(r1, times(1)).binderDied();
+        verify(r2, times(0)).binderDied();
+        verify(r3, times(0)).binderDied();
+        verify(r4, times(0)).binderDied();
+        verify(r5, times(0)).binderDied();
+
+        assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+        reset(r1, r2, r3, r4, r5);
+
+        t3.die();
+        verify(r1, times(0)).binderDied();
+        verify(r2, times(0)).binderDied();
+        verify(r3, times(1)).binderDied();
+        verify(r4, times(0)).binderDied();
+        verify(r5, times(1)).binderDied();
+
+        assertThat(d.getTargetsForTest().size()).isEqualTo(0);
+
+        // Try to register to a dead object -> should return -1.
+        assertThat(d.linkToDeath(t1, r1)).isEqualTo(-1);
+
+        assertThat(d.getTargetsForTest().size()).isEqualTo(0);
+    }
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3fe2492..fdf0bc9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -953,8 +953,8 @@
     <!-- Shows to explain the double tap interaction with notifications: After tapping a notification on Keyguard, this will explain users to tap again to launch a notification. [CHAR LIMIT=60] -->
     <string name="notification_tap_again">Tap again to open</string>
 
-    <!-- Shows when people have pressed the unlock icon to explain how to unlock. [CHAR LIMIT=60] -->
-    <string name="keyguard_unlock">Swipe up to unlock</string>
+    <!-- Message shown when lock screen is tapped or face authentication fails. [CHAR LIMIT=60] -->
+    <string name="keyguard_unlock">Swipe up to open</string>
 
     <!-- Text on keyguard screen and in Quick Settings footer indicating that the device is enterprise-managed by a Device Owner [CHAR LIMIT=60] -->
     <string name="do_disclosure_generic">This device is managed by your organization</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5d1f8b1..3c119a6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -937,6 +937,11 @@
                 == LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
     }
 
+    public boolean userNeedsStrongAuth() {
+        return mStrongAuthTracker.getStrongAuthForUser(getCurrentUser())
+                != LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+    }
+
     public boolean needsSlowUnlockTransition() {
         return mNeedsSlowUnlockTransition;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 5e19219..b7bb4d9 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -77,8 +77,10 @@
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.phone.NavigationBarTransitions;
+import com.android.systemui.statusbar.phone.NavigationModeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.tuner.TunablePadding;
 import com.android.systemui.tuner.TunerService;
@@ -125,6 +127,7 @@
     private Handler mHandler;
     private boolean mAssistHintBlocked = false;
     private boolean mIsReceivingNavBarColor = false;
+    private boolean mInGesturalMode;
 
     /**
      * Converts a set of {@link Rect}s into a {@link Region}
@@ -149,6 +152,28 @@
         mHandler.post(this::startOnScreenDecorationsThread);
         setupStatusBarPaddingIfNeeded();
         putComponent(ScreenDecorations.class, this);
+        mInGesturalMode = QuickStepContract.isGesturalMode(
+                Dependency.get(NavigationModeController.class)
+                        .addListener(this::handleNavigationModeChange));
+    }
+
+    @VisibleForTesting
+    void handleNavigationModeChange(int navigationMode) {
+        if (!mHandler.getLooper().isCurrentThread()) {
+            mHandler.post(() -> handleNavigationModeChange(navigationMode));
+            return;
+        }
+        boolean inGesturalMode = QuickStepContract.isGesturalMode(navigationMode);
+        if (mInGesturalMode != inGesturalMode) {
+            mInGesturalMode = inGesturalMode;
+
+            if (mInGesturalMode && mOverlay == null) {
+                setupDecorations();
+                if (mOverlay != null) {
+                    updateLayoutParams();
+                }
+            }
+        }
     }
 
     private void fade(View view, boolean fadeIn, boolean isLeft) {
@@ -232,6 +257,7 @@
                     break;
             }
         }
+        updateWindowVisibilities();
     }
 
     /**
@@ -256,11 +282,15 @@
         return thread.getThreadHandler();
     }
 
+    private boolean shouldHostHandles() {
+        return mInGesturalMode;
+    }
+
     private void startOnScreenDecorationsThread() {
         mRotation = RotationUtils.getExactRotation(mContext);
         mWindowManager = mContext.getSystemService(WindowManager.class);
         updateRoundedCornerRadii();
-        if (hasRoundedCorners() || shouldDrawCutout()) {
+        if (hasRoundedCorners() || shouldDrawCutout() || shouldHostHandles()) {
             setupDecorations();
         }
 
@@ -565,7 +595,10 @@
         boolean visibleForCutout = shouldDrawCutout()
                 && overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
         boolean visibleForRoundedCorners = hasRoundedCorners();
-        overlay.setVisibility(visibleForCutout || visibleForRoundedCorners
+        boolean visibleForHandles = overlay.findViewById(R.id.assist_hint_left).getVisibility()
+                == View.VISIBLE || overlay.findViewById(R.id.assist_hint_right).getVisibility()
+                == View.VISIBLE;
+        overlay.setVisibility(visibleForCutout || visibleForRoundedCorners || visibleForHandles
                 ? View.VISIBLE : View.GONE);
     }
 
@@ -688,10 +721,6 @@
                 setSize(mOverlay.findViewById(R.id.right), sizeTop);
                 setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
                 setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
-                setSize(mOverlay.findViewById(R.id.assist_hint_left), sizeTop * 2);
-                setSize(mOverlay.findViewById(R.id.assist_hint_right), sizeTop * 2);
-                setSize(mBottomOverlay.findViewById(R.id.assist_hint_left), sizeBottom * 2);
-                setSize(mBottomOverlay.findViewById(R.id.assist_hint_right), sizeBottom * 2);
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 0fdab01..34cc70c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -60,6 +60,7 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockMethodCache;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.util.InjectionInflationController;
 import com.android.systemui.util.leak.GarbageMonitor;
@@ -130,8 +131,8 @@
             KeyguardBouncer.BouncerExpansionCallback expansionCallback) {
         return new KeyguardBouncer(context, callback, lockPatternUtils, container,
                 dismissCallbackRegistry, FalsingManagerFactory.getInstance(context),
-                expansionCallback, KeyguardUpdateMonitor.getInstance(context),
-                new Handler(Looper.getMainLooper()));
+                expansionCallback, UnlockMethodCache.getInstance(context),
+                KeyguardUpdateMonitor.getInstance(context), new Handler(Looper.getMainLooper()));
     }
 
     public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 5717a54..443c4e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.biometrics;
 
+import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
+
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.graphics.PixelFormat;
@@ -36,6 +38,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.Interpolator;
 import android.widget.Button;
 import android.widget.ImageView;
@@ -72,6 +76,7 @@
     protected static final int STATE_PENDING_CONFIRMATION = 3;
     protected static final int STATE_AUTHENTICATED = 4;
 
+    private final AccessibilityManager mAccessibilityManager;
     private final IBinder mWindowToken = new Binder();
     private final Interpolator mLinearOutSlowIn;
     private final WindowManager mWindowManager;
@@ -150,6 +155,7 @@
         super(context);
         mCallback = callback;
         mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
+        mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         mWindowManager = mContext.getSystemService(WindowManager.class);
         mUserManager = mContext.getSystemService(UserManager.class);
         mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
@@ -228,6 +234,10 @@
             showTryAgainButton(false /* show */);
             mCallback.onTryAgainPressed();
         });
+
+        // Must set these in order for the back button events to be received.
+        mLayout.setFocusableInTouchMode(true);
+        mLayout.requestFocus();
     }
 
     public void onSaveState(Bundle bundle) {
@@ -280,6 +290,7 @@
         final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
         if (TextUtils.isEmpty(subtitleText)) {
             mSubtitleText.setVisibility(View.GONE);
+            announceAccessibilityEvent();
         } else {
             mSubtitleText.setVisibility(View.VISIBLE);
             mSubtitleText.setText(subtitleText);
@@ -289,6 +300,7 @@
                 mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
         if (TextUtils.isEmpty(descriptionText)) {
             mDescriptionText.setVisibility(View.GONE);
+            announceAccessibilityEvent();
         } else {
             mDescriptionText.setVisibility(View.VISIBLE);
             mDescriptionText.setText(descriptionText);
@@ -447,12 +459,14 @@
         if (newState == STATE_PENDING_CONFIRMATION) {
             mHandler.removeMessages(MSG_RESET_MESSAGE);
             mErrorText.setVisibility(View.INVISIBLE);
+            announceAccessibilityEvent();
             mPositiveButton.setVisibility(View.VISIBLE);
             mPositiveButton.setEnabled(true);
         } else if (newState == STATE_AUTHENTICATED) {
             mPositiveButton.setVisibility(View.GONE);
             mNegativeButton.setVisibility(View.GONE);
             mErrorText.setVisibility(View.INVISIBLE);
+            announceAccessibilityEvent();
         }
 
         if (newState == STATE_PENDING_CONFIRMATION || newState == STATE_AUTHENTICATED) {
@@ -471,14 +485,20 @@
 
     public void restoreState(Bundle bundle) {
         mRestoredState = bundle;
-        mTryAgainButton.setVisibility(bundle.getInt(KEY_TRY_AGAIN_VISIBILITY));
-        mPositiveButton.setVisibility(bundle.getInt(KEY_CONFIRM_VISIBILITY));
+        final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY);
+        mTryAgainButton.setVisibility(tryAgainVisibility);
+        final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY);
+        mPositiveButton.setVisibility(confirmVisibility);
         mState = bundle.getInt(KEY_STATE);
         mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
         mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
-        mErrorText.setVisibility(bundle.getInt(KEY_ERROR_TEXT_VISIBILITY));
+        final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY);
+        mErrorText.setVisibility(errorTextVisibility);
+        if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE
+                || confirmVisibility == View.INVISIBLE) {
+            announceAccessibilityEvent();
+        }
         mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
-
         if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) {
             mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE),
                     BiometricPrompt.HIDE_DIALOG_DELAY);
@@ -501,4 +521,18 @@
         lp.token = mWindowToken;
         return lp;
     }
+
+    // Every time a view becomes invisible we need to announce an accessibility event.
+    // This is due to an issue in the framework, b/132298701 recommended this workaround.
+    protected void announceAccessibilityEvent() {
+        if (!mAccessibilityManager.isEnabled()) {
+            return;
+        }
+        AccessibilityEvent event = AccessibilityEvent.obtain();
+        event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        event.setContentChangeTypes(CONTENT_CHANGE_TYPE_SUBTREE);
+        mDialog.sendAccessibilityEventUnchecked(event);
+        mDialog.notifySubtreeAccessibilityStateChanged(mDialog, mDialog,
+                CONTENT_CHANGE_TYPE_SUBTREE);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
index 8f26f18..91124cb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
@@ -149,6 +149,7 @@
     private final Runnable mErrorToIdleAnimationRunnable = () -> {
         updateState(STATE_IDLE);
         mErrorText.setVisibility(View.INVISIBLE);
+        announceAccessibilityEvent();
     };
 
     public FaceDialogView(Context context,
@@ -188,6 +189,7 @@
             mDialog.invalidateOutline();
 
             mSize = newSize;
+            announceAccessibilityEvent();
         } else if (mSize == SIZE_SMALL && newSize == SIZE_BIG) {
             mSize = SIZE_GROWING;
 
@@ -294,6 +296,7 @@
             mErrorText.setVisibility(View.VISIBLE);
         } else {
             mErrorText.setVisibility(View.INVISIBLE);
+            announceAccessibilityEvent();
         }
     }
 
@@ -368,11 +371,13 @@
                 mTryAgainButton.setVisibility(View.VISIBLE);
             } else {
                 mTryAgainButton.setVisibility(View.GONE);
+                announceAccessibilityEvent();
             }
         }
 
         if (show) {
             mPositiveButton.setVisibility(View.GONE);
+            announceAccessibilityEvent();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 835ffc9..6579c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -59,13 +59,15 @@
 
     @Inject
     public SysuiColorExtractor(Context context, ConfigurationController configurationController) {
-        this(context, new Tonal(context), configurationController, true);
+        this(context, new Tonal(context), configurationController, true,
+                context.getSystemService(WallpaperManager.class));
     }
 
     @VisibleForTesting
     public SysuiColorExtractor(Context context, ExtractionType type,
-            ConfigurationController configurationController, boolean registerVisibility) {
-        super(context, type, false /* immediately */);
+            ConfigurationController configurationController, boolean registerVisibility,
+            WallpaperManager wallpaperManager) {
+        super(context, type, false /* immediately */, wallpaperManager);
         mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context);
         mWpHiddenColors = new GradientColors();
         configurationController.addCallback(this);
@@ -91,13 +93,10 @@
             }
         }
 
-        WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
-        if (wallpaperManager != null) {
-            // Listen to all users instead of only the current one.
-            wallpaperManager.removeOnColorsChangedListener(this);
-            wallpaperManager.addOnColorsChangedListener(this, null /* handler */,
-                    UserHandle.USER_ALL);
-        }
+        // Listen to all users instead of only the current one.
+        wallpaperManager.removeOnColorsChangedListener(this);
+        wallpaperManager.addOnColorsChangedListener(this, null /* handler */,
+                UserHandle.USER_ALL);
     }
 
     private void updateDefaultGradients(WallpaperColors colors) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 0f6740d..7c6c556 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -569,16 +569,21 @@
 
         switch (behavior) {
             case BEHAVIOR_ALERTING:
-                alert.setSelected(true);
-                silence.setSelected(false);
                 mPriorityDescriptionView.setVisibility(VISIBLE);
                 mSilentDescriptionView.setVisibility(GONE);
+                post(() -> {
+                    alert.setSelected(true);
+                    silence.setSelected(false);
+                });
                 break;
             case BEHAVIOR_SILENT:
-                alert.setSelected(false);
-                silence.setSelected(true);
+
                 mSilentDescriptionView.setVisibility(VISIBLE);
                 mPriorityDescriptionView.setVisibility(GONE);
+                post(() -> {
+                    alert.setSelected(false);
+                    silence.setSelected(true);
+                });
                 break;
             default:
                 throw new IllegalArgumentException("Unrecognized alerting behavior: " + behavior);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 0b994ff..f5d058c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -54,7 +54,7 @@
 public class KeyguardBouncer {
 
     private static final String TAG = "KeyguardBouncer";
-    static final long BOUNCER_FACE_DELAY = 800;
+    static final long BOUNCER_FACE_DELAY = 1200;
     static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
     static final float EXPANSION_HIDDEN = 1f;
     static final float EXPANSION_VISIBLE = 0f;
@@ -68,6 +68,7 @@
     private final Handler mHandler;
     private final BouncerExpansionCallback mExpansionCallback;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final UnlockMethodCache mUnlockMethodCache;
     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
                 @Override
@@ -95,7 +96,7 @@
     public KeyguardBouncer(Context context, ViewMediatorCallback callback,
             LockPatternUtils lockPatternUtils, ViewGroup container,
             DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
-            BouncerExpansionCallback expansionCallback,
+            BouncerExpansionCallback expansionCallback, UnlockMethodCache unlockMethodCache,
             KeyguardUpdateMonitor keyguardUpdateMonitor, Handler handler) {
         mContext = context;
         mCallback = callback;
@@ -106,6 +107,7 @@
         mDismissCallbackRegistry = dismissCallbackRegistry;
         mExpansionCallback = expansionCallback;
         mHandler = handler;
+        mUnlockMethodCache = unlockMethodCache;
         mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
     }
 
@@ -168,7 +170,8 @@
 
         // Split up the work over multiple frames.
         DejankUtils.removeCallbacks(mResetRunnable);
-        if (mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
+        if (mUnlockMethodCache.isUnlockingWithFacePossible() && !needsFullscreenBouncer()
+                && !mKeyguardUpdateMonitor.userNeedsStrongAuth()) {
             mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
         } else {
             DejankUtils.postAfterTraversal(mShowRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 3ec1609..4bc0036 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -238,7 +238,19 @@
             info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
             return;
         }
+
         info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+        ButtonDispatcher imeSwitchButton = getImeSwitchButton();
+        if (imeSwitchButton.getVisibility() == VISIBLE) {
+            // If the IME is not up, but the ime switch button is visible, then make sure that
+            // button is touchable
+            int[] loc = new int[2];
+            View buttonView = imeSwitchButton.getCurrentView();
+            buttonView.getLocationInWindow(loc);
+            info.touchableRegion.set(loc[0], loc[1], loc[0] + buttonView.getWidth(),
+                    loc[1] + buttonView.getHeight());
+            return;
+        }
         info.touchableRegion.setEmpty();
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 9536be3..8ad25bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1523,6 +1523,8 @@
 
     @Override  // UnlockMethodCache.OnUnlockMethodChangedListener
     public void onUnlockMethodStateChanged() {
+        // Unlock method state changed. Notify KeguardMonitor
+        updateKeyguardState();
         logStateToEventlog();
     }
 
@@ -3419,9 +3421,7 @@
         checkBarModes();
         updateScrimController();
         mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
-        mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
-                mUnlockMethodCache.isMethodSecure(),
-                mStatusBarKeyguardViewManager.isOccluded());
+        updateKeyguardState();
         Trace.endSection();
     }
 
@@ -3460,6 +3460,12 @@
         mStatusBarStateController.setIsDozing(dozing);
     }
 
+    private void updateKeyguardState() {
+        mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
+                mUnlockMethodCache.isMethodSecure(),
+                mStatusBarKeyguardViewManager.isOccluded());
+    }
+
     public void onActivationReset() {
         mKeyguardIndicationController.hideTransientIndication();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 64ab060..3b5e12c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -45,6 +45,7 @@
 import android.view.Display;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.WindowManagerPolicyConstants;
 
 import androidx.test.filters.SmallTest;
 
@@ -52,6 +53,7 @@
 import com.android.systemui.ScreenDecorations.TunablePaddingTagListener;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.statusbar.phone.NavigationModeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
 import com.android.systemui.tuner.TunablePadding;
@@ -78,6 +80,7 @@
     private TunerService mTunerService;
     private StatusBarWindowView mView;
     private TunablePaddingService mTunablePaddingService;
+    private NavigationModeController mNavigationModeController;
 
     @Before
     public void setup() {
@@ -87,6 +90,8 @@
         mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
         mTunerService = mDependency.injectMockDependency(TunerService.class);
         mFragmentService = mDependency.injectMockDependency(FragmentService.class);
+        mNavigationModeController = mDependency.injectMockDependency(
+                NavigationModeController.class);
 
         mStatusBar = mock(StatusBar.class);
         mWindowManager = mock(WindowManager.class);
@@ -208,6 +213,54 @@
     }
 
     @Test
+    public void testAssistHandles() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(dimen.rounded_corner_content_padding, 0);
+        when(mNavigationModeController.addListener(any())).thenReturn(
+                WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
+
+        mScreenDecorations.start();
+
+        // Add 2 windows for rounded corners (top and bottom).
+        verify(mWindowManager, times(2)).addView(any(), any());
+    }
+
+    @Test
+    public void testDelayedAssistHandles() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(dimen.rounded_corner_content_padding, 0);
+        when(mNavigationModeController.addListener(any())).thenReturn(
+                WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON);
+
+        mScreenDecorations.start();
+
+        // No handles and no corners
+        verify(mWindowManager, never()).addView(any(), any());
+
+        mScreenDecorations.handleNavigationModeChange(
+                WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
+
+        // Add 2 windows for rounded corners (top and bottom).
+        verify(mWindowManager, times(2)).addView(any(), any());
+    }
+
+    @Test
     public void hasRoundedCornerOverlayFlagSet() {
         assertThat(mScreenDecorations.getWindowLayoutParams().privateFlags
                         & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 9c2c822..e3a162c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -40,6 +40,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
@@ -57,6 +58,8 @@
             ColorExtractor.TYPE_DARK,
             ColorExtractor.TYPE_EXTRA_DARK};
 
+    @Mock
+    private WallpaperManager mWallpaperManager;
     private ColorExtractor.GradientColors mColors;
     private SysuiColorExtractor mColorExtractor;
 
@@ -72,7 +75,7 @@
                     outGradientColorsNormal.set(mColors);
                     outGradientColorsDark.set(mColors);
                     outGradientColorsExtraDark.set(mColors);
-                }, mock(ConfigurationController.class), false);
+                }, mock(ConfigurationController.class), false, mWallpaperManager);
     }
 
     @Test
@@ -127,7 +130,7 @@
         Tonal tonal = mock(Tonal.class);
         ConfigurationController configurationController = mock(ConfigurationController.class);
         SysuiColorExtractor sysuiColorExtractor = new SysuiColorExtractor(getContext(),
-                tonal, configurationController, false /* registerVisibility */);
+                tonal, configurationController, false /* registerVisibility */, mWallpaperManager);
         verify(configurationController).addCallback(eq(sysuiColorExtractor));
 
         reset(tonal);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 3bc5f3e..4e0ef56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -82,6 +82,8 @@
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
+    private UnlockMethodCache mUnlockMethodCache;
+    @Mock
     private Handler mHandler;
 
     private KeyguardBouncer mBouncer;
@@ -96,7 +98,7 @@
         when(mKeyguardHostView.getHeight()).thenReturn(500);
         mBouncer = new KeyguardBouncer(getContext(), mViewMediatorCallback,
                 mLockPatternUtils, container, mDismissCallbackRegistry, mFalsingManager,
-                mExpansionCallback, mKeyguardUpdateMonitor, mHandler) {
+                mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor, mHandler) {
             @Override
             protected void inflateView() {
                 super.inflateView();
@@ -377,7 +379,7 @@
 
     @Test
     public void testShow_delaysIfFaceAuthIsRunning() {
-        when(mKeyguardUpdateMonitor.isFaceDetectionRunning()).thenReturn(true);
+        when(mUnlockMethodCache.isUnlockingWithFacePossible()).thenReturn(true);
         mBouncer.show(true /* reset */);
 
         ArgumentCaptor<Runnable> showRunnable = ArgumentCaptor.forClass(Runnable.class);
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 7709311..593478c 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -98,7 +98,7 @@
         RemoteContentSuggestionsService service = getRemoteServiceLocked();
         if (service != null) {
             ActivityManager.TaskSnapshot snapshot =
-                    mActivityTaskManagerInternal.getTaskSnapshot(taskId, false);
+                    mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false);
             GraphicBuffer snapshotBuffer = null;
             int colorSpaceId = 0;
             if (snapshot != null) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7a2aa7e..a6ff37e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3633,20 +3633,37 @@
         mNotifier.showNotification(nai.network.netId, type, nai, null, pendingIntent, true);
     }
 
+    private boolean shouldPromptUnvalidated(NetworkAgentInfo nai) {
+        // Don't prompt if the network is validated, and don't prompt on captive portals
+        // because we're already prompting the user to sign in.
+        if (nai.everValidated || nai.everCaptivePortalDetected) {
+            return false;
+        }
+
+        // If a network has partial connectivity, always prompt unless the user has already accepted
+        // partial connectivity and selected don't ask again. This ensures that if the device
+        // automatically connects to a network that has partial Internet access, the user will
+        // always be able to use it, either because they've already chosen "don't ask again" or
+        // because we have prompt them.
+        if (nai.partialConnectivity && !nai.networkMisc.acceptPartialConnectivity) {
+            return true;
+        }
+
+        // If a network has no Internet access, only prompt if the network was explicitly selected
+        // and if the user has not already told us to use the network regardless of whether it
+        // validated or not.
+        if (nai.networkMisc.explicitlySelected && !nai.networkMisc.acceptUnvalidated) {
+            return true;
+        }
+
+        return false;
+    }
+
     private void handlePromptUnvalidated(Network network) {
         if (VDBG || DDBG) log("handlePromptUnvalidated " + network);
         NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
 
-        // Only prompt if the network is unvalidated or network has partial internet connectivity
-        // and was explicitly selected by the user, and if we haven't already been told to switch
-        // to it regardless of whether it validated or not. Also don't prompt on captive portals
-        // because we're already prompting the user to sign in.
-        if (nai == null || nai.everValidated || nai.everCaptivePortalDetected
-                || !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated
-                // TODO: Once the value of acceptPartialConnectivity is moved to IpMemoryStore,
-                // we should reevaluate how to handle acceptPartialConnectivity when network just
-                // connected.
-                || nai.networkMisc.acceptPartialConnectivity) {
+        if (nai == null || !shouldPromptUnvalidated(nai)) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 998ee1e..7824a0a 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -22,6 +22,7 @@
 import android.annotation.RequiresPermission;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.job.JobInfo;
 import android.content.BroadcastReceiver;
@@ -56,6 +57,7 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
@@ -63,6 +65,7 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BinderDeathDispatcher;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
@@ -83,6 +86,9 @@
     static final String TAG = "ContentService";
     static final boolean DEBUG = false;
 
+    /** Do a WTF if a single observer is registered more than this times. */
+    private static final int TOO_MANY_OBSERVERS_THRESHOLD = 1000;
+
     public static class Lifecycle extends SystemService {
         private ContentService mService;
 
@@ -135,6 +141,12 @@
     private SyncManager mSyncManager = null;
     private final Object mSyncManagerLock = new Object();
 
+    private static final BinderDeathDispatcher<IContentObserver> sObserverDeathDispatcher =
+            new BinderDeathDispatcher<>();
+
+    @GuardedBy("sObserverLeakDetectedUid")
+    private static final ArraySet<Integer> sObserverLeakDetectedUid = new ArraySet<>(0);
+
     /**
      * Map from userId to providerPackageName to [clientPackageName, uri] to
      * value. This structure is carefully optimized to keep invalidation logic
@@ -236,6 +248,13 @@
                 pw.println();
                 pw.print(" Total number of nodes: "); pw.println(counts[0]);
                 pw.print(" Total number of observers: "); pw.println(counts[1]);
+
+                sObserverDeathDispatcher.dump(pw, " ");
+            }
+            synchronized (sObserverLeakDetectedUid) {
+                pw.println();
+                pw.print("Observer leaking UIDs: ");
+                pw.println(sObserverLeakDetectedUid.toString());
             }
 
             synchronized (mCache) {
@@ -1345,18 +1364,40 @@
             private final Object observersLock;
 
             public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
-                                 int _uid, int _pid, int _userHandle) {
+                                 int _uid, int _pid, int _userHandle, Uri uri) {
                 this.observersLock = observersLock;
                 observer = o;
                 uid = _uid;
                 pid = _pid;
                 userHandle = _userHandle;
                 notifyForDescendants = n;
-                try {
-                    observer.asBinder().linkToDeath(this, 0);
-                } catch (RemoteException e) {
+
+                final int entries = sObserverDeathDispatcher.linkToDeath(observer, this);
+                if (entries == -1) {
                     binderDied();
+                } else if (entries == TOO_MANY_OBSERVERS_THRESHOLD) {
+                    boolean alreadyDetected;
+
+                    synchronized (sObserverLeakDetectedUid) {
+                        alreadyDetected = sObserverLeakDetectedUid.contains(uid);
+                        if (!alreadyDetected) {
+                            sObserverLeakDetectedUid.add(uid);
+                        }
+                    }
+                    if (!alreadyDetected) {
+                        String caller = null;
+                        try {
+                            caller = ArrayUtils.firstOrNull(AppGlobals.getPackageManager()
+                                    .getPackagesForUid(uid));
+                        } catch (RemoteException ignore) {
+                        }
+                        Slog.wtf(TAG, "Observer registered too many times. Leak? cpid=" + pid
+                                + " cuid=" + uid
+                                + " cpkg=" + caller
+                                + " url=" + uri);
+                    }
                 }
+
             }
 
             @Override
@@ -1454,7 +1495,7 @@
             // If this is the leaf node add the observer
             if (index == countUriSegments(uri)) {
                 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
-                        uid, pid, userHandle));
+                        uid, pid, userHandle, uri));
                 return;
             }
 
@@ -1498,7 +1539,7 @@
                 if (entry.observer.asBinder() == observerBinder) {
                     mObservers.remove(i);
                     // We no longer need to listen for death notifications. Remove it.
-                    observerBinder.unlinkToDeath(entry, 0);
+                    sObserverDeathDispatcher.unlinkToDeath(observer, entry);
                     break;
                 }
             }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index bd88594..0032e9a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -617,7 +617,9 @@
             mStagingManager.createSession(session);
         }
 
-        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
+        if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+            mCallbacks.notifySessionCreated(session.sessionId, session.userId);
+        }
         writeSessionsAsync();
         return sessionId;
     }
@@ -1210,16 +1212,25 @@
 
     class InternalCallback {
         public void onSessionBadgingChanged(PackageInstallerSession session) {
-            mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
+            if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+                mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
+            }
+
             writeSessionsAsync();
         }
 
         public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
-            mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
+            if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+                mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId,
+                        active);
+            }
         }
 
         public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
-            mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
+            if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+                mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId,
+                        progress);
+            }
         }
 
         public void onStagedSessionChanged(PackageInstallerSession session) {
@@ -1232,7 +1243,9 @@
         }
 
         public void onSessionFinished(final PackageInstallerSession session, boolean success) {
-            mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
+            if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+                mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
+            }
 
             mInstallHandler.post(new Runnable() {
                 @Override
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 301f650..e107c9a 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1079,7 +1079,7 @@
                     if (info.getPackageName().equals(packageName)) {
                         mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), info);
                         saveRollbackData(data);
-                        return;
+                        break;
                     }
                 }
             }
@@ -1091,7 +1091,6 @@
                     mAppDataRollbackHelper.snapshotAppData(rollback.data.info.getRollbackId(),
                             info);
                     saveRollbackData(rollback.data);
-                    return;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index b3b6efe..4f40dc4 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2383,7 +2383,11 @@
                 r.setVisible(true);
             }
             if (r != starting) {
-                mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
+                // We should not resume activities that being launched behind because these
+                // activities are actually behind other fullscreen activities, but still required
+                // to be visible (such as performing Recents animation).
+                mStackSupervisor.startSpecificActivityLocked(r, andResume && !r.mLaunchTaskBehind,
+                        true /* checkConfig */);
                 return true;
             }
         }
@@ -2636,7 +2640,7 @@
 
         if (!hasRunningActivity) {
             // There are no activities left in the stack, let's look somewhere else.
-            return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");
+            return resumeNextFocusableActivityWhenStackIsEmpty(prev, options);
         }
 
         next.delayedResume = false;
@@ -3037,21 +3041,33 @@
         return true;
     }
 
-    private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
-            ActivityOptions options, String reason) {
-        final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
-        if (nextFocusedStack != null) {
-            // Try to move focus to the next visible stack with a running activity if this
-            // stack is not covering the entire screen or is on a secondary display (with no home
-            // stack).
-            return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack, prev,
-                    null /* targetOptions */);
+    /**
+     * Resume the next eligible activity in a focusable stack when this one does not have any
+     * running activities left. The focus will be adjusted to the next focusable stack and
+     * top running activities will be resumed in all focusable stacks. However, if the current stack
+     * is a home stack - we have to keep it focused, start and resume a home activity on the current
+     * display instead to make sure that the display is not empty.
+     */
+    private boolean resumeNextFocusableActivityWhenStackIsEmpty(ActivityRecord prev,
+            ActivityOptions options) {
+        final String reason = "noMoreActivities";
+
+        if (!isActivityTypeHome()) {
+            final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
+            if (nextFocusedStack != null) {
+                // Try to move focus to the next visible stack with a running activity if this
+                // stack is not covering the entire screen or is on a secondary display with no home
+                // stack.
+                return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack,
+                        prev, null /* targetOptions */);
+            }
         }
 
-        // Let's just start up the Launcher...
+        // If the current stack is a home stack, or if focus didn't switch to a different stack -
+        // just start up the Launcher...
         ActivityOptions.abort(options);
         if (DEBUG_STATES) Slog.d(TAG_STATES,
-                "resumeTopActivityInNextFocusableStack: " + reason + ", go home");
+                "resumeNextFocusableActivityWhenStackIsEmpty: " + reason + ", go home");
         return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index dbc530d..ab35652 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -559,7 +559,7 @@
     /**
      * Gets bitmap snapshot of the provided task id.
      */
-    public abstract ActivityManager.TaskSnapshot getTaskSnapshot(int taskId,
+    public abstract ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
             boolean reducedResolution);
 
     /** Returns true if uid is considered foreground for activity start purposes. */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8ba7ca1..9fc278e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4521,22 +4521,27 @@
         enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
         final long ident = Binder.clearCallingIdentity();
         try {
-            final TaskRecord task;
-            synchronized (mGlobalLock) {
-                task = mRootActivityContainer.anyTaskForId(taskId,
-                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-                if (task == null) {
-                    Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
-                    return null;
-                }
-            }
-            // Don't call this while holding the lock as this operation might hit the disk.
-            return task.getSnapshot(reducedResolution);
+            return getTaskSnapshot(taskId, reducedResolution, true /* restoreFromDisk */);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
+    private ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution,
+            boolean restoreFromDisk) {
+        final TaskRecord task;
+        synchronized (mGlobalLock) {
+            task = mRootActivityContainer.anyTaskForId(taskId,
+                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+            if (task == null) {
+                Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
+                return null;
+            }
+        }
+        // Don't call this while holding the lock as this operation might hit the disk.
+        return task.getSnapshot(reducedResolution, restoreFromDisk);
+    }
+
     @Override
     public void setDisablePreviewScreenshots(IBinder token, boolean disable) {
         synchronized (mGlobalLock) {
@@ -7419,10 +7424,10 @@
         }
 
         @Override
-        public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
-            synchronized (mGlobalLock) {
-                return ActivityTaskManagerService.this.getTaskSnapshot(taskId, reducedResolution);
-            }
+        public ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
+                boolean reducedResolution) {
+            return ActivityTaskManagerService.this.getTaskSnapshot(taskId, reducedResolution,
+                    false /* restoreFromDisk */);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index cae7612..4a9a3f7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -176,6 +176,7 @@
     boolean inPendingTransaction;
     boolean allDrawn;
     private boolean mLastAllDrawn;
+    private boolean mUseTransferredAnimation;
 
     // Set to true when this app creates a surface while in the middle of an animation. In that
     // case do not clear allDrawn until the animation completes.
@@ -618,9 +619,12 @@
             boolean runningAppAnimation = false;
 
             if (transit != WindowManager.TRANSIT_UNSET) {
-                if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) {
-                    delayed = runningAppAnimation = true;
+                if (mUseTransferredAnimation) {
+                    runningAppAnimation = isReallyAnimating();
+                } else if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) {
+                    runningAppAnimation = true;
                 }
+                delayed = runningAppAnimation;
                 final WindowState window = findMainWindow();
                 if (window != null && accessibilityController != null) {
                     accessibilityController.onAppWindowTransitionLocked(window, transit);
@@ -667,6 +671,7 @@
                 getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
             }
         }
+        mUseTransferredAnimation = false;
 
         if (isReallyAnimating()) {
             delayed = true;
@@ -1531,9 +1536,9 @@
                 transferAnimation(fromToken);
 
                 // When transferring an animation, we no longer need to apply an animation to the
-                // the token we transfer the animation over. Thus, remove the animation from
-                // pending opening apps.
-                getDisplayContent().mOpeningApps.remove(this);
+                // the token we transfer the animation over. Thus, set this flag to indicate we've
+                // transferred the animation.
+                mUseTransferredAnimation = true;
 
                 mWmService.updateFocusedWindowLocked(
                         UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 298b302..3fd4e83 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -851,11 +851,12 @@
     /**
      * DO NOT HOLD THE ACTIVITY MANAGER LOCK WHEN CALLING THIS METHOD!
      */
-    TaskSnapshot getSnapshot(boolean reducedResolution) {
+    TaskSnapshot getSnapshot(boolean reducedResolution, boolean restoreFromDisk) {
 
         // TODO: Move this to {@link TaskWindowContainerController} once recent tasks are more
         // synchronized between AM and WM.
-        return mService.mWindowManager.getTaskSnapshot(taskId, userId, reducedResolution);
+        return mService.mWindowManager.getTaskSnapshot(taskId, userId, reducedResolution,
+                restoreFromDisk);
     }
 
     void touchActiveTime() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0a2fc9b..45c7e69 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3551,8 +3551,9 @@
         return true;
     }
 
-    public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean reducedResolution) {
-        return mTaskSnapshotController.getSnapshot(taskId, userId, true /* restoreFromDisk */,
+    public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean reducedResolution,
+            boolean restoreFromDisk) {
+        return mTaskSnapshotController.getSnapshot(taskId, userId, restoreFromDisk,
                 reducedResolution);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
index 62b0ca8..891ca74 100644
--- a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
@@ -30,7 +30,7 @@
 import com.android.server.content.ContentService.ObserverNode;
 
 /**
- * bit FrameworksServicesTests:com.android.server.content.ObserverNodeTest
+ * atest FrameworksServicesTests:com.android.server.content.ObserverNodeTest
  */
 @SmallTest
 public class ObserverNodeTest extends AndroidTestCase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 8f41a42..1f8b33e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -24,14 +24,18 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -107,6 +111,39 @@
     }
 
     @Test
+    public void testRestartRecentsActivity() throws Exception {
+        // Have a recents activity that is not attached to its process (ActivityRecord.app = null).
+        ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        ActivityStack recentsStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        ActivityRecord recentActivity = new ActivityBuilder(mService).setComponent(
+                mRecentsComponent).setCreateTask(true).setStack(recentsStack).build();
+        WindowProcessController app = recentActivity.app;
+        recentActivity.app = null;
+
+        // Start an activity on top.
+        new ActivityBuilder(mService).setCreateTask(true).build().getActivityStack().moveToFront(
+                "testRestartRecentsActivity");
+
+        doCallRealMethod().when(mRootActivityContainer).ensureActivitiesVisible(
+                any() /* starting */, anyInt() /* configChanges */,
+                anyBoolean() /* preserveWindows */);
+        doReturn(app).when(mService).getProcessController(eq(recentActivity.processName), anyInt());
+        ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
+        doNothing().when(lifecycleManager).scheduleTransaction(any());
+        AppWarnings appWarnings = mService.getAppWarningsLocked();
+        spyOn(appWarnings);
+        doNothing().when(appWarnings).onStartActivity(any());
+
+        startRecentsActivity();
+
+        // Recents activity must be restarted, but not be resumed while running recents animation.
+        verify(mRootActivityContainer.mStackSupervisor).startSpecificActivityLocked(
+                eq(recentActivity), eq(false), anyBoolean());
+        assertThat(recentActivity.getState()).isEqualTo(PAUSED);
+    }
+
+    @Test
     public void testSetLaunchTaskBehindOfTargetActivity() {
         ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
         display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index c77e25f..8d2c3dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -35,6 +36,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
 
 import static org.junit.Assert.assertEquals;
@@ -397,6 +399,58 @@
     }
 
     /**
+     * Verify that home activity will be started on a display even if another display has a
+     * focusable activity.
+     */
+    @Test
+    public void testResumeFocusedStacksStartsHomeActivity_NoActivities() {
+        mFullscreenStack.remove();
+        mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY).getHomeStack().remove();
+        mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY)
+                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+
+        doReturn(true).when(mRootActivityContainer).resumeHomeActivity(any(), any(), anyInt());
+
+        mService.setBooted(true);
+
+        // Trigger resume on all displays
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
+
+        // Verify that home activity was started on the default display
+        verify(mRootActivityContainer).resumeHomeActivity(any(), any(), eq(DEFAULT_DISPLAY));
+    }
+
+    /**
+     * Verify that home activity will be started on a display even if another display has a
+     * focusable activity.
+     */
+    @Test
+    public void testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen() {
+        mFullscreenStack.remove();
+        mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY).getHomeStack().remove();
+        mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY)
+                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+
+        // Create an activity on secondary display.
+        final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
+                ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        new ActivityBuilder(mService).setTask(task).build();
+
+        doReturn(true).when(mRootActivityContainer).resumeHomeActivity(any(), any(), anyInt());
+
+        mService.setBooted(true);
+
+        // Trigger resume on all displays
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
+
+        // Verify that home activity was started on the default display
+        verify(mRootActivityContainer).resumeHomeActivity(any(), any(), eq(DEFAULT_DISPLAY));
+    }
+
+    /**
      * Verify that a lingering transition is being executed in case the activity to be resumed is
      * already resumed
      */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8ef381d..9806199 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2685,6 +2685,30 @@
         public static final String KEY_PREFIX = "gps.";
 
         /**
+         * Location information during (and after) an emergency call is only provided over control
+         * plane signaling from the network.
+         * @hide
+         */
+        public static final int SUPL_EMERGENCY_MODE_TYPE_CP_ONLY = 0;
+
+        /**
+         * Location information during (and after) an emergency call is provided over the data
+         * plane and serviced by the framework GNSS service, but if it fails, the carrier also
+         * supports control plane backup signaling.
+         * @hide
+         */
+        public static final int SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK = 1;
+
+        /**
+         * Location information during (and after) an emergency call is provided over the data plane
+         * and serviced by the framework GNSS service only. There is no backup signalling over the
+         * control plane if it fails.
+         * @hide
+         */
+        public static final int SUPL_EMERGENCY_MODE_TYPE_DP_ONLY = 2;
+
+
+        /**
          * Determine whether current lpp_mode used for E-911 needs to be kept persistently.
          * {@code false} - not keeping the lpp_mode means using default configuration of gps.conf
          *                 when sim is not presented.
@@ -2775,6 +2799,23 @@
          */
         public static final String KEY_NFW_PROXY_APPS_STRING = KEY_PREFIX + "nfw_proxy_apps";
 
+        /**
+         * Determines whether or not SUPL ES mode supports a control-plane mechanism to get a user's
+         * location in the event that data plane SUPL fails or is otherwise unavailable.
+         * <p>
+         * An integer value determines the support type of this carrier. If this carrier only
+         * supports data plane SUPL ES, then the value will be
+         * {@link #SUPL_EMERGENCY_MODE_TYPE_DP_ONLY}. If the carrier supports control plane fallback
+         * for emergency SUPL, the value will be {@link #SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK}.
+         * If the carrier does not support data plane SUPL using the framework, the value will be
+         * {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
+         * <p>
+         * The default value for this configuration is {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
+         * @hide
+         */
+        public static final String KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT = KEY_PREFIX
+                + "es_supl_control_plane_support_int";
+
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
             defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
@@ -2789,6 +2830,8 @@
             defaults.putString(KEY_GPS_LOCK_STRING, "3");
             defaults.putString(KEY_ES_EXTENSION_SEC_STRING, "0");
             defaults.putString(KEY_NFW_PROXY_APPS_STRING, "");
+            defaults.putInt(KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                    SUPL_EMERGENCY_MODE_TYPE_CP_ONLY);
             return defaults;
         }
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d6011b9..b7b511e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1498,6 +1498,48 @@
      */
     public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4;
 
+    /**
+     * Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
+     * to indicate if the SIM combination in DSDS has limitation or compatible issue.
+     * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios.
+     *
+     * @hide
+     */
+    public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE =
+            "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
+
+    /** @hide */
+    @IntDef({
+            EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE,
+            EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SimCombinationWarningType{}
+
+    /**
+     * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+     * to indicate there's no SIM combination warning.
+     * @hide
+     */
+    public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0;
+
+    /**
+     * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+     * to indicate two active SIMs are both CDMA hence there might be functional limitation.
+     * @hide
+     */
+    public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1;
+
+    /**
+     * String intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
+     * to indicate what's the name of SIM combination it has limitation or compatible issue.
+     * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios, and the
+     * name will be "operator1 & operator2".
+     *
+     * @hide
+     */
+    public static final String EXTRA_SIM_COMBINATION_NAMES =
+            "android.telephony.extra.SIM_COMBINATION_NAMES";
     //
     //
     // Device Info
diff --git a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
index 17fa931..f88a7c4 100644
--- a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
+++ b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
@@ -47,17 +47,19 @@
 @RunWith(AndroidJUnit4.class)
 public class ColorExtractorTest {
 
-    Context mContext;
+    private Context mContext;
+    private WallpaperManager mWallpaperManager;
 
     @Before
     public void setup() {
         mContext = InstrumentationRegistry.getContext();
+        mWallpaperManager = mock(WallpaperManager.class);
     }
 
     @Test
     public void ColorExtractor_extractWhenInitialized() {
         ExtractionType type = mock(Tonal.class);
-        new ColorExtractor(mContext, type, true);
+        new ColorExtractor(mContext, type, true, mWallpaperManager);
         // 1 for lock and 1 for system
         verify(type, times(2))
                 .extractInto(any(), any(), any(), any());
@@ -84,7 +86,7 @@
                     outGradientColorsDark.set(colorsExpectedDark);
                     outGradientColorsExtraDark.set(colorsExpectedExtraDark);
                 };
-        ColorExtractor extractor = new ColorExtractor(mContext, type, true);
+        ColorExtractor extractor = new ColorExtractor(mContext, type, true, mWallpaperManager);
 
         GradientColors colors = extractor.getColors(WallpaperManager.FLAG_SYSTEM,
                 ColorExtractor.TYPE_NORMAL);
@@ -99,7 +101,8 @@
     public void addOnColorsChangedListener_invokesListener() {
         ColorExtractor.OnColorsChangedListener mockedListeners =
                 mock(ColorExtractor.OnColorsChangedListener.class);
-        ColorExtractor extractor = new ColorExtractor(mContext, new Tonal(mContext), true);
+        ColorExtractor extractor = new ColorExtractor(mContext, new Tonal(mContext), true,
+                mWallpaperManager);
         extractor.addOnColorsChangedListener(mockedListeners);
 
         extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.RED), null, null),