Merge changes from topic "sc_handles"

* changes:
  Remove SurfaceControl#getHandle from JAVA apis 2/2
  Remove SurfaceControl#getHandle from JAVA apis 1/2
diff --git a/api/test-current.txt b/api/test-current.txt
index 0127dfe..bf2730a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -318,10 +318,16 @@
   }
 
   public final class NotificationChannel implements android.os.Parcelable {
+    method public int getOriginalImportance();
+    method public boolean isBlockableSystem();
     method public boolean isImportanceLockedByCriticalDeviceFunction();
     method public boolean isImportanceLockedByOEM();
+    method public void setBlockableSystem(boolean);
+    method public void setDeleted(boolean);
+    method public void setFgServiceShown(boolean);
     method public void setImportanceLockedByCriticalDeviceFunction(boolean);
     method public void setImportanceLockedByOEM(boolean);
+    method public void setOriginalImportance(int);
   }
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 69ec831..3effd11 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,8 +15,6 @@
  */
 package android.app;
 
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -86,6 +84,7 @@
     private static final String ATT_GROUP = "group";
     private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
     private static final String ATT_ALLOW_BUBBLE = "can_bubble";
+    private static final String ATT_ORIG_IMP = "orig_imp";
     private static final String DELIMITER = ",";
 
     /**
@@ -151,6 +150,7 @@
     private String mName;
     private String mDesc;
     private int mImportance = DEFAULT_IMPORTANCE;
+    private int mOriginalImportance = DEFAULT_IMPORTANCE;
     private boolean mBypassDnd;
     private int mLockscreenVisibility = DEFAULT_VISIBILITY;
     private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI;
@@ -234,6 +234,7 @@
         mBlockableSystem = in.readBoolean();
         mAllowBubbles = in.readBoolean();
         mImportanceLockedByOEM = in.readBoolean();
+        mOriginalImportance = in.readInt();
     }
 
     @Override
@@ -288,6 +289,7 @@
         dest.writeBoolean(mBlockableSystem);
         dest.writeBoolean(mAllowBubbles);
         dest.writeBoolean(mImportanceLockedByOEM);
+        dest.writeInt(mOriginalImportance);
     }
 
     /**
@@ -307,6 +309,7 @@
     /**
      * @hide
      */
+    @TestApi
     public void setFgServiceShown(boolean shown) {
         mFgServiceShown = shown;
     }
@@ -314,6 +317,7 @@
     /**
      * @hide
      */
+    @TestApi
     public void setDeleted(boolean deleted) {
         mDeleted = deleted;
     }
@@ -322,6 +326,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @TestApi
     public void setBlockableSystem(boolean blockableSystem) {
         mBlockableSystem = blockableSystem;
     }
@@ -641,6 +646,7 @@
     /**
      * @hide
      */
+    @TestApi
     public boolean isBlockableSystem() {
         return mBlockableSystem;
     }
@@ -678,6 +684,22 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public int getOriginalImportance() {
+        return mOriginalImportance;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public void setOriginalImportance(int importance) {
+        mOriginalImportance = importance;
+    }
+
+    /**
      * Returns whether the user has chosen the importance of this channel, either to affirm the
      * initial selection from the app, or changed it to be higher or lower.
      * @see #getImportance()
@@ -729,6 +751,7 @@
         setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
         setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
         setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
+        setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE));
     }
 
     @Nullable
@@ -850,6 +873,9 @@
         if (canBubble() != DEFAULT_ALLOW_BUBBLE) {
             out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(canBubble()));
         }
+        if (getOriginalImportance() != DEFAULT_IMPORTANCE) {
+            out.attribute(null, ATT_ORIG_IMP, Integer.toString(getOriginalImportance()));
+        }
 
         // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
         // truth and so aren't written to this xml file
@@ -896,6 +922,7 @@
         record.put(ATT_GROUP, getGroup());
         record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem());
         record.put(ATT_ALLOW_BUBBLE, canBubble());
+        // TODO: original importance
         return record;
     }
 
@@ -1005,7 +1032,8 @@
                 && Objects.equals(getGroup(), that.getGroup())
                 && Objects.equals(getAudioAttributes(), that.getAudioAttributes())
                 && mImportanceLockedByOEM == that.mImportanceLockedByOEM
-                && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp;
+                && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
+                && mOriginalImportance == that.mOriginalImportance;
     }
 
     @Override
@@ -1015,7 +1043,7 @@
                 getUserLockedFields(),
                 isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
                 getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
-                mImportanceLockedByOEM, mImportanceLockedDefaultApp);
+                mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance);
         result = 31 * result + Arrays.hashCode(mVibration);
         return result;
     }
@@ -1045,6 +1073,7 @@
                 + ", mAllowBubbles=" + mAllowBubbles
                 + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
                 + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
+                + ", mOriginalImp=" + mOriginalImportance
                 + '}';
         pw.println(prefix + output);
     }
@@ -1073,6 +1102,7 @@
                 + ", mAllowBubbles=" + mAllowBubbles
                 + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
                 + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
+                + ", mOriginalImp=" + mOriginalImportance
                 + '}';
     }
 
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index bf0ef94..2c2c295 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1122,6 +1122,9 @@
             if (limit > lineEnd) {
                 limit = lineEnd;
             }
+            if (limit == start) {
+                continue;
+            }
             level[limit - lineStart - 1] =
                     (byte) ((runs[i + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK);
         }
@@ -1220,8 +1223,8 @@
     }
 
     /**
-     * Computes in linear time the results of calling
-     * #getHorizontal for all offsets on a line.
+     * Computes in linear time the results of calling #getHorizontal for all offsets on a line.
+     *
      * @param line The line giving the offsets we compute information for
      * @param clamped Whether to clamp the results to the width of the layout
      * @param primary Whether the results should be the primary or the secondary horizontal
@@ -1257,7 +1260,7 @@
         TextLine.recycle(tl);
 
         if (clamped) {
-            for (int offset = 0; offset <= wid.length; ++offset) {
+            for (int offset = 0; offset < wid.length; ++offset) {
                 if (wid[offset] > mWidth) {
                     wid[offset] = mWidth;
                 }
diff --git a/core/java/android/util/TimingsTraceLog.java b/core/java/android/util/TimingsTraceLog.java
index bc21722..5370645 100644
--- a/core/java/android/util/TimingsTraceLog.java
+++ b/core/java/android/util/TimingsTraceLog.java
@@ -21,10 +21,10 @@
 import android.os.SystemClock;
 import android.os.Trace;
 
-import java.util.ArrayDeque;
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Deque;
 import java.util.List;
 
 /**
@@ -37,16 +37,37 @@
 public class TimingsTraceLog {
     // Debug boot time for every step if it's non-user build.
     private static final boolean DEBUG_BOOT_TIME = !Build.IS_USER;
-    private final Deque<Pair<String, Long>> mStartTimes =
-            DEBUG_BOOT_TIME ? new ArrayDeque<>() : null;
+
+    // Maximum number of nested calls that are stored
+    private static final int MAX_NESTED_CALLS = 10;
+
+    private final String[] mStartNames;
+    private final long[] mStartTimes;
+
     private final String mTag;
-    private long mTraceTag;
-    private long mThreadId;
+    private final long mTraceTag;
+    private final long mThreadId;
+    private final int mMaxNestedCalls;
+
+    private int mCurrentLevel = -1;
 
     public TimingsTraceLog(String tag, long traceTag) {
+        this(tag, traceTag, DEBUG_BOOT_TIME ? MAX_NESTED_CALLS : -1);
+    }
+
+    @VisibleForTesting
+    public TimingsTraceLog(String tag, long traceTag, int maxNestedCalls) {
         mTag = tag;
         mTraceTag = traceTag;
         mThreadId = Thread.currentThread().getId();
+        mMaxNestedCalls = maxNestedCalls;
+        if (maxNestedCalls > 0) {
+            mStartNames = new String[maxNestedCalls];
+            mStartTimes = new long[maxNestedCalls];
+        } else {
+            mStartNames = null;
+            mStartTimes = null;
+        }
     }
 
     /**
@@ -56,27 +77,41 @@
     public void traceBegin(String name) {
         assertSameThread();
         Trace.traceBegin(mTraceTag, name);
-        if (DEBUG_BOOT_TIME) {
-            mStartTimes.push(Pair.create(name, SystemClock.elapsedRealtime()));
+
+        if (!DEBUG_BOOT_TIME) return;
+
+        if (mCurrentLevel + 1 >= mMaxNestedCalls) {
+            Slog.w(mTag, "not tracing duration of '" + name + "' because already reached "
+                    + mMaxNestedCalls + " levels");
+            return;
         }
+
+        mCurrentLevel++;
+        mStartNames[mCurrentLevel] = name;
+        mStartTimes[mCurrentLevel] = SystemClock.elapsedRealtime();
     }
 
     /**
      * End tracing previously {@link #traceBegin(String) started} section.
-     * Also {@link #logDuration logs} the duration.
+     *
+     * <p>Also {@link #logDuration logs} the duration.
      */
     public void traceEnd() {
         assertSameThread();
         Trace.traceEnd(mTraceTag);
-        if (!DEBUG_BOOT_TIME) {
-            return;
-        }
-        if (mStartTimes.peek() == null) {
+
+        if (!DEBUG_BOOT_TIME) return;
+
+        if (mCurrentLevel < 0) {
             Slog.w(mTag, "traceEnd called more times than traceBegin");
             return;
         }
-        Pair<String, Long> event = mStartTimes.pop();
-        logDuration(event.first, (SystemClock.elapsedRealtime() - event.second));
+
+        final String name = mStartNames[mCurrentLevel];
+        final long duration = SystemClock.elapsedRealtime() - mStartTimes[mCurrentLevel];
+        mCurrentLevel--;
+
+        logDuration(name, duration);
     }
 
     private void assertSameThread() {
@@ -89,7 +124,7 @@
     }
 
     /**
-     * Log the duration so it can be parsed by external tools for performance reporting
+     * Logs a duration so it can be parsed by external tools for performance reporting.
      */
     public void logDuration(String name, long timeMs) {
         Slog.d(mTag, name + " took to complete: " + timeMs + "ms");
@@ -105,10 +140,11 @@
      */
     @NonNull
     public final List<String> getUnfinishedTracesForDebug() {
-        if (mStartTimes == null || mStartTimes.isEmpty()) return Collections.emptyList();
-
-        final ArrayList<String> stack = new ArrayList<>(mStartTimes.size());
-        mStartTimes.descendingIterator().forEachRemaining((pair) -> stack.add(pair.first));
-        return stack;
+        if (mStartTimes == null || mCurrentLevel < 0) return Collections.emptyList();
+        final ArrayList<String> list = new ArrayList<>(mCurrentLevel + 1);
+        for (int i = 0; i <= mCurrentLevel; i++) {
+            list.add(mStartNames[i]);
+        }
+        return list;
     }
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index be13710..d1ab7c99 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -84,7 +84,6 @@
     private static native long nativeCopyFromSurfaceControl(long nativeObject);
     private static native void nativeWriteToParcel(long nativeObject, Parcel out);
     private static native void nativeRelease(long nativeObject);
-    private static native void nativeDestroy(long nativeObject);
     private static native void nativeDisconnect(long nativeObject);
 
     private static native ScreenshotGraphicBuffer nativeScreenshot(IBinder displayToken,
@@ -931,19 +930,6 @@
     }
 
     /**
-     * Release the local resources like {@link #release} but also
-     * remove the Surface from the screen.
-     * @hide
-     */
-    public void remove() {
-        if (mNativeObject != 0) {
-            nativeDestroy(mNativeObject);
-            mNativeObject = 0;
-        }
-        mCloseGuard.close();
-    }
-
-    /**
      * Disconnect any client still connected to the surface.
      * @hide
      */
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f765471..f661e06 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -200,6 +200,7 @@
     private int mPendingReportDraws;
 
     private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
+    private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
 
     public SurfaceView(Context context) {
         this(context, null);
@@ -339,7 +340,7 @@
 
         updateSurface();
         if (mSurfaceControl != null) {
-            mSurfaceControl.remove();
+            mTmpTransaction.remove(mSurfaceControl).apply();
         }
         mSurfaceControl = null;
 
@@ -534,13 +535,14 @@
 
     private void releaseSurfaces() {
         if (mSurfaceControl != null) {
-            mSurfaceControl.remove();
+            mTmpTransaction.remove(mSurfaceControl);
             mSurfaceControl = null;
         }
         if (mBackgroundControl != null) {
-            mBackgroundControl.remove();
+            mTmpTransaction.remove(mBackgroundControl);
             mBackgroundControl = null;
         }
+        mTmpTransaction.apply();
     }
 
     /** @hide */
@@ -849,7 +851,7 @@
         }
 
         if (mDeferredDestroySurfaceControl != null) {
-            mDeferredDestroySurfaceControl.remove();
+            mTmpTransaction.remove(mDeferredDestroySurfaceControl).apply();
             mDeferredDestroySurfaceControl = null;
         }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 6b30f5e..fa1c05d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1631,7 +1631,7 @@
         mSurfaceSession = null;
 
         if (mBoundsSurfaceControl != null) {
-            mBoundsSurfaceControl.remove();
+            mTransaction.remove(mBoundsSurfaceControl).apply();
             mBoundsSurface.release();
             mBoundsSurfaceControl = null;
         }
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index ccafa64..d3c6972 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -1035,7 +1035,7 @@
             // Destroy the renderer. This will not proceed until pending frame callbacks complete.
             mRenderer.destroy();
             mSurface.destroy();
-            mSurfaceControl.remove();
+            new SurfaceControl.Transaction().remove(mSurfaceControl).apply();
             mSurfaceSession.kill();
             mHandler.removeCallbacks(mMagnifierUpdater);
             if (mBitmap != null) {
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 454fe3c..0e7c1d4 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -196,6 +196,11 @@
     public static final String ASSIST_TAP_PASSTHROUGH = "assist_tap_passthrough";
   
     /**
+     * (bool) Whether to show handles when taught.
+     */
+    public static final String ASSIST_HANDLES_SHOW_WHEN_TAUGHT = "assist_handles_show_when_taught";
+
+    /**
      * (bool) Whether to use the new BrightLineFalsingManager.
      */
     public static final String BRIGHTLINE_FALSING_MANAGER_ENABLED =
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 53a35be..78dd1aa 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -217,12 +217,6 @@
     ctrl->decStrong((void *)nativeCreate);
 }
 
-static void nativeDestroy(JNIEnv* env, jclass clazz, jlong nativeObject) {
-    sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject));
-    ctrl->destroy();
-    ctrl->decStrong((void *)nativeCreate);
-}
-
 static void nativeDisconnect(JNIEnv* env, jclass clazz, jlong nativeObject) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     if (ctrl != NULL) {
@@ -1278,8 +1272,6 @@
             (void*)nativeWriteToParcel },
     {"nativeRelease", "(J)V",
             (void*)nativeRelease },
-    {"nativeDestroy", "(J)V",
-            (void*)nativeDestroy },
     {"nativeDisconnect", "(J)V",
             (void*)nativeDisconnect },
     {"nativeCreateTransaction", "()J",
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 990161a..93a6b15 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -743,6 +743,9 @@
         assertPrimaryIsTrailingPrevious(
                 RTL + LRI + RTL + LTR + PDI + RTL,
                 new boolean[]{false, false, true, false, false, false, false});
+        assertPrimaryIsTrailingPrevious(
+                "",
+                new boolean[]{false});
     }
 }
 
diff --git a/core/tests/coretests/src/android/util/TimingsTraceLogTest.java b/core/tests/coretests/src/android/util/TimingsTraceLogTest.java
deleted file mode 100644
index f76471c..0000000
--- a/core/tests/coretests/src/android/util/TimingsTraceLogTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Trace;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Tests for {@link TimingsTraceLog}.
- *
- * <p>Usage: {@code atest FrameworksCoreTests:android.util.TimingsTraceLogTest}
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TimingsTraceLogTest {
-
-    @Test
-    public void testDifferentThreads() throws Exception {
-        TimingsTraceLog log = new TimingsTraceLog("TEST", Trace.TRACE_TAG_APP);
-        // Should be able to log on the same thread
-        log.traceBegin("test");
-        log.traceEnd();
-        final List<String> errors = new ArrayList<>();
-        // Calling from a different thread should fail
-        Thread t = new Thread(() -> {
-            try {
-                log.traceBegin("test");
-                errors.add("traceBegin should fail on a different thread");
-            } catch (IllegalStateException expected) {
-            }
-            try {
-                log.traceEnd();
-                errors.add("traceEnd should fail on a different thread");
-            } catch (IllegalStateException expected) {
-            }
-            // Verify that creating a new log will work
-            TimingsTraceLog log2 = new TimingsTraceLog("TEST", Trace.TRACE_TAG_APP);
-            log2.traceBegin("test");
-            log2.traceEnd();
-
-        });
-        t.start();
-        t.join();
-        assertThat(errors).isEmpty();
-    }
-
-    @Test
-    public void testGetUnfinishedTracesForDebug() {
-        TimingsTraceLog log = new TimingsTraceLog("TEST", Trace.TRACE_TAG_APP);
-        assertThat(log.getUnfinishedTracesForDebug()).isEmpty();
-
-        log.traceBegin("One");
-        assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder();
-
-        log.traceBegin("Two");
-        assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One", "Two").inOrder();
-
-        log.traceEnd();
-        assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder();
-
-        log.traceEnd();
-        assertThat(log.getUnfinishedTracesForDebug()).isEmpty();
-    }
-}
diff --git a/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
new file mode 100644
index 0000000..5dc44d2
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import static android.os.Trace.TRACE_TAG_APP;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.contains;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.matches;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+
+import android.os.Trace;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.MockedVoidMethod;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link TimingsTraceLog}.
+ *
+ * <p>Usage: {@code atest FrameworksMockingCoreTests:android.util.TimingsTraceLogTest}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TimingsTraceLogTest {
+
+    private static final String TAG = "TEST";
+
+    private MockitoSession mSession;
+
+    @Before
+    public final void startMockSession() {
+        mSession = mockitoSession()
+                .spyStatic(Slog.class)
+                .spyStatic(Trace.class)
+                .startMocking();
+    }
+
+    @After
+    public final void finishMockSession() {
+        mSession.finishMocking();
+    }
+
+    @Test
+    public void testDifferentThreads() throws Exception {
+        TimingsTraceLog log = new TimingsTraceLog(TAG, TRACE_TAG_APP);
+        // Should be able to log on the same thread
+        log.traceBegin("test");
+        log.traceEnd();
+        final List<String> errors = new ArrayList<>();
+        // Calling from a different thread should fail
+        Thread t = new Thread(() -> {
+            try {
+                log.traceBegin("test");
+                errors.add("traceBegin should fail on a different thread");
+            } catch (IllegalStateException expected) {
+            }
+            try {
+                log.traceEnd();
+                errors.add("traceEnd should fail on a different thread");
+            } catch (IllegalStateException expected) {
+            }
+            // Verify that creating a new log will work
+            TimingsTraceLog log2 = new TimingsTraceLog(TAG, TRACE_TAG_APP);
+            log2.traceBegin("test");
+            log2.traceEnd();
+
+        });
+        t.start();
+        t.join();
+        assertThat(errors).isEmpty();
+    }
+
+    @Test
+    public void testGetUnfinishedTracesForDebug() {
+        TimingsTraceLog log = new TimingsTraceLog("TEST", Trace.TRACE_TAG_APP);
+        assertThat(log.getUnfinishedTracesForDebug()).isEmpty();
+
+        log.traceBegin("One");
+        assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder();
+
+        log.traceBegin("Two");
+        assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One", "Two").inOrder();
+
+        log.traceEnd();
+        assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder();
+
+        log.traceEnd();
+        assertThat(log.getUnfinishedTracesForDebug()).isEmpty();
+    }
+
+    @Test
+    public void testLogDuration() throws Exception {
+        TimingsTraceLog log = new TimingsTraceLog(TAG, TRACE_TAG_APP, 10);
+        log.logDuration("logro", 42);
+        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), contains("logro took to complete: 42ms")));
+    }
+
+    @Test
+    public void testOneLevel() throws Exception {
+        TimingsTraceLog log = new TimingsTraceLog(TAG, TRACE_TAG_APP, 10);
+        log.traceBegin("test");
+        log.traceEnd();
+
+        verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "test"));
+        verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP));
+        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("test took to complete: \\dms")));
+    }
+
+    @Test
+    public void testMultipleLevels() throws Exception {
+        TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_APP, 10);
+        log.traceBegin("L1");
+        log.traceBegin("L2");
+        log.traceEnd();
+        log.traceEnd();
+
+        verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L1"));
+        verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L2"));
+        verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(2)); // L1 and L2
+
+        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L2 took to complete: \\d+ms")));
+        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L1 took to complete: \\d+ms")));
+    }
+
+    @Test
+    public void testTooManyLevels() throws Exception {
+        TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_APP, 2);
+
+        log.traceBegin("L1"); // ok
+        log.traceBegin("L2"); // ok
+        log.traceBegin("L3"); // logging ignored ( > 2)
+
+        log.traceEnd();
+        log.traceEnd();
+        log.traceEnd();
+
+        verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L1"));
+        verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L2"));
+        verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L3"));
+        verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(3));
+
+        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L2 took to complete: \\d+ms")));
+        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L1 took to complete: \\d+ms")));
+        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L3 took to complete: \\d+ms")),
+                never());
+
+        verify((MockedVoidMethod) () -> Slog.w(TAG, "not tracing duration of 'L3' "
+                + "because already reached 2 levels"));
+    }
+
+    @Test
+    public void testEndNoBegin() throws Exception {
+        TimingsTraceLog log = new TimingsTraceLog(TAG, TRACE_TAG_APP);
+        log.traceEnd();
+        verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP));
+        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), anyString()), never());
+        verify((MockedVoidMethod) () -> Slog.w(TAG, "traceEnd called more times than traceBegin"));
+    }
+}
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 5133bae..3b8caeb 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -27,7 +27,7 @@
 namespace skiapipeline {
 
 StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
-        : mEndChildIndex(0), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {}
+        : mEndChildIndex(-1), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {}
 
 void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
     if (mChildren.empty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 0af333e..f9ddeae 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -67,6 +67,7 @@
     private static final boolean DEFAULT_SUPPRESS_ON_LOCKSCREEN = false;
     private static final boolean DEFAULT_SUPPRESS_ON_LAUNCHER = false;
     private static final boolean DEFAULT_SUPPRESS_ON_APPS = true;
+    private static final boolean DEFAULT_SHOW_WHEN_TAUGHT = false;
 
     private static final String[] DEFAULT_HOME_CHANGE_ACTIONS = new String[] {
             PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED,
@@ -309,7 +310,7 @@
             return;
         }
 
-        if (mIsDozing || mIsNavBarHidden || mOnLockscreen) {
+        if (mIsDozing || mIsNavBarHidden || mOnLockscreen || !getShowWhenTaught()) {
             mAssistHandleCallbacks.hide();
         } else if (justUnlocked) {
             long currentEpochDay = LocalDate.now().toEpochDay();
@@ -429,6 +430,12 @@
                 DEFAULT_SUPPRESS_ON_APPS);
     }
 
+    private boolean getShowWhenTaught() {
+        return mPhenotypeHelper.getBoolean(
+                SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_WHEN_TAUGHT,
+                DEFAULT_SHOW_WHEN_TAUGHT);
+    }
+
     @Override
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "Current AssistHandleReminderExpBehavior State:");
@@ -480,5 +487,9 @@
                 + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_APPS
                 + "="
                 + getSuppressOnApps());
+        pw.println(prefix + "      "
+                + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_WHEN_TAUGHT
+                + "="
+                + getShowWhenTaught());
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index d6657f1..8415272 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -31,6 +31,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.IWindow;
@@ -83,6 +84,7 @@
             mInteractionConnections = new SparseArray<>();
     private final SparseArray<SparseArray<IBinder>> mWindowTokens = new SparseArray<>();
 
+    private final List<WindowInfo> mCachedWindowInfos = new ArrayList<>();
     private List<AccessibilityWindowInfo> mWindows;
 
     private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection;
@@ -173,25 +175,133 @@
     }
 
     /**
-     * Callbacks from from window manager when there's an accessibility change in windows.
+     * Callbacks from window manager when there's an accessibility change in windows.
      *
+     * @param forceSend Send the windows for accessibility even if they haven't changed.
      * @param windows The windows of current display for accessibility.
      */
     @Override
-    public void onWindowsForAccessibilityChanged(@NonNull List<WindowInfo> windows) {
+    public void onWindowsForAccessibilityChanged(boolean forceSend,
+            @NonNull List<WindowInfo> windows) {
         synchronized (mLock) {
             if (DEBUG) {
                 Slog.i(LOG_TAG, "Windows changed: " + windows);
             }
 
-            // Let the policy update the focused and active windows.
-            updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows);
-
-            // Someone may be waiting for the windows - advertise it.
-            mLock.notifyAll();
+            if (shouldUpdateWindowsLocked(forceSend, windows)) {
+                cacheWindows(windows);
+                // Let the policy update the focused and active windows.
+                updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows);
+                // Someone may be waiting for the windows - advertise it.
+                mLock.notifyAll();
+            }
         }
     }
 
+    private boolean shouldUpdateWindowsLocked(boolean forceSend,
+            @NonNull List<WindowInfo> windows) {
+        if (forceSend) {
+            return true;
+        }
+
+        final int windowCount = windows.size();
+        // We computed the windows and if they changed notify the client.
+        if (mCachedWindowInfos.size() != windowCount) {
+            // Different size means something changed.
+            return true;
+        } else if (!mCachedWindowInfos.isEmpty() || !windows.isEmpty()) {
+            // Since we always traverse windows from high to low layer
+            // the old and new windows at the same index should be the
+            // same, otherwise something changed.
+            for (int i = 0; i < windowCount; i++) {
+                WindowInfo oldWindow = mCachedWindowInfos.get(i);
+                WindowInfo newWindow = windows.get(i);
+                // We do not care for layer changes given the window
+                // order does not change. This brings no new information
+                // to the clients.
+                if (windowChangedNoLayer(oldWindow, newWindow)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private void cacheWindows(List<WindowInfo> windows) {
+        final int oldWindowCount = mCachedWindowInfos.size();
+        for (int i = oldWindowCount - 1; i >= 0; i--) {
+            mCachedWindowInfos.remove(i).recycle();
+        }
+        final int newWindowCount = windows.size();
+        for (int i = 0; i < newWindowCount; i++) {
+            WindowInfo newWindow = windows.get(i);
+            mCachedWindowInfos.add(WindowInfo.obtain(newWindow));
+        }
+    }
+
+    private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
+        if (oldWindow == newWindow) {
+            return false;
+        }
+        if (oldWindow == null) {
+            return true;
+        }
+        if (newWindow == null) {
+            return true;
+        }
+        if (oldWindow.type != newWindow.type) {
+            return true;
+        }
+        if (oldWindow.focused != newWindow.focused) {
+            return true;
+        }
+        if (oldWindow.token == null) {
+            if (newWindow.token != null) {
+                return true;
+            }
+        } else if (!oldWindow.token.equals(newWindow.token)) {
+            return true;
+        }
+        if (oldWindow.parentToken == null) {
+            if (newWindow.parentToken != null) {
+                return true;
+            }
+        } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
+            return true;
+        }
+        if (oldWindow.activityToken == null) {
+            if (newWindow.activityToken != null) {
+                return true;
+            }
+        } else if (!oldWindow.activityToken.equals(newWindow.activityToken)) {
+            return true;
+        }
+        if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
+            return true;
+        }
+        if (oldWindow.childTokens != null && newWindow.childTokens != null
+                && !oldWindow.childTokens.equals(newWindow.childTokens)) {
+            return true;
+        }
+        if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
+            return true;
+        }
+        if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
+            return true;
+        }
+        if (oldWindow.inPictureInPicture != newWindow.inPictureInPicture) {
+            return true;
+        }
+        if (oldWindow.hasFlagWatchOutsideTouch != newWindow.hasFlagWatchOutsideTouch) {
+            return true;
+        }
+        if (oldWindow.displayId != newWindow.displayId) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Start tracking windows changes from window manager.
      */
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f451a09..4222fc1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -272,6 +272,7 @@
 import android.os.storage.StorageManager;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.provider.DeviceConfig.Properties;
 import android.server.ServerProtoEnums;
 import android.sysprop.VoldProperties;
 import android.text.TextUtils;
@@ -356,6 +357,7 @@
 import com.android.server.utils.PriorityDump;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.ActivityMetricsLaunchObserver;
 import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.ActivityTaskManagerService;
@@ -392,6 +394,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiFunction;
 
@@ -861,6 +864,51 @@
      */
     final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
 
+    /**
+     * Depth of overlapping activity-start PSS deferral notes
+     */
+    private final AtomicInteger mActivityStartingNesting = new AtomicInteger(0);
+
+    private final ActivityMetricsLaunchObserver mActivityLaunchObserver =
+            new ActivityMetricsLaunchObserver() {
+        @Override
+        public void onActivityLaunched(byte[] activity, int temperature) {
+            // This is safe to force to the head of the queue because it relies only
+            // on refcounting to track begin/end of deferrals, not on actual
+            // message ordering.  We don't care *what* activity is being
+            // launched; only that we're doing so.
+            if (mPssDeferralTime > 0) {
+                final Message msg = mBgHandler.obtainMessage(DEFER_PSS_MSG);
+                mBgHandler.sendMessageAtFrontOfQueue(msg);
+            }
+        }
+
+        // The other observer methods are unused
+        @Override
+        public void onIntentStarted(Intent intent) {
+        }
+
+        @Override
+        public void onIntentFailed() {
+        }
+
+        @Override
+        public void onActivityLaunchCancelled(byte[] abortingActivity) {
+        }
+
+        @Override
+        public void onActivityLaunchFinished(byte[] finalActivity) {
+        }
+    };
+
+    /**
+     * How long we defer PSS gathering while activities are starting, in milliseconds.
+     * This is adjustable via DeviceConfig.  If it is zero or negative, no PSS deferral
+     * is done.
+     */
+    private volatile long mPssDeferralTime = 0;
+    private static final String ACTIVITY_START_PSS_DEFER_CONFIG = "activity_start_pss_defer";
+
     private boolean mBinderTransactionTrackingEnabled = false;
 
     /**
@@ -874,6 +922,20 @@
      */
     boolean mFullPssPending = false;
 
+    /**
+     * Observe DeviceConfig changes to the PSS calculation interval
+     */
+    private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener =
+            new DeviceConfig.OnPropertiesChangedListener() {
+                @Override
+                public void onPropertiesChanged(Properties properties) {
+                    mPssDeferralTime = properties.getLong(ACTIVITY_START_PSS_DEFER_CONFIG, 0);
+                    if (DEBUG_PSS) {
+                        Slog.d(TAG_PSS, "Activity-start PSS delay now "
+                                + mPssDeferralTime + " ms");
+                    }
+                }
+            };
 
     /**
      * This is for verifying the UID report flow.
@@ -1838,6 +1900,8 @@
     }
 
     static final int COLLECT_PSS_BG_MSG = 1;
+    static final int DEFER_PSS_MSG = 2;
+    static final int STOP_DEFERRING_PSS_MSG = 3;
 
     final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
         @Override
@@ -1945,6 +2009,30 @@
                     }
                 } while (true);
             }
+
+            case DEFER_PSS_MSG: {
+                deferPssForActivityStart();
+            } break;
+
+            case STOP_DEFERRING_PSS_MSG: {
+                final int nesting = mActivityStartingNesting.decrementAndGet();
+                if (nesting <= 0) {
+                    if (DEBUG_PSS) {
+                        Slog.d(TAG_PSS, "PSS activity start deferral interval ended; now "
+                                + nesting);
+                    }
+                    if (nesting < 0) {
+                        Slog.wtf(TAG, "Activity start nesting undercount!");
+                        mActivityStartingNesting.incrementAndGet();
+                    }
+                } else {
+                    if (DEBUG_PSS) {
+                        Slog.d(TAG_PSS, "Still deferring PSS, nesting=" + nesting);
+                    }
+                }
+            }
+            break;
+
             }
         }
     };
@@ -8832,6 +8920,12 @@
                 NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
         mHiddenApiBlacklist.registerObserver();
 
+        final long pssDeferralMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                ACTIVITY_START_PSS_DEFER_CONFIG, 0L);
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                ActivityThread.currentApplication().getMainExecutor(),
+                mPssDelayConfigListener);
+
         synchronized (this) {
             mDebugApp = mOrigDebugApp = debugApp;
             mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
@@ -8848,6 +8942,7 @@
                     com.android.internal.R.bool.config_multiuserDelayUserDataLocking);
 
             mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
+            mPssDeferralTime = pssDeferralMs;
         }
     }
 
@@ -8921,6 +9016,10 @@
         mAtmInternal.updateTopComponentForFactoryTest();
         t.traceEnd();
 
+        t.traceBegin("registerActivityLaunchObserver");
+        mAtmInternal.getLaunchObserverRegistry().registerLaunchObserver(mActivityLaunchObserver);
+        t.traceEnd();
+
         t.traceBegin("watchDeviceProvisioning");
         watchDeviceProvisioning(mContext);
         t.traceEnd();
@@ -16177,7 +16276,13 @@
             return false;
         }
         if (mPendingPssProcesses.size() == 0) {
-            mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+            final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0)
+                    ? mPssDeferralTime : 0;
+            if (DEBUG_PSS && deferral > 0) {
+                Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by "
+                        + deferral + " ms");
+            }
+            mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, deferral);
         }
         if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc);
         proc.pssProcState = procState;
@@ -16187,6 +16292,30 @@
     }
 
     /**
+     * Re-defer a posted PSS collection pass, if one exists.  Assumes deferral is
+     * currently active policy when called.
+     */
+    private void deferPssIfNeededLocked() {
+        if (mPendingPssProcesses.size() > 0) {
+            mBgHandler.removeMessages(COLLECT_PSS_BG_MSG);
+            mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, mPssDeferralTime);
+        }
+    }
+
+    private void deferPssForActivityStart() {
+        synchronized (ActivityManagerService.this) {
+            if (mPssDeferralTime > 0) {
+                if (DEBUG_PSS) {
+                    Slog.d(TAG_PSS, "Deferring PSS collection for activity start");
+                }
+                deferPssIfNeededLocked();
+                mActivityStartingNesting.getAndIncrement();
+                mBgHandler.sendEmptyMessageDelayed(STOP_DEFERRING_PSS_MSG, mPssDeferralTime);
+            }
+        }
+    }
+
+    /**
      * Schedule PSS collection of all processes.
      */
     void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 36d9c0e..7be1b8a 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -634,13 +634,8 @@
         if (mSurfaceControl != null) {
             mSurfaceLayout.dispose();
             mSurfaceLayout = null;
-            SurfaceControl.openTransaction();
-            try {
-                mSurfaceControl.remove();
-                mSurface.release();
-            } finally {
-                SurfaceControl.closeTransaction();
-            }
+            new Transaction().remove(mSurfaceControl).apply();
+            mSurface.release();
             mSurfaceControl = null;
             mSurfaceVisible = false;
             mSurfaceAlpha = 0f;
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 7b45a1b..92ac1d4 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -17,6 +17,7 @@
 package com.android.server.notification;
 
 import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -686,6 +687,11 @@
                     }
                 }
 
+                if (existing.getOriginalImportance() == IMPORTANCE_UNSPECIFIED) {
+                    existing.setOriginalImportance(channel.getImportance());
+                    needsPolicyFileChange = true;
+                }
+
                 updateConfig();
                 return needsPolicyFileChange;
             }
@@ -719,7 +725,7 @@
             if (!r.showBadge) {
                 channel.setShowBadge(false);
             }
-
+            channel.setOriginalImportance(channel.getImportance());
             r.channels.put(channel.getId(), channel);
             if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
                 updateChannelsBypassingDnd(mContext.getUserId());
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 6b7187e..bace7e3 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -43,9 +43,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.text.TextUtils;
 import android.util.ArraySet;
-import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedValue;
@@ -907,7 +905,7 @@
                 }
 
                 public void releaseSurface() {
-                    mSurfaceControl.remove();
+                    mService.mTransactionFactory.make().remove(mSurfaceControl).apply();
                     mSurface.release();
                 }
 
@@ -1036,12 +1034,9 @@
 
         private static final boolean DEBUG = false;
 
-        private final SparseArray<WindowState> mTempWindowStates =
-                new SparseArray<WindowState>();
+        private final SparseArray<WindowState> mTempWindowStates = new SparseArray<>();
 
-        private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
-
-        private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
+        private final Set<IBinder> mTempBinderSet = new ArraySet<>();
 
         private final RectF mTempRectF = new RectF();
 
@@ -1098,8 +1093,7 @@
                 Slog.i(LOG_TAG, "computeChangedWindows()");
             }
 
-            boolean windowsChanged = false;
-            List<WindowInfo> windows = new ArrayList<WindowInfo>();
+            List<WindowInfo> windows = new ArrayList<>();
 
             synchronized (mService.mGlobalLock) {
                 // Do not send the windows if there is no current focus as
@@ -1169,46 +1163,9 @@
 
                 visibleWindows.clear();
                 addedWindows.clear();
-
-                if (!forceSend) {
-                    // We computed the windows and if they changed notify the client.
-                    if (mOldWindows.size() != windows.size()) {
-                        // Different size means something changed.
-                        windowsChanged = true;
-                    } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
-                        // Since we always traverse windows from high to low layer
-                        // the old and new windows at the same index should be the
-                        // same, otherwise something changed.
-                        for (int i = 0; i < windowCount; i++) {
-                            WindowInfo oldWindow = mOldWindows.get(i);
-                            WindowInfo newWindow = windows.get(i);
-                            // We do not care for layer changes given the window
-                            // order does not change. This brings no new information
-                            // to the clients.
-                            if (windowChangedNoLayer(oldWindow, newWindow)) {
-                                windowsChanged = true;
-                                break;
-                            }
-                        }
-                    }
-                }
-
-                if (forceSend || windowsChanged) {
-                    cacheWindows(windows);
-                }
             }
 
-            // Now we do not hold the lock, so send the windows over.
-            if (forceSend || windowsChanged) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "Windows changed or force sending:" + windows);
-                }
-                mCallback.onWindowsForAccessibilityChanged(windows);
-            } else {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "No windows changed.");
-                }
-            }
+            mCallback.onWindowsForAccessibilityChanged(forceSend, windows);
 
             // Recycle the windows as we do not need them.
             clearAndRecycleWindows(windows);
@@ -1313,67 +1270,6 @@
             tokenOut.add(window.token);
         }
 
-        private void cacheWindows(List<WindowInfo> windows) {
-            final int oldWindowCount = mOldWindows.size();
-            for (int i = oldWindowCount - 1; i >= 0; i--) {
-                mOldWindows.remove(i).recycle();
-            }
-            final int newWindowCount = windows.size();
-            for (int i = 0; i < newWindowCount; i++) {
-                WindowInfo newWindow = windows.get(i);
-                mOldWindows.add(WindowInfo.obtain(newWindow));
-            }
-        }
-
-        private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
-            if (oldWindow == newWindow) {
-                return false;
-            }
-            if (oldWindow == null) {
-                return true;
-            }
-            if (newWindow == null) {
-                return true;
-            }
-            if (oldWindow.type != newWindow.type) {
-                return true;
-            }
-            if (oldWindow.focused != newWindow.focused) {
-                return true;
-            }
-            if (oldWindow.token == null) {
-                if (newWindow.token != null) {
-                    return true;
-                }
-            } else if (!oldWindow.token.equals(newWindow.token)) {
-                return true;
-            }
-            if (oldWindow.parentToken == null) {
-                if (newWindow.parentToken != null) {
-                    return true;
-                }
-            } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
-                return true;
-            }
-            if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
-                return true;
-            }
-            if (oldWindow.childTokens != null && newWindow.childTokens != null
-                    && !oldWindow.childTokens.equals(newWindow.childTokens)) {
-                return true;
-            }
-            if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
-                return true;
-            }
-            if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
-                return true;
-            }
-            if (oldWindow.displayId != newWindow.displayId) {
-                return true;
-            }
-            return false;
-        }
-
         private static void clearAndRecycleWindows(List<WindowInfo> windows) {
             final int windowCount = windows.size();
             for (int i = windowCount - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 84ba5ca9..7fc17e1 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -97,6 +97,7 @@
     final BlackSurface[] mBlackSurfaces = new BlackSurface[4];
 
     final boolean mForceDefaultOrientation;
+    private final TransactionFactory mTransactionFactory;
 
     public void printTo(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("Outer: "); mOuterRect.printShortString(pw);
@@ -111,11 +112,12 @@
         }
     }
 
-    public BlackFrame(SurfaceControl.Transaction t,
-            Rect outer, Rect inner, int layer, DisplayContent dc,
-            boolean forceDefaultOrientation) throws OutOfResourcesException {
+    public BlackFrame(TransactionFactory factory, SurfaceControl.Transaction t, Rect outer,
+            Rect inner, int layer, DisplayContent dc, boolean forceDefaultOrientation)
+            throws OutOfResourcesException {
         boolean success = false;
 
+        mTransactionFactory = factory;
         mForceDefaultOrientation = forceDefaultOrientation;
 
         // TODO: Why do we use 4 surfaces instead of just one big one behind the screenshot?
@@ -149,14 +151,16 @@
 
     public void kill() {
         if (mBlackSurfaces != null) {
+            SurfaceControl.Transaction t = mTransactionFactory.make();
             for (int i=0; i<mBlackSurfaces.length; i++) {
                 if (mBlackSurfaces[i] != null) {
                     if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
                             "  BLACK " + mBlackSurfaces[i].surface + ": DESTROY");
-                    mBlackSurfaces[i].surface.remove();
+                    t.remove(mBlackSurfaces[i].surface);
                     mBlackSurfaces[i] = null;
                 }
             }
+            t.apply();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4dcb58a..57ed92d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4554,13 +4554,15 @@
                         .show(mSplitScreenDividerAnchor);
                 scheduleAnimation();
             } else {
-                mAppAnimationLayer.remove();
+                mWmService.mTransactionFactory.make()
+                        .remove(mAppAnimationLayer)
+                        .remove(mBoostedAppAnimationLayer)
+                        .remove(mHomeAppAnimationLayer)
+                        .remove(mSplitScreenDividerAnchor)
+                        .apply();
                 mAppAnimationLayer = null;
-                mBoostedAppAnimationLayer.remove();
                 mBoostedAppAnimationLayer = null;
-                mHomeAppAnimationLayer.remove();
                 mHomeAppAnimationLayer = null;
-                mSplitScreenDividerAnchor.remove();
                 mSplitScreenDividerAnchor = null;
             }
         }
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index c3ea72f..bb035d5 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -261,7 +261,7 @@
 
         public void remove() {
             if (mSurface != null) {
-                mSurface.remove();
+                new SurfaceControl.Transaction().remove(mSurface).apply();
                 mSurface = null;
             }
             if (mInputInterceptor != null) {
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index b90d602..fe4c9a4 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -557,7 +557,7 @@
                 Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
                         mOriginalWidth*2, mOriginalHeight*2);
                 Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
-                mCustomBlackFrame = new BlackFrame(t, outer, inner,
+                mCustomBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
                         SCREEN_FREEZE_LAYER_CUSTOM, mDisplayContent, false);
                 mCustomBlackFrame.setMatrix(t, mFrameInitialMatrix);
             } catch (OutOfResourcesException e) {
@@ -588,7 +588,7 @@
                             mOriginalWidth*2, mOriginalHeight*2);
                     inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
                 }
-                mExitingBlackFrame = new BlackFrame(t, outer, inner,
+                mExitingBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
                         SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
                 mExitingBlackFrame.setMatrix(t, mFrameInitialMatrix);
             } catch (OutOfResourcesException e) {
@@ -601,7 +601,7 @@
                 Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
                         finalWidth*2, finalHeight*2);
                 Rect inner = new Rect(0, 0, finalWidth, finalHeight);
-                mEnteringBlackFrame = new BlackFrame(t, outer, inner,
+                mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
                         SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
@@ -639,7 +639,7 @@
             if (SHOW_TRANSACTIONS ||
                     SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
                             "  FREEZE " + mSurfaceControl + ": DESTROY");
-            mSurfaceControl.remove();
+            mService.mTransactionFactory.make().remove(mSurfaceControl).apply();
             mSurfaceControl = null;
         }
         if (mCustomBlackFrame != null) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 0798c59..ddf25e6 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1007,7 +1007,7 @@
         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
 
         if (mAnimationBackgroundSurface != null) {
-            mAnimationBackgroundSurface.remove();
+            mWmService.mTransactionFactory.make().remove(mAnimationBackgroundSurface).apply();
             mAnimationBackgroundSurface = null;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 40bec14..6910ce9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -51,9 +51,10 @@
         /**
          * Called when the windows for accessibility changed.
          *
+         * @param forceSend Send the windows for accessibility even if they haven't changed.
          * @param windows The windows for accessibility.
          */
-        public void onWindowsForAccessibilityChanged(List<WindowInfo> windows);
+        void onWindowsForAccessibilityChanged(boolean forceSend, List<WindowInfo> windows);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 264eeda..2bf24f6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7694,7 +7694,7 @@
 
     private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
         final WindowState touchedWindow = windowForClientLocked(null, touchedToken, false);
-        if (touchedWindow == null) {
+        if (touchedWindow == null || !touchedWindow.canReceiveKeys()) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 836460f..a616e06 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -161,7 +161,7 @@
         }
         try {
             if (mSurfaceControl != null) {
-                mSurfaceControl.remove();
+                mTmpTransaction.remove(mSurfaceControl).apply();
             }
         } catch (RuntimeException e) {
             Slog.w(TAG, "Error destroying surface in: " + this, e);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 04ca40e..22408cc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -70,6 +70,8 @@
  */
 public class AccessibilityWindowManagerTest {
     private static final String PACKAGE_NAME = "com.android.server.accessibility";
+    private static final boolean FORCE_SEND = true;
+    private static final boolean SEND_ON_WINDOW_CHANGES = false;
     private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM;
     private static final int NUM_GLOBAL_WINDOWS = 4;
     private static final int NUM_APP_WINDOWS = 4;
@@ -122,7 +124,7 @@
         mWindowInfos.get(DEFAULT_FOCUSED_INDEX).focused = true;
         // Turn on windows tracking, and update window info
         mA11yWindowManager.startTrackingWindows();
-        mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+        mA11yWindowManager.onWindowsForAccessibilityChanged(FORCE_SEND, mWindowInfos);
         assertEquals(mA11yWindowManager.getWindowListLocked().size(),
                 mWindowInfos.size());
 
@@ -169,16 +171,16 @@
     @Test
     public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() {
         final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
-        WindowInfo focuedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX);
+        WindowInfo focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX);
         assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked(
-                USER_SYSTEM_ID, focuedWindowInfo.token));
+                USER_SYSTEM_ID, focusedWindowInfo.token));
 
-        focuedWindowInfo.focused = false;
-        focuedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX + 1);
-        focuedWindowInfo.focused = true;
+        focusedWindowInfo.focused = false;
+        focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX + 1);
+        focusedWindowInfo.focused = true;
 
         mA11yWindowManager.onTouchInteractionStart();
-        mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
         assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
     }
 
@@ -208,6 +210,52 @@
     }
 
     @Test
+    public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
+        final WindowInfo windowInfo = mWindowInfos.get(0);
+        final int correctLayer = mA11yWindowManager.getWindowListLocked().get(0).getLayer();
+        windowInfo.layer += 1;
+
+        mA11yWindowManager.onWindowsForAccessibilityChanged(FORCE_SEND, mWindowInfos);
+        assertNotEquals(correctLayer, mA11yWindowManager.getWindowListLocked().get(0).getLayer());
+    }
+
+    @Test
+    public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() {
+        final WindowInfo windowInfo = mWindowInfos.get(0);
+        final int correctLayer = mA11yWindowManager.getWindowListLocked().get(0).getLayer();
+        windowInfo.layer += 1;
+
+        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+        assertEquals(correctLayer, mA11yWindowManager.getWindowListLocked().get(0).getLayer());
+    }
+
+    @Test
+    public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows()
+            throws RemoteException {
+        final AccessibilityWindowInfo oldWindow = mA11yWindowManager.getWindowListLocked().get(0);
+        final IWindow token = addAccessibilityInteractionConnection(true);
+        final WindowInfo windowInfo = WindowInfo.obtain();
+        windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
+        windowInfo.token = token.asBinder();
+        windowInfo.layer = 0;
+        windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+        mWindowInfos.set(0, windowInfo);
+
+        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+        assertNotEquals(oldWindow, mA11yWindowManager.getWindowListLocked().get(0));
+    }
+
+    @Test
+    public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() {
+        final WindowInfo focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX);
+        final WindowInfo windowInfo = mWindowInfos.get(0);
+        focusedWindowInfo.focused = false;
+        windowInfo.focused = true;
+        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+        assertTrue(mA11yWindowManager.getWindowListLocked().get(0).isFocused());
+    }
+
+    @Test
     public void removeAccessibilityInteractionConnection_byWindowToken_shouldRemoved() {
         for (int i = 0; i < NUM_OF_WINDOWS; i++) {
             final int windowId = mA11yWindowTokens.keyAt(i);
@@ -264,7 +312,7 @@
         windowInfo = mWindowInfos.get(1);
         windowInfo.boundsInScreen.set(0, SCREEN_HEIGHT / 2,
                 SCREEN_WIDTH, SCREEN_HEIGHT);
-        mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
 
         final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
         final Region outBounds = new Region();
@@ -291,7 +339,7 @@
         windowInfo = mWindowInfos.get(1);
         windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
 
-        mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
         final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
         final Region outBounds = new Region();
         int windowId = a11yWindows.get(1).getId();
@@ -309,7 +357,7 @@
         windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
         windowInfo = mWindowInfos.get(1);
         windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
-        mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
 
         final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
         final Region outBounds = new Region();
@@ -498,7 +546,7 @@
     public void getPictureInPictureWindow_shouldNotNull() {
         assertNull(mA11yWindowManager.getPictureInPictureWindow());
         mWindowInfos.get(1).inPictureInPicture = true;
-        mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
 
         assertNotNull(mA11yWindowManager.getPictureInPictureWindow());
     }
@@ -511,7 +559,7 @@
                 mA11yWindowManager.getConnectionLocked(
                         USER_SYSTEM_ID, outsideWindowId).getRemote();
         mWindowInfos.get(0).hasFlagWatchOutsideTouch = true;
-        mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos);
+        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
 
         mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId);
         verify(mockRemoteConnection).notifyOutsideTouch();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
deleted file mode 100644
index 1408749..0000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNull;
-
-import android.app.NotificationChannel;
-import android.net.Uri;
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Xml;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.FastXmlSerializer;
-import com.android.server.UiServiceTestCase;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class NotificationChannelTest extends UiServiceTestCase {
-
-    @Test
-    public void testWriteToParcel() {
-        NotificationChannel channel =
-                new NotificationChannel("1", "one", IMPORTANCE_DEFAULT);
-        Parcel parcel = Parcel.obtain();
-        channel.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        NotificationChannel channel1 = NotificationChannel.CREATOR.createFromParcel(parcel);
-        assertEquals(channel, channel1);
-    }
-
-    @Test
-    public void testSystemBlockable() {
-        NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
-        assertEquals(false, channel.isBlockableSystem());
-        channel.setBlockableSystem(true);
-        assertEquals(true, channel.isBlockableSystem());
-    }
-
-    @Test
-    public void testEmptyVibration_noException() throws Exception {
-        NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
-        channel.setVibrationPattern(new long[0]);
-
-        XmlSerializer serializer = new FastXmlSerializer();
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
-        channel.writeXml(serializer);
-    }
-
-    @Test
-    public void testBackupEmptySound() throws Exception {
-        NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
-        channel.setSound(Uri.EMPTY, null);
-
-        XmlSerializer serializer = new FastXmlSerializer();
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
-        channel.writeXmlForBackup(serializer, getContext());
-
-        XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(new BufferedInputStream(
-                new ByteArrayInputStream(baos.toByteArray())), null);
-        NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
-        restored.populateFromXmlForRestore(parser, getContext());
-
-        assertNull(restored.getSound());
-    }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 1ed1639..9ecf198 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -131,6 +131,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.Xml;
 
 import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
@@ -138,6 +139,7 @@
 import com.android.internal.R;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.internal.util.FastXmlSerializer;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.UiServiceTestCase;
@@ -156,9 +158,13 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.stubbing.Answer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.util.ArrayList;
@@ -3060,6 +3066,25 @@
     }
 
     @Test
+    public void testBackupEmptySound() throws Exception {
+        NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+        channel.setSound(Uri.EMPTY, null);
+
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        channel.writeXmlForBackup(serializer, getContext());
+
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+        restored.populateFromXmlForRestore(parser, getContext());
+
+        assertNull(restored.getSound());
+    }
+
+    @Test
     public void testBackup() throws Exception {
         int systemChecks = mService.countSystemChecks;
         mBinderService.getBackupPayload(1);
@@ -3067,6 +3092,17 @@
     }
 
     @Test
+    public void testEmptyVibration_noException() throws Exception {
+        NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+        channel.setVibrationPattern(new long[0]);
+
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        channel.writeXml(serializer);
+    }
+
+    @Test
     public void updateUriPermissions_update() throws Exception {
         NotificationChannel c = new NotificationChannel(
                 TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 292a05b..a98f79c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -169,7 +169,7 @@
 
         mDimmer.updateDims(mTransaction, new Rect());
         verify(mTransaction).show(getDimLayer());
-        verify(dimLayer, never()).remove();
+        verify(mTransaction, never()).remove(dimLayer);
     }
 
     @Test
@@ -231,7 +231,7 @@
 
         mDimmer.updateDims(mTransaction, new Rect());
         verify(mTransaction).show(dimLayer);
-        verify(dimLayer, never()).remove();
+        verify(mTransaction, never()).remove(dimLayer);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index b93c994..acfc2ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -114,16 +114,12 @@
     @Test
     public void testAddChildSetsSurfacePosition() {
         try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) {
-
-            final SurfaceControl.Transaction transaction = mock(SurfaceControl.Transaction.class);
-            mWm.mTransactionFactory = () -> transaction;
-
             WindowContainer child = new WindowContainer(mWm);
             child.setBounds(1, 1, 10, 10);
 
-            verify(transaction, never()).setPosition(any(), anyFloat(), anyFloat());
+            verify(mTransaction, never()).setPosition(any(), anyFloat(), anyFloat());
             top.addChild(child, 0);
-            verify(transaction, times(1)).setPosition(any(), eq(1.f), eq(1.f));
+            verify(mTransaction, times(1)).setPosition(any(), eq(1.f), eq(1.f));
         }
     }
 
diff --git a/startop/scripts/app_startup/run_app_with_prefetch b/startop/scripts/app_startup/run_app_with_prefetch
index 643df1b..92a31c3 100755
--- a/startop/scripts/app_startup/run_app_with_prefetch
+++ b/startop/scripts/app_startup/run_app_with_prefetch
@@ -101,6 +101,15 @@
     esac
     shift
   done
+
+  if [[ $when == "aot" ]]; then
+    # TODO: re-implement aot later for experimenting.
+    echo "Error: --when $when is unsupported" >&2
+    exit 1
+  elif [[ $when != "jit" ]]; then
+    echo "Error: --when must be one of (aot jit)." >&2
+    exit 1
+  fi
 }
 
 echo_to_output_file() {
@@ -212,6 +221,12 @@
   local the_when="$1" # user: aot, jit
   local the_mode="$2" # warm, cold, fadvise, mlock, etc.
 
+  # iorapd readahead for jit+(mlock/fadvise)
+  if [[ $the_when == "jit" && $the_mode != 'warm' && $the_mode != 'cold' ]]; then
+    iorapd_readahead_enable
+    return 0
+  fi
+
   if [[ $the_when != "aot" ]]; then
     # TODO: just in time implementation.. should probably use system server.
     return 0
@@ -250,13 +265,18 @@
   local the_when="$1" # user: aot, jit
   local the_mode="$2" # warm, cold, fadvise, mlock, etc.
   local logcat_timestamp="$3"  # timestamp from before am start.
+  local res
 
   if [[ $the_when != "aot" ]]; then
     if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then
       # Validate that readahead completes.
       # If this fails for some reason, then this will also discard the timing of the run.
       iorapd_readahead_wait_until_finished "$package" "$activity" "$logcat_timestamp" "$timeout"
-      return $?
+      res=$?
+
+      iorapd_readahead_disable
+
+      return $res
     fi
     # Don't need to do anything for warm or cold.
     return 0
diff --git a/startop/scripts/iorap/collector b/startop/scripts/iorap/collector
index d96125f..3dc080a 100755
--- a/startop/scripts/iorap/collector
+++ b/startop/scripts/iorap/collector
@@ -322,6 +322,7 @@
   iorapd_compiler_purge_trace_file "$package" "$activity" || return $?
 
   iorapd_perfetto_enable || return $?
+  iorapd_readahead_disable || return $?
   iorapd_start || return $?
 
   # Wait for perfetto trace to finished writing itself out.
diff --git a/startop/scripts/iorap/common b/startop/scripts/iorap/common
index cb2b618..031dabf 100755
--- a/startop/scripts/iorap/common
+++ b/startop/scripts/iorap/common
@@ -45,7 +45,7 @@
   iorapd_reset # iorapd only reads this flag when initializing
 }
 
-# Enable perfetto tracing.
+# Disable perfetto tracing.
 # Subsequent launches of applications will no longer record perfetto trace protobufs.
 iorapd_perfetto_disable() {
   verbose_print 'disable perfetto'
@@ -53,6 +53,31 @@
   iorapd_reset # iorapd only reads this flag when initializing
 }
 
+# Enable readahead
+# Subsequent launches of an application will be sped up by iorapd readahead prefetching
+# (Provided an appropriate compiled trace exists for that application)
+iorapd_readahead_enable() {
+  if [[ "$(adb shell getprop iorapd.readahead.enable)" == true ]]; then
+    verbose_print 'enable readahead [already enabled]'
+    return 0
+  fi
+  verbose_print 'enable readahead [reset iorapd]'
+  adb shell setprop iorapd.readahead.enable true
+  iorapd_reset # iorapd only reads this flag when initializing
+}
+
+# Disable readahead
+# Subsequent launches of an application will be not be sped up by iorapd readahead prefetching.
+iorapd_readahead_disable() {
+  if [[ "$(adb shell getprop iorapd.readahead.enable)" == false ]]; then
+    verbose_print 'disable readahead [already disabled]'
+    return 0
+  fi
+  verbose_print 'disable readahead [reset iorapd]'
+  adb shell setprop iorapd.readahead.enable false
+  iorapd_reset # iorapd only reads this flag when initializing
+}
+
 _iorapd_path_to_data_file() {
   local package="$1"
   local activity="$2"