Merge "Copy rects such that we don't modify state" into qt-dev
diff --git a/api/current.txt b/api/current.txt
index eb244e4..8ec7594 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -41758,7 +41758,7 @@
     method public int getDisabledShowContext();
     method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
     method public android.os.IBinder onBind(android.content.Intent);
-    method @Nullable public java.util.Set<java.lang.String> onGetSupportedVoiceActions(@NonNull java.util.Set<java.lang.String>);
+    method @NonNull public java.util.Set<java.lang.String> onGetSupportedVoiceActions(@NonNull java.util.Set<java.lang.String>);
     method public void onLaunchVoiceAssistFromKeyguard();
     method public void onReady();
     method public void onShutdown();
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index e3e63e5..0de17ca 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -17,7 +17,6 @@
 package android.service.voice;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
@@ -41,6 +40,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
@@ -211,11 +211,11 @@
      *
      * @param voiceActions A set of checked voice actions.
      * @return Returns a subset of checked voice actions. Additional voice actions in the
-     * returned set will be ignored. Returns null or empty set if no actions are supported.
+     * returned set will be ignored. Returns empty set if no actions are supported.
      */
-    @Nullable
+    @NonNull
     public Set<String> onGetSupportedVoiceActions(@NonNull Set<String> voiceActions) {
-        return null;
+        return Collections.emptySet();
     }
 
     @Override
@@ -272,7 +272,7 @@
             try {
                 Set<String> voiceActionsSet = new ArraySet<>(voiceActions);
                 Set<String> resultSet = onGetSupportedVoiceActions(voiceActionsSet);
-                callback.onComplete(resultSet == null ? null : new ArrayList<>(resultSet));
+                callback.onComplete(new ArrayList<>(resultSet));
             } catch (RemoteException e) {
             }
         }
diff --git a/core/jni/android_nio_utils.cpp b/core/jni/android_nio_utils.cpp
index 19a1c72..a62dd7c 100644
--- a/core/jni/android_nio_utils.cpp
+++ b/core/jni/android_nio_utils.cpp
@@ -18,42 +18,51 @@
 
 #include "core_jni_helpers.h"
 
-void* android::nio_getPointer(JNIEnv *_env, jobject buffer, jarray *array) {
-    assert(array);
+namespace {
 
+void* getPointer(JNIEnv *_env, jobject buffer, jarray *array, void** elements) {
+    assert(array);
     jint position;
     jint limit;
     jint elementSizeShift;
     jlong pointer = jniGetNioBufferFields(_env, buffer, &position, &limit, &elementSizeShift);
     if (pointer != 0L) {
+        *array = nullptr;
+        *elements = nullptr;
         pointer += position << elementSizeShift;
         return reinterpret_cast<void*>(pointer);
     }
-
     jint offset = jniGetNioBufferBaseArrayOffset(_env, buffer);
     *array = jniGetNioBufferBaseArray(_env, buffer);
-    void * data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    return reinterpret_cast<void*>(reinterpret_cast<char*>(data) + offset);
+    *elements = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
+    return reinterpret_cast<void*>(reinterpret_cast<char*>(*elements) + offset);
 }
 
+void releasePointer(JNIEnv *_env, jarray array, void *elements, jboolean commit) {
+    _env->ReleasePrimitiveArrayCritical(array, elements, commit ? 0 : JNI_ABORT);
+}
 
-void android::nio_releasePointer(JNIEnv *_env, jarray array, void *data,
-                                jboolean commit) {
-    _env->ReleasePrimitiveArrayCritical(array, data,
-                                        commit ? 0 : JNI_ABORT);
+}  // namespace
+
+void* android::nio_getPointer(JNIEnv *_env, jobject buffer, jarray *array) {
+    void* elements;
+    return getPointer(_env, buffer, array, &elements);
+}
+
+void android::nio_releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) {
+    releasePointer(_env, array, data, commit);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-android::AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer,
-                                              jboolean commit) {
+android::AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit) {
     fEnv = env;
     fCommit = commit;
-    fPointer = android::nio_getPointer(env, nioBuffer, &fArray);
+    fPointer = getPointer(env, nioBuffer, &fArray, &fElements);
 }
 
 android::AutoBufferPointer::~AutoBufferPointer() {
-    if (NULL != fArray) {
-        android::nio_releasePointer(fEnv, fArray, fPointer, fCommit);
+    if (nullptr != fArray) {
+        releasePointer(fEnv, fArray, fElements, fCommit);
     }
 }
diff --git a/core/jni/android_nio_utils.h b/core/jni/android_nio_utils.h
index c634cb91..7c9acd2 100644
--- a/core/jni/android_nio_utils.h
+++ b/core/jni/android_nio_utils.h
@@ -20,7 +20,7 @@
 #include <android_runtime/AndroidRuntime.h>
 
 namespace android {
-    
+
 /**
  * Given an nio.Buffer, return a pointer to it, beginning at its current
  * position. The returned pointer is only valid for the current JNI stack-frame.
@@ -63,9 +63,10 @@
 
 private:
     JNIEnv* fEnv;
-    void*   fPointer;
-    jarray  fArray;
-    jboolean fCommit;
+    void*   fPointer;   // pointer to current buffer position.
+    void*   fElements;  // pointer to array element 0 (may be directly in fArray or a copy).
+    jarray  fArray;     // pointer to array on managed heap.
+    jboolean fCommit;   // commit data to source if required (when fElements is a copy of fArray).
 };
 
 }   /* namespace android */
diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java
index d2a1dd9..4bee243 100644
--- a/core/tests/coretests/src/android/graphics/BitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapTest.java
@@ -22,6 +22,8 @@
 
 import junit.framework.TestCase;
 
+import java.nio.ByteBuffer;
+
 public class BitmapTest extends TestCase {
 
     @SmallTest
@@ -262,4 +264,74 @@
         assertFalse(hardwareBitmap.isMutable());
         assertEquals(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), hardwareBitmap.getColorSpace());
     }
+
+    @SmallTest
+    public void testCopyWithDirectBuffer() {
+        // Initialize Bitmap
+        final int width = 2;
+        final int height = 2;
+        Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+        bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
+
+        // Copy bytes to direct buffer, buffer is padded by fixed amount (pad bytes) either side
+        // of bitmap.
+        final int pad = 1;
+        final byte padValue = 0x5a;
+        final int bufferSize = pad + width * height * 2 + pad;
+        ByteBuffer directBuffer = ByteBuffer.allocateDirect(bufferSize);
+
+        // Write padding
+        directBuffer.put(0, padValue);
+        directBuffer.put(directBuffer.limit() - 1, padValue);
+
+        // Copy bitmap
+        directBuffer.position(pad);
+        bm1.copyPixelsToBuffer(directBuffer);
+        assertEquals(directBuffer.position(), pad + width * height * 2);
+
+        // Check padding
+        assertEquals(directBuffer.get(0), padValue);
+        assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue);
+
+        // Create bitmap from direct buffer and check match.
+        directBuffer.position(pad);
+        Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+        bm2.copyPixelsFromBuffer(directBuffer);
+        assertTrue(bm2.sameAs(bm1));
+    }
+
+    @SmallTest
+    public void testCopyWithHeapBuffer() {
+        // Initialize Bitmap
+        final int width = 2;
+        final int height = 2;
+        Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+        bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
+
+        // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side
+        // of bitmap.
+        final int pad = 1;
+        final byte padValue = 0x5a;
+        final int bufferSize = pad + width * height * 2 + pad;
+        ByteBuffer heapBuffer = ByteBuffer.allocate(bufferSize);
+
+        // Write padding
+        heapBuffer.put(0, padValue);
+        heapBuffer.put(heapBuffer.limit() - 1, padValue);
+
+        // Copy bitmap
+        heapBuffer.position(pad);
+        bm1.copyPixelsToBuffer(heapBuffer);
+        assertEquals(heapBuffer.position(), pad + width * height * 2);
+
+        // Check padding
+        assertEquals(heapBuffer.get(0), padValue);
+        assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue);
+
+        // Create bitmap from heap buffer and check match.
+        heapBuffer.position(pad);
+        Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+        bm2.copyPixelsFromBuffer(heapBuffer);
+        assertTrue(bm2.sameAs(bm1));
+    }
 }
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index ebbbdec..bdd3038 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -39,11 +39,13 @@
 import android.util.SparseArray;
 import android.view.SurfaceControl.Transaction;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.test.InsetsModeSession;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -62,7 +64,6 @@
  * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
  */
 @Presubmit
-@FlakyTest(detail = "Promote once confirmed non-flaky")
 @RunWith(AndroidJUnit4.class)
 public class InsetsAnimationControlImplTest {
 
@@ -72,15 +73,25 @@
     private SurfaceControl mTopLeash;
     private SurfaceControl mNavLeash;
     private InsetsState mInsetsState;
+    private static InsetsModeSession sInsetsModeSession;
 
     @Mock Transaction mMockTransaction;
     @Mock InsetsController mMockController;
     @Mock WindowInsetsAnimationControlListener mMockListener;
     @Mock SyncRtSurfaceTransactionApplier mMockTransactionApplier;
 
+    @BeforeClass
+    public static void setupOnce() {
+        sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
+    }
+
+    @AfterClass
+    public static void tearDownOnce() throws Exception {
+        sInsetsModeSession.close();
+    }
+
     @Before
     public void setup() {
-        ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL;
         MockitoAnnotations.initMocks(this);
         mTopLeash = new SurfaceControl.Builder(mSession)
                 .setName("testSurface")
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 4d8d3f6..1e55828 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -43,7 +43,6 @@
 import android.widget.TextView;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
@@ -63,7 +62,6 @@
  * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
  */
 @Presubmit
-@FlakyTest(detail = "Promote once confirmed non-flaky")
 @RunWith(AndroidJUnit4.class)
 public class InsetsControllerTest {
 
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index a32fa77..971e143 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -34,7 +34,6 @@
 import android.widget.TextView;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
@@ -53,7 +52,6 @@
  * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
  */
 @Presubmit
-@FlakyTest(detail = "Promote once confirmed non-flaky")
 @RunWith(AndroidJUnit4.class)
 public class InsetsSourceConsumerTest {
 
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index b55a9c6..533a58e 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -24,7 +24,6 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
@@ -41,7 +40,6 @@
  * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
  */
 @Presubmit
-@FlakyTest(detail = "Promote once confirmed non-flaky")
 @RunWith(AndroidJUnit4.class)
 public class InsetsSourceTest {
 
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 8e167da..a73269a 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -40,7 +40,6 @@
 import android.view.WindowInsets.Type;
 import android.view.test.InsetsModeSession;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
@@ -56,7 +55,6 @@
  * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
  */
 @Presubmit
-@FlakyTest(detail = "Promote once confirmed non-flaky")
 @RunWith(AndroidJUnit4.class)
 public class InsetsStateTest {
 
diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp
index cdad20e..dc53dd6 100644
--- a/libs/hwui/TreeInfo.cpp
+++ b/libs/hwui/TreeInfo.cpp
@@ -25,6 +25,7 @@
         , prepareTextures(mode == MODE_FULL)
         , canvasContext(canvasContext)
         , damageGenerationId(canvasContext.getFrameNumber())
-        , disableForceDark(canvasContext.useForceDark() ? 0 : 1) {}
+        , disableForceDark(canvasContext.useForceDark() ? 0 : 1)
+        , screenSize(canvasContext.getNextFrameSize()) {}
 
 }  // namespace android::uirenderer
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 04eabac..7e8d12f 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -20,6 +20,7 @@
 #include "utils/Macros.h"
 
 #include <utils/Timers.h>
+#include "SkSize.h"
 
 #include <string>
 
@@ -96,6 +97,8 @@
 
     int disableForceDark;
 
+    const SkISize screenSize;
+
     struct Out {
         bool hasFunctors = false;
         // This is only updated if evaluateAnimations is true
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index da905cf..5418b33 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -547,6 +547,11 @@
 }
 
 void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) {
+    if (canvas->quickReject(bounds)) {
+        // The RenderNode is on screen, but the AVD is not.
+        return;
+    }
+
     // Update the paint for any animatable properties
     SkPaint paint = inPaint;
     paint.setAlpha(mProperties.getRootAlpha() * 255);
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 29d5ef2..41bcfc2 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -22,6 +22,7 @@
 #include "renderthread/CanvasContext.h"
 
 #include <SkImagePriv.h>
+#include <SkPathOps.h>
 
 namespace android {
 namespace uirenderer {
@@ -35,7 +36,7 @@
         animatedImage->syncProperties();
     }
     for (auto& vectorDrawable : mVectorDrawables) {
-        vectorDrawable->syncProperties();
+        vectorDrawable.first->syncProperties();
     }
 }
 
@@ -51,6 +52,29 @@
     }
 }
 
+static bool intersects(const SkISize screenSize, const Matrix4& mat, const SkRect& bounds) {
+    Vector3 points[] = { Vector3 {bounds.fLeft, bounds.fTop, 0},
+                         Vector3 {bounds.fRight, bounds.fTop, 0},
+                         Vector3 {bounds.fRight, bounds.fBottom, 0},
+                         Vector3 {bounds.fLeft, bounds.fBottom, 0}};
+    float minX, minY, maxX, maxY;
+    bool first = true;
+    for (auto& point : points) {
+        mat.mapPoint3d(point);
+        if (first) {
+            minX = maxX = point.x;
+            minY = maxY = point.y;
+            first = false;
+        } else {
+            minX = std::min(minX, point.x);
+            minY = std::min(minY, point.y);
+            maxX = std::max(maxX, point.x);
+            maxY = std::max(maxY, point.y);
+        }
+    }
+    return SkRect::Make(screenSize).intersects(SkRect::MakeLTRB(minX, minY, maxX, maxY));
+}
+
 bool SkiaDisplayList::prepareListAndChildren(
         TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
         std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
@@ -107,15 +131,23 @@
         }
     }
 
-    for (auto& vectorDrawable : mVectorDrawables) {
+    for (auto& vectorDrawablePair : mVectorDrawables) {
         // If any vector drawable in the display list needs update, damage the node.
+        auto& vectorDrawable = vectorDrawablePair.first;
         if (vectorDrawable->isDirty()) {
-            isDirty = true;
-            static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
-                    ->getVectorDrawables()
-                    ->push_back(vectorDrawable);
+            Matrix4 totalMatrix;
+            info.damageAccumulator->computeCurrentTransform(&totalMatrix);
+            Matrix4 canvasMatrix(vectorDrawablePair.second);
+            totalMatrix.multiply(canvasMatrix);
+            const SkRect& bounds = vectorDrawable->properties().getBounds();
+            if (intersects(info.screenSize, totalMatrix, bounds)) {
+                isDirty = true;
+                static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
+                        ->getVectorDrawables()
+                        ->push_back(vectorDrawable);
+                vectorDrawable->setPropertyChangeWillBeConsumed(true);
+            }
         }
-        vectorDrawable->setPropertyChangeWillBeConsumed(true);
     }
     return isDirty;
 }
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 3219ad1..b791037 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -22,6 +22,7 @@
 #include "TreeInfo.h"
 #include "hwui/AnimatedImageDrawable.h"
 #include "utils/LinearAllocator.h"
+#include "utils/Pair.h"
 
 #include <deque>
 
@@ -41,12 +42,6 @@
 
 namespace skiapipeline {
 
-/**
- * This class is intended to be self contained, but still subclasses from
- * DisplayList to make it easier to support switching between the two at
- * runtime.  The downside of this inheritance is that we pay for the overhead
- * of the parent class construction/destruction without any real benefit.
- */
 class SkiaDisplayList {
 public:
     size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); }
@@ -156,7 +151,17 @@
     std::deque<RenderNodeDrawable> mChildNodes;
     std::deque<FunctorDrawable*> mChildFunctors;
     std::vector<SkImage*> mMutableImages;
-    std::vector<VectorDrawableRoot*> mVectorDrawables;
+private:
+    std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables;
+public:
+    void appendVD(VectorDrawableRoot* r) {
+        appendVD(r, SkMatrix::I());
+    }
+
+    void appendVD(VectorDrawableRoot* r, const SkMatrix& mat) {
+        mVectorDrawables.push_back(Pair<VectorDrawableRoot*, SkMatrix>(r, mat));
+    }
+
     std::vector<AnimatedImageDrawable*> mAnimatedImages;
     DisplayListData mDisplayList;
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 5a47a29..0a28949 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -152,7 +152,9 @@
 
 void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
     mRecorder.drawVectorDrawable(tree);
-    mDisplayList->mVectorDrawables.push_back(tree);
+    SkMatrix mat;
+    this->getMatrix(&mat);
+    mDisplayList->appendVD(tree, mat);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 1b3bd30..2957b14 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -41,6 +41,7 @@
 #include <sys/stat.h>
 #include <algorithm>
 
+#include <cstdint>
 #include <cstdlib>
 #include <functional>
 
@@ -510,6 +511,17 @@
     prepareAndDraw(nullptr);
 }
 
+SkISize CanvasContext::getNextFrameSize() const {
+    ReliableSurface* surface = mNativeSurface.get();
+    if (surface) {
+        SkISize size;
+        surface->query(NATIVE_WINDOW_WIDTH, &size.fWidth);
+        surface->query(NATIVE_WINDOW_HEIGHT, &size.fHeight);
+        return size;
+    }
+    return {INT32_MAX, INT32_MAX};
+}
+
 void CanvasContext::prepareAndDraw(RenderNode* node) {
     ATRACE_CALL();
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 0bd080d..912b125 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -32,6 +32,7 @@
 #include <EGL/egl.h>
 #include <SkBitmap.h>
 #include <SkRect.h>
+#include <SkSize.h>
 #include <cutils/compiler.h>
 #include <gui/Surface.h>
 #include <utils/Functor.h>
@@ -112,7 +113,7 @@
     void setSurface(sp<Surface>&& surface);
     bool pauseSurface();
     void setStopped(bool stopped);
-    bool hasSurface() { return mNativeSurface.get(); }
+    bool hasSurface() const { return mNativeSurface.get(); }
     void allocateBuffers();
 
     void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
@@ -205,6 +206,8 @@
     // Must be called before setSurface
     void setRenderAheadDepth(uint32_t renderAhead);
 
+    SkISize getNextFrameSize() const;
+
 private:
     CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
                   IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 1b4cf7e..6fb164a 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -23,6 +23,7 @@
 #include "pipeline/skia/GLFunctorDrawable.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "renderthread/CanvasContext.h"
+#include "tests/common/TestContext.h"
 #include "tests/common/TestUtils.h"
 
 using namespace android;
@@ -50,13 +51,13 @@
     GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas);
     skiaDL->mChildFunctors.push_back(&functorDrawable);
     skiaDL->mMutableImages.push_back(nullptr);
-    skiaDL->mVectorDrawables.push_back(nullptr);
+    skiaDL->appendVD(nullptr);
     skiaDL->mProjectionReceiver = &drawable;
 
     ASSERT_FALSE(skiaDL->mChildNodes.empty());
     ASSERT_FALSE(skiaDL->mChildFunctors.empty());
     ASSERT_FALSE(skiaDL->mMutableImages.empty());
-    ASSERT_FALSE(skiaDL->mVectorDrawables.empty());
+    ASSERT_TRUE(skiaDL->hasVectorDrawables());
     ASSERT_FALSE(skiaDL->isEmpty());
     ASSERT_TRUE(skiaDL->mProjectionReceiver);
 
@@ -65,7 +66,7 @@
     ASSERT_TRUE(skiaDL->mChildNodes.empty());
     ASSERT_TRUE(skiaDL->mChildFunctors.empty());
     ASSERT_TRUE(skiaDL->mMutableImages.empty());
-    ASSERT_TRUE(skiaDL->mVectorDrawables.empty());
+    ASSERT_FALSE(skiaDL->hasVectorDrawables());
     ASSERT_TRUE(skiaDL->isEmpty());
     ASSERT_FALSE(skiaDL->mProjectionReceiver);
 }
@@ -110,7 +111,7 @@
     SkRect bounds = SkRect::MakeWH(200, 200);
     VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
     vectorDrawable.mutateStagingProperties()->setBounds(bounds);
-    skiaDL.mVectorDrawables.push_back(&vectorDrawable);
+    skiaDL.appendVD(&vectorDrawable);
 
     // ensure that the functor and vectorDrawable are properly synced
     TestUtils::runOnRenderThread([&](auto&) {
@@ -149,9 +150,14 @@
 
     SkiaDisplayList skiaDL;
 
+    // The VectorDrawableRoot needs to have bounds on screen (and therefore not
+    // empty) in order to have PropertyChangeWillBeConsumed set.
+    const auto bounds = SkRect::MakeIWH(100, 100);
+
     // prepare with a clean VD
     VectorDrawableRoot cleanVD(new VectorDrawable::Group());
-    skiaDL.mVectorDrawables.push_back(&cleanVD);
+    cleanVD.mutateProperties()->setBounds(bounds);
+    skiaDL.appendVD(&cleanVD);
     cleanVD.getBitmapUpdateIfDirty();  // this clears the dirty bit
 
     ASSERT_FALSE(cleanVD.isDirty());
@@ -159,11 +165,12 @@
     TestUtils::MockTreeObserver observer;
     ASSERT_FALSE(skiaDL.prepareListAndChildren(observer, info, false,
                                                [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
-    ASSERT_TRUE(cleanVD.getPropertyChangeWillBeConsumed());
+    ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
 
     // prepare again this time adding a dirty VD
     VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
-    skiaDL.mVectorDrawables.push_back(&dirtyVD);
+    dirtyVD.mutateProperties()->setBounds(bounds);
+    skiaDL.appendVD(&dirtyVD);
 
     ASSERT_TRUE(dirtyVD.isDirty());
     ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
@@ -191,6 +198,169 @@
     canvasContext->destroy();
 }
 
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
+    auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
+    ContextFactory contextFactory;
+    std::unique_ptr<CanvasContext> canvasContext(
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+
+    // Set up a Surface so that we can position the VectorDrawable offscreen.
+    test::TestContext testContext;
+    testContext.setRenderOffscreen(true);
+    auto surface = testContext.surface();
+    int width, height;
+    surface->query(NATIVE_WINDOW_WIDTH, &width);
+    surface->query(NATIVE_WINDOW_HEIGHT, &height);
+    canvasContext->setSurface(std::move(surface));
+
+    TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
+    DamageAccumulator damageAccumulator;
+    info.damageAccumulator = &damageAccumulator;
+
+    // The VectorDrawableRoot needs to have bounds on screen (and therefore not
+    // empty) in order to have PropertyChangeWillBeConsumed set.
+    const auto bounds = SkRect::MakeIWH(100, 100);
+
+    for (const SkRect b : {bounds.makeOffset(width, 0),
+                           bounds.makeOffset(0, height),
+                           bounds.makeOffset(-bounds.width(), 0),
+                           bounds.makeOffset(0, -bounds.height())}) {
+        SkiaDisplayList skiaDL;
+        VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
+        dirtyVD.mutateProperties()->setBounds(b);
+        skiaDL.appendVD(&dirtyVD);
+
+        ASSERT_TRUE(dirtyVD.isDirty());
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+
+        TestUtils::MockTreeObserver observer;
+        ASSERT_FALSE(skiaDL.prepareListAndChildren(
+                observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+    }
+
+    // The DamageAccumulator's transform can also result in the
+    // VectorDrawableRoot being offscreen.
+    for (const SkISize translate : { SkISize{width, 0},
+                                     SkISize{0, height},
+                                     SkISize{-width, 0},
+                                     SkISize{0, -height}}) {
+        Matrix4 mat4;
+        mat4.translate(translate.fWidth, translate.fHeight);
+        damageAccumulator.pushTransform(&mat4);
+
+        SkiaDisplayList skiaDL;
+        VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
+        dirtyVD.mutateProperties()->setBounds(bounds);
+        skiaDL.appendVD(&dirtyVD);
+
+        ASSERT_TRUE(dirtyVD.isDirty());
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+
+        TestUtils::MockTreeObserver observer;
+        ASSERT_FALSE(skiaDL.prepareListAndChildren(
+                observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+        damageAccumulator.popTransform();
+    }
+
+    // Another way to be offscreen: a matrix from the draw call.
+    for (const SkMatrix translate : { SkMatrix::MakeTrans(width, 0),
+                                      SkMatrix::MakeTrans(0, height),
+                                      SkMatrix::MakeTrans(-width, 0),
+                                      SkMatrix::MakeTrans(0, -height)}) {
+        SkiaDisplayList skiaDL;
+        VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
+        dirtyVD.mutateProperties()->setBounds(bounds);
+        skiaDL.appendVD(&dirtyVD, translate);
+
+        ASSERT_TRUE(dirtyVD.isDirty());
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+
+        TestUtils::MockTreeObserver observer;
+        ASSERT_FALSE(skiaDL.prepareListAndChildren(
+                observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+    }
+
+    // Verify that the matrices are combined in the right order.
+    {
+        // Rotate and then translate, so the VD is offscreen.
+        Matrix4 mat4;
+        mat4.loadRotate(180);
+        damageAccumulator.pushTransform(&mat4);
+
+        SkiaDisplayList skiaDL;
+        VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
+        dirtyVD.mutateProperties()->setBounds(bounds);
+        SkMatrix translate = SkMatrix::MakeTrans(50, 50);
+        skiaDL.appendVD(&dirtyVD, translate);
+
+        ASSERT_TRUE(dirtyVD.isDirty());
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+
+        TestUtils::MockTreeObserver observer;
+        ASSERT_FALSE(skiaDL.prepareListAndChildren(
+                observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+        damageAccumulator.popTransform();
+    }
+    {
+        // Switch the order of rotate and translate, so it is on screen.
+        Matrix4 mat4;
+        mat4.translate(50, 50);
+        damageAccumulator.pushTransform(&mat4);
+
+        SkiaDisplayList skiaDL;
+        VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
+        dirtyVD.mutateProperties()->setBounds(bounds);
+        SkMatrix rotate;
+        rotate.setRotate(180);
+        skiaDL.appendVD(&dirtyVD, rotate);
+
+        ASSERT_TRUE(dirtyVD.isDirty());
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+
+        TestUtils::MockTreeObserver observer;
+        ASSERT_TRUE(skiaDL.prepareListAndChildren(
+                observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
+        ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
+        damageAccumulator.popTransform();
+    }
+    {
+        // An AVD that is larger than the screen.
+        SkiaDisplayList skiaDL;
+        VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
+        dirtyVD.mutateProperties()->setBounds(SkRect::MakeLTRB(-1, -1, width + 1, height + 1));
+        skiaDL.appendVD(&dirtyVD);
+
+        ASSERT_TRUE(dirtyVD.isDirty());
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+
+        TestUtils::MockTreeObserver observer;
+        ASSERT_TRUE(skiaDL.prepareListAndChildren(
+                observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
+        ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
+    }
+    {
+        // An AVD whose bounds are not a rectangle after applying a matrix.
+        SkiaDisplayList skiaDL;
+        VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
+        dirtyVD.mutateProperties()->setBounds(bounds);
+        SkMatrix mat;
+        mat.setRotate(45, 50, 50);
+        skiaDL.appendVD(&dirtyVD, mat);
+
+        ASSERT_TRUE(dirtyVD.isDirty());
+        ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+
+        TestUtils::MockTreeObserver observer;
+        ASSERT_TRUE(skiaDL.prepareListAndChildren(
+                observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
+        ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
+    }
+}
+
 TEST(SkiaDisplayList, updateChildren) {
     SkiaDisplayList skiaDL;
 
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 1544adb..b2baff5 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -272,6 +272,9 @@
                 final int importance = entry.getImportance() < IMPORTANCE_LOW
                         ? entry.getImportance() : IMPORTANCE_LOW;
                 signals.putInt(KEY_IMPORTANCE, importance);
+            } else {
+                // Even if no change is made, send an identity adjustment for metric logging.
+                signals.putInt(KEY_IMPORTANCE, entry.getImportance());
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d2b1e8f..c12ee03 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -20032,7 +20032,7 @@
                     PreferredActivity pa = removed.get(j);
                     pir.removeFilter(pa);
                 }
-                outUserChanged.setValueAt(thisUserId, true);
+                outUserChanged.put(thisUserId, true);
             }
         }
     }
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index 71a28b5..eb568e0 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -17,6 +17,7 @@
 package android.telecom;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.os.Build;
@@ -174,4 +175,21 @@
                 in.readString(),
                 UserHandle.CREATOR.createFromParcel(in));
     }
+
+    /**
+     * Determines if two {@link PhoneAccountHandle}s are from the same package.
+     *
+     * @param a Phone account handle to check for same {@link ConnectionService} package.
+     * @param b Other phone account handle to check for same {@link ConnectionService} package.
+     * @return {@code true} if the two {@link PhoneAccountHandle}s passed in belong to the same
+     * {@link ConnectionService} / package, {@code false} otherwise.  Note: {@code null} phone
+     * account handles are considered equivalent to other {@code null} phone account handles.
+     * @hide
+     */
+    public static boolean areFromSamePackage(@Nullable PhoneAccountHandle a,
+            @Nullable PhoneAccountHandle b) {
+        String aPackageName = a != null ? a.getComponentName().getPackageName() : null;
+        String bPackageName = b != null ? b.getComponentName().getPackageName() : null;
+        return Objects.equals(aPackageName, bPackageName);
+    }
 }