Merge "Merge six commits from master-skia to master"
diff --git a/core/java/android/content/pm/AppsQueryHelper.java b/core/java/android/content/pm/AppsQueryHelper.java
index 56b3173..a5a8e3f 100644
--- a/core/java/android/content/pm/AppsQueryHelper.java
+++ b/core/java/android/content/pm/AppsQueryHelper.java
@@ -23,6 +23,8 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -46,6 +48,14 @@
      */
     public static int GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM = 1 << 1;
 
+    /**
+     * Return all input methods that are marked as default.
+     * <p>When this flag is set, {@code user} specified in
+     * {@link #queryApps(int, boolean, UserHandle)} must be
+     * {@link UserHandle#myUserId user of the current process}.
+     */
+    public static int GET_DEFAULT_IMES = 1 << 2;
+
     private final Context mContext;
     private List<ApplicationInfo> mAllApps;
 
@@ -56,13 +66,14 @@
     /**
      * Return a List of all packages that satisfy a specified criteria.
      * @param flags search flags. Use any combination of {@link #GET_NON_LAUNCHABLE_APPS},
-     * {@link #GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM}
+     * {@link #GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM} or {@link #GET_DEFAULT_IMES}.
      * @param systemAppsOnly if true, only system apps will be returned
      * @param user user, whose apps are queried
      */
     public List<String> queryApps(int flags, boolean systemAppsOnly, UserHandle user) {
         boolean nonLaunchableApps = (flags & GET_NON_LAUNCHABLE_APPS) > 0;
         boolean interactAcrossUsers = (flags & GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM) > 0;
+        boolean defaultImes = (flags & GET_DEFAULT_IMES) > 0;
         if (mAllApps == null) {
             mAllApps = getAllApps(user.getIdentifier());
         }
@@ -118,6 +129,33 @@
             }
         }
 
+        if (defaultImes) {
+            if (UserHandle.myUserId() != user.getIdentifier()) {
+                throw new IllegalArgumentException("Specified user handle " + user
+                        + " is not a user of the current process.");
+            }
+            List<InputMethodInfo> imis = getInputMethodList();
+            int imisSize = imis.size();
+            ArraySet<String> defaultImePackages = new ArraySet<>();
+            for (int i = 0; i < imisSize; i++) {
+                InputMethodInfo imi = imis.get(i);
+                if (imi.isDefault(mContext)) {
+                    defaultImePackages.add(imi.getPackageName());
+                }
+            }
+            final int allAppsSize = mAllApps.size();
+            for (int i = 0; i < allAppsSize; i++) {
+                final ApplicationInfo appInfo = mAllApps.get(i);
+                if (systemAppsOnly && !appInfo.isSystemApp()) {
+                    continue;
+                }
+                final String packageName = appInfo.packageName;
+                if (defaultImePackages.contains(packageName)) {
+                    result.add(packageName);
+                }
+            }
+        }
+
         return result;
     }
 
@@ -150,4 +188,12 @@
             throw new IllegalStateException("Package manager has died", e);
         }
     }
+
+    @VisibleForTesting
+    @SuppressWarnings("unchecked")
+    protected List<InputMethodInfo> getInputMethodList() {
+        InputMethodManager imm = (InputMethodManager)
+                mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+        return imm.getInputMethodList();
+    }
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 909d33c..17e1a28 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -774,17 +774,20 @@
     }
 
     /**
-     * This will no longer work.  Use {@link #setUserRestriction(String, boolean)} instead.
+     * This will no longer work.  Device owners and profile owners should use
+     * {@link DevicePolicyManager#addUserRestriction(ComponentName, String)} instead.
      */
+    // System apps should use UserManager.setUserRestriction() instead.
     @Deprecated
     public void setUserRestrictions(Bundle restrictions) {
         throw new UnsupportedOperationException("This method is no longer supported");
     }
 
     /**
-     * This will no longer work.  Use {@link #setUserRestriction(String, boolean, UserHandle)}
-     * instead.
+     * This will no longer work.  Device owners and profile owners should use
+     * {@link DevicePolicyManager#addUserRestriction(ComponentName, String)} instead.
      */
+    // System apps should use UserManager.setUserRestriction() instead.
     @Deprecated
     public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) {
         throw new UnsupportedOperationException("This method is no longer supported");
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2621bc9c..dd42c22 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -367,7 +367,8 @@
 
     <dimen name="resolver_max_width">480dp</dimen>
 
-    <!-- Size of the offset applied to the position of the circular mask. This
+    <!-- @deprecated Use config_windowOutsetBottom instead.
+         Size of the offset applied to the position of the circular mask. This
          is only used on circular displays. In the case where there is no
          "chin", this will default to 0 -->
     <dimen name="circular_display_mask_offset">0px</dimen>
diff --git a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
index 80181cf..4c9b00a 100644
--- a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.os.UserHandle;
 import android.test.AndroidTestCase;
+import android.view.inputmethod.InputMethodInfo;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -77,6 +78,27 @@
         assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);
     }
 
+    public void testQueryAppsDefaultIme() {
+        // Test query default system IMEs
+        List<String> apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_DEFAULT_IMES,
+                true, UserHandle.of(UserHandle.myUserId()));
+        assertEqualsIgnoreOrder(Arrays.asList("sys_app1"), apps);
+
+        // Test query default IMEs
+        apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_DEFAULT_IMES, false,
+                UserHandle.of(UserHandle.myUserId()));
+        assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "app4"), apps);
+
+        // Test that GET_DEFAULT_IMES cannot be used with a user id different from current process
+        try {
+            mAppsQueryHelper.queryApps(AppsQueryHelper.GET_DEFAULT_IMES, false,
+                    UserHandle.of(UserHandle.USER_NULL));
+            fail("queryApps must fail if wrong user was passed");
+        } catch (IllegalArgumentException e) {
+            // OK
+        }
+    }
+
     private class AppsQueryHelperTestable extends AppsQueryHelper {
 
         public AppsQueryHelperTestable(Context context) {
@@ -121,6 +143,21 @@
             p1.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
             return Arrays.asList(p1);
         }
+
+        @Override
+        protected List<InputMethodInfo> getInputMethodList() {
+            final ResolveInfo sysApp1 = new ResolveInfo();
+            sysApp1.serviceInfo = new ServiceInfo();
+            sysApp1.serviceInfo.packageName = "sys_app1";
+            sysApp1.serviceInfo.name = "name";
+            InputMethodInfo imi1 = new InputMethodInfo(sysApp1, false, null, null, 0, true);
+            final ResolveInfo app4 = new ResolveInfo();
+            app4.serviceInfo = new ServiceInfo();
+            app4.serviceInfo.packageName = "app4";
+            app4.serviceInfo.name = "name";
+            InputMethodInfo imi2 = new InputMethodInfo(app4, false, null, null, 0, true);
+            return Arrays.asList(imi1, imi2);
+        }
     }
 
     private static void assertEqualsIgnoreOrder(List<String> expected, List<String> actual) {
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 7dbba25..0868853 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -25,190 +25,210 @@
 namespace android {
 namespace uirenderer {
 
-void BakedOpRenderer::Info::setViewport(uint32_t width, uint32_t height) {
-    viewportWidth = width;
-    viewportHeight = height;
-    orthoMatrix.loadOrtho(viewportWidth, viewportHeight);
+////////////////////////////////////////////////////////////////////////////////
+// OffscreenBuffer
+////////////////////////////////////////////////////////////////////////////////
 
-    renderState.setViewport(width, height);
-    renderState.blend().syncEnabled();
+OffscreenBuffer::OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
+        uint32_t viewportWidth, uint32_t viewportHeight)
+        : texture(caches)
+        , texCoords(0, viewportHeight / float(textureHeight), viewportWidth / float(textureWidth), 0) {
+    texture.width = textureWidth;
+    texture.height = textureHeight;
+
+    caches.textureState().activateTexture(0);
+    glGenTextures(1, &texture.id);
+    caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id);
+
+    texture.setWrap(GL_CLAMP_TO_EDGE, false, false, GL_TEXTURE_2D);
+    // not setting filter on texture, since it's set when drawing, based on transform
+
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0,
+            GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
 }
 
-Texture* BakedOpRenderer::Info::getTexture(const SkBitmap* bitmap) {
-    Texture* texture = renderState.assetAtlas().getEntryTexture(bitmap);
-    if (!texture) {
-        return caches.textureCache.get(bitmap);
-    }
-    return texture;
-}
+////////////////////////////////////////////////////////////////////////////////
+// BakedOpRenderer
+////////////////////////////////////////////////////////////////////////////////
 
-void BakedOpRenderer::Info::renderGlop(const BakedOpState& state, const Glop& glop) {
-    bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
-    renderState.scissor().setEnabled(useScissor);
-    if (useScissor) {
-        const Rect& clip = state.computedState.clipRect;
-        renderState.scissor().set(clip.left, viewportHeight - clip.bottom,
-            clip.getWidth(), clip.getHeight());
-    }
-    renderState.render(glop, orthoMatrix);
-    didDraw = true;
-}
+OffscreenBuffer* BakedOpRenderer::startLayer(uint32_t width, uint32_t height) {
+    LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
 
-Layer* BakedOpRenderer::startLayer(Info& info, uint32_t width, uint32_t height) {
-    info.caches.textureState().activateTexture(0);
-    Layer* layer = info.caches.layerCache.get(info.renderState, width, height);
-    LOG_ALWAYS_FATAL_IF(!layer, "need layer...");
+    // TODO: really should be caching these!
+    OffscreenBuffer* buffer = new OffscreenBuffer(mCaches, width, height, width, height);
+    mRenderTarget.offscreenBuffer = buffer;
 
-    info.layer = layer;
-    layer->texCoords.set(0.0f, width / float(layer->getHeight()),
-            height / float(layer->getWidth()), 0.0f);
-
-    layer->setFbo(info.renderState.genFramebuffer());
-    info.renderState.bindFramebuffer(layer->getFbo());
-    layer->bindTexture();
-
-    // Initialize the texture if needed
-    if (layer->isEmpty()) {
-        layer->allocateTexture();
-        layer->setEmpty(false);
-    }
+    // create and bind framebuffer
+    mRenderTarget.frameBufferId = mRenderState.genFramebuffer();
+    mRenderState.bindFramebuffer(mRenderTarget.frameBufferId);
 
     // attach the texture to the FBO
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-            layer->getTextureId(), 0);
+            buffer->texture.id, 0);
     LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
     LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
             "framebuffer incomplete!");
 
     // Clear the FBO
-    info.renderState.scissor().setEnabled(false);
+    mRenderState.scissor().setEnabled(false);
     glClear(GL_COLOR_BUFFER_BIT);
 
     // Change the viewport & ortho projection
-    info.setViewport(width, height);
-    return layer;
+    setViewport(width, height);
+    return buffer;
 }
 
-void BakedOpRenderer::endLayer(Info& info) {
-    Layer* layer = info.layer;
-    info.layer = nullptr;
+void BakedOpRenderer::endLayer() {
+    mRenderTarget.offscreenBuffer = nullptr;
 
     // Detach the texture from the FBO
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
     LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED");
-    layer->removeFbo(false);
+    mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId);
+    mRenderTarget.frameBufferId = -1;
 }
 
-void BakedOpRenderer::startFrame(Info& info, uint32_t width, uint32_t height) {
-    info.renderState.bindFramebuffer(0);
-    info.setViewport(width, height);
-    Caches::getInstance().clearGarbage();
+void BakedOpRenderer::startFrame(uint32_t width, uint32_t height) {
+    mRenderState.bindFramebuffer(0);
+    setViewport(width, height);
+    mCaches.clearGarbage();
 
-    if (!info.opaque) {
+    if (!mOpaque) {
         // TODO: partial invalidate!
-        info.renderState.scissor().setEnabled(false);
+        mRenderState.scissor().setEnabled(false);
         glClear(GL_COLOR_BUFFER_BIT);
-        info.didDraw = true;
+        mHasDrawn = true;
     }
 }
-void BakedOpRenderer::endFrame(Info& info) {
-    info.caches.pathCache.trim();
-    info.caches.tessellationCache.trim();
+
+void BakedOpRenderer::endFrame() {
+    mCaches.pathCache.trim();
+    mCaches.tessellationCache.trim();
 
 #if DEBUG_OPENGL
     GLUtils::dumpGLErrors();
 #endif
 
 #if DEBUG_MEMORY_USAGE
-    info.caches.dumpMemoryUsage();
+    mCaches.dumpMemoryUsage();
 #else
     if (Properties::debugLevel & kDebugMemory) {
-        info.caches.dumpMemoryUsage();
+        mCaches.dumpMemoryUsage();
     }
 #endif
 }
 
-void BakedOpRenderer::onRenderNodeOp(Info&, const RenderNodeOp&, const BakedOpState&) {
+void BakedOpRenderer::setViewport(uint32_t width, uint32_t height) {
+    mRenderTarget.viewportWidth = width;
+    mRenderTarget.viewportHeight = height;
+    mRenderTarget.orthoMatrix.loadOrtho(width, height);
+
+    mRenderState.setViewport(width, height);
+    mRenderState.blend().syncEnabled();
+}
+
+Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) {
+    Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
+    if (!texture) {
+        return mCaches.textureCache.get(bitmap);
+    }
+    return texture;
+}
+
+void BakedOpRenderer::renderGlop(const BakedOpState& state, const Glop& glop) {
+    bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
+    mRenderState.scissor().setEnabled(useScissor);
+    if (useScissor) {
+        const Rect& clip = state.computedState.clipRect;
+        mRenderState.scissor().set(clip.left, mRenderTarget.viewportHeight - clip.bottom,
+            clip.getWidth(), clip.getHeight());
+    }
+    mRenderState.render(glop, mRenderTarget.orthoMatrix);
+    mHasDrawn = true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static BakedOpDispatcher methods
+////////////////////////////////////////////////////////////////////////////////
+
+void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
     LOG_ALWAYS_FATAL("unsupported operation");
 }
 
-void BakedOpRenderer::onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) {
-    info.caches.textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
-    Texture* texture = info.getTexture(op.bitmap);
+void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
+    renderer.caches().textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
+    Texture* texture = renderer.getTexture(op.bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
     const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
             ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
     Glop glop;
-    GlopBuilder(info.renderState, info.caches, &glop)
+    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
             .setRoundRectClipState(state.roundRectClipState)
             .setMeshTexturedUnitQuad(texture->uvMapper)
             .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
             .setTransform(state.computedState.transform, TransformFlags::None)
             .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
             .build();
-    info.renderGlop(state, glop);
+    renderer.renderGlop(state, glop);
 }
 
-void BakedOpRenderer::onRectOp(Info& info, const RectOp& op, const BakedOpState& state) {
+void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
     Glop glop;
-    GlopBuilder(info.renderState, info.caches, &glop)
+    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
             .setRoundRectClipState(state.roundRectClipState)
             .setMeshUnitQuad()
             .setFillPaint(*op.paint, state.alpha)
             .setTransform(state.computedState.transform, TransformFlags::None)
             .setModelViewMapUnitToRect(op.unmappedBounds)
             .build();
-    info.renderGlop(state, glop);
+    renderer.renderGlop(state, glop);
 }
 
-void BakedOpRenderer::onSimpleRectsOp(Info& info, const SimpleRectsOp& op, const BakedOpState& state) {
+void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
     Glop glop;
-    GlopBuilder(info.renderState, info.caches, &glop)
+    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
             .setRoundRectClipState(state.roundRectClipState)
             .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
             .setFillPaint(*op.paint, state.alpha)
             .setTransform(state.computedState.transform, TransformFlags::None)
             .setModelViewOffsetRect(0, 0, op.unmappedBounds)
             .build();
-    info.renderGlop(state, glop);
+    renderer.renderGlop(state, glop);
 }
 
-void BakedOpRenderer::onBeginLayerOp(Info& info, const BeginLayerOp& op, const BakedOpState& state) {
+void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
     LOG_ALWAYS_FATAL("unsupported operation");
 }
 
-void BakedOpRenderer::onEndLayerOp(Info& info, const EndLayerOp& op, const BakedOpState& state) {
+void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
     LOG_ALWAYS_FATAL("unsupported operation");
 }
 
-void BakedOpRenderer::onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) {
-    Layer* layer = *op.layerHandle;
+void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
+    OffscreenBuffer* buffer = *op.layerHandle;
 
-    // TODO: make this work for HW layers
-    layer->setPaint(op.paint);
-    layer->setBlend(true);
-    float layerAlpha = (layer->getAlpha() / 255.0f) * state.alpha;
-
+    // TODO: extend this to handle HW layers & paint properties which
+    // reside in node.properties().layerProperties()
+    float layerAlpha = (op.paint->getAlpha() / 255.0f) * state.alpha;
     const bool tryToSnap = state.computedState.transform.isPureTranslate();
     Glop glop;
-    GlopBuilder(info.renderState, info.caches, &glop)
+    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
             .setRoundRectClipState(state.roundRectClipState)
-            .setMeshTexturedUvQuad(nullptr, layer->texCoords)
-            .setFillLayer(layer->getTexture(), layer->getColorFilter(), layerAlpha, layer->getMode(), Blend::ModeOrderSwap::NoSwap)
+            .setMeshTexturedUvQuad(nullptr, buffer->texCoords)
+            .setFillLayer(buffer->texture, op.paint->getColorFilter(), layerAlpha, PaintUtils::getXfermodeDirect(op.paint), Blend::ModeOrderSwap::NoSwap)
             .setTransform(state.computedState.transform, TransformFlags::None)
             .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
             .build();
-    info.renderGlop(state, glop);
+    renderer.renderGlop(state, glop);
 
-    // return layer to cache, since each clipped savelayer is only drawn once.
-    layer->setConvexMask(nullptr);
-    if (!info.caches.layerCache.put(layer)) {
-        // Failing to add the layer to the cache should happen only if the layer is too large
-        LAYER_LOGD("Deleting layer");
-        layer->decStrong(nullptr);
-    }
+    // destroy and delete, since each clipped saveLayer is only drawn once.
+    buffer->texture.deleteTexture();
+
+    // TODO: return texture/offscreenbuffer to cache!
+    delete buffer;
 }
 
 } // namespace uirenderer
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 616adde..16afad4 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -28,48 +28,82 @@
 class Layer;
 class RenderState;
 
+/**
+ * Lightweight alternative to Layer. Owns the persistent state of an offscreen render target, and
+ * encompasses enough information to draw it back on screen (minus paint properties, which are held
+ * by LayerOp).
+ */
+class OffscreenBuffer {
+public:
+    OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
+            uint32_t viewportWidth, uint32_t viewportHeight);
+
+    Texture texture;
+    Rect texCoords;
+    Region region;
+};
+
+/**
+ * Main rendering manager for a collection of work - one frame + any contained FBOs.
+ *
+ * Manages frame and FBO lifecycle, binding the GL framebuffer as appropriate. This is the only
+ * place where FBOs are bound, created, and destroyed.
+ *
+ * All rendering operations will be sent by the Dispatcher, a collection of static methods,
+ * which has intentionally limited access to the renderer functionality.
+ */
 class BakedOpRenderer {
 public:
-    class Info {
-    public:
-        Info(Caches& caches, RenderState& renderState, bool opaque)
-                : renderState(renderState)
-                , caches(caches)
-                , opaque(opaque) {
-        }
+    BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque)
+            : mRenderState(renderState)
+            , mCaches(caches)
+            , mOpaque(opaque) {
+    }
 
-        void setViewport(uint32_t width, uint32_t height);
+    RenderState& renderState() { return mRenderState; }
+    Caches& caches() { return mCaches; }
 
-        Texture* getTexture(const SkBitmap* bitmap);
+    void startFrame(uint32_t width, uint32_t height);
+    void endFrame();
+    OffscreenBuffer* startLayer(uint32_t width, uint32_t height);
+    void endLayer();
 
-        void renderGlop(const BakedOpState& state, const Glop& glop);
-        RenderState& renderState;
-        Caches& caches;
+    Texture* getTexture(const SkBitmap* bitmap);
 
-        bool didDraw = false;
+    void renderGlop(const BakedOpState& state, const Glop& glop);
+    bool didDraw() { return mHasDrawn; }
+private:
+    void setViewport(uint32_t width, uint32_t height);
 
-        Layer* layer = nullptr;
+    RenderState& mRenderState;
+    Caches& mCaches;
+    bool mOpaque;
+    bool mHasDrawn = false;
 
-        // where should these live? layer state object?
-        bool opaque;
+    // render target state - setup by start/end layer/frame
+    // only valid to use in between start/end pairs.
+    struct {
+        GLuint frameBufferId = 0;
+        OffscreenBuffer* offscreenBuffer = nullptr;
         uint32_t viewportWidth = 0;
         uint32_t viewportHeight = 0;
         Matrix4 orthoMatrix;
-    };
+    } mRenderTarget;
+};
 
-    static Layer* startLayer(Info& info, uint32_t width, uint32_t height);
-    static void endLayer(Info& info);
-    static void startFrame(Info& info, uint32_t width, uint32_t height);
-    static void endFrame(Info& info);
-
-    /**
-     * Declare all "onBitmapOp(...)" style function for every op type.
-     *
-     * These functions will perform the actual rendering of the individual operations in OpenGL,
-     * given the transform/clip and other state built into the BakedOpState object passed in.
-     */
-    #define BAKED_OP_RENDERER_METHOD(Type) static void on##Type(Info& info, const Type& op, const BakedOpState& state);
-    MAP_OPS(BAKED_OP_RENDERER_METHOD);
+/**
+ * Provides all "onBitmapOp(...)" style static methods for every op type, which convert the
+ * RecordedOps and their state to Glops, and renders them with the provided BakedOpRenderer.
+ *
+ * This dispatcher is separate from the renderer so that the dispatcher / renderer interaction is
+ * minimal through public BakedOpRenderer APIs.
+ */
+class BakedOpDispatcher {
+public:
+    // Declares all "onBitmapOp(...)" style methods for every op type
+#define DISPATCH_METHOD(Type) \
+        static void on##Type(BakedOpRenderer& renderer, const Type& op, const BakedOpState& state);
+    MAP_OPS(DISPATCH_METHOD);
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index cde42f8..ddeb336 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -277,7 +277,8 @@
     }
 }
 
-void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) const {
+void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers) const {
+    ATRACE_NAME("flush drawing commands");
     for (const BatchBase* batch : mBatches) {
         // TODO: different behavior based on batch->isMerging()
         for (const BakedOpState* op : batch->getOps()) {
@@ -353,10 +354,6 @@
     }
 }
 
-void OpReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) {
-    ATRACE_NAME("flush drawing commands");
-}
-
 void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
     if (op.renderNode->nothingToDraw()) {
         return;
@@ -435,7 +432,7 @@
             beginLayerOp.localMatrix,
             beginLayerOp.localClipRect,
             beginLayerOp.paint,
-            &mLayerReorderers[finishedLayerIndex].layer);
+            &mLayerReorderers[finishedLayerIndex].offscreenBuffer);
     BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
 
     if (bakedOpState) {
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index f32b858..927ecfa 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -33,6 +33,7 @@
 class BakedOpState;
 class BatchBase;
 class MergingOpBatch;
+class OffscreenBuffer;
 class OpBatch;
 class Rect;
 
@@ -55,7 +56,7 @@
 }
 
 class OpReorderer : public CanvasStateClient {
-    typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpReceiver;
+    typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpDispatcher;
 
     /**
      * Stores the deferred render operations and state used to compute ordering
@@ -79,7 +80,7 @@
         void deferMergeableOp(LinearAllocator& allocator,
                 BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
 
-        void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) const;
+        void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers) const;
 
         bool empty() const {
             return mBatches.empty();
@@ -91,7 +92,7 @@
 
         void dump() const;
 
-        Layer* layer = nullptr;
+        OffscreenBuffer* offscreenBuffer = nullptr;
         const BeginLayerOp* beginLayerOp = nullptr;
         const uint32_t width;
         const uint32_t height;
@@ -127,15 +128,15 @@
      *
      * For example a BitmapOp would resolve, via the lambda lookup, to calling:
      *
-     * StaticReceiver::onBitmapOp(Arg* arg, const BitmapOp& op, const BakedOpState& state);
+     * StaticDispatcher::onBitmapOp(Renderer& renderer, const BitmapOp& op, const BakedOpState& state);
      */
 #define BAKED_OP_RECEIVER(Type) \
-    [](void* internalArg, const RecordedOp& op, const BakedOpState& state) { \
-        StaticReceiver::on##Type(*(static_cast<Arg*>(internalArg)), static_cast<const Type&>(op), state); \
+    [](void* internalRenderer, const RecordedOp& op, const BakedOpState& state) { \
+        StaticDispatcher::on##Type(*(static_cast<Renderer*>(internalRenderer)), static_cast<const Type&>(op), state); \
     },
-    template <typename StaticReceiver, typename Arg>
-    void replayBakedOps(Arg& arg) {
-        static BakedOpReceiver receivers[] = {
+    template <typename StaticDispatcher, typename Renderer>
+    void replayBakedOps(Renderer& renderer) {
+        static BakedOpDispatcher receivers[] = {
             MAP_OPS(BAKED_OP_RECEIVER)
         };
 
@@ -144,16 +145,16 @@
         for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
             LayerReorderer& layer = mLayerReorderers[i];
             if (!layer.empty()) {
-                layer.layer = StaticReceiver::startLayer(arg, layer.width, layer.height);
-                layer.replayBakedOpsImpl((void*)&arg, receivers);
-                StaticReceiver::endLayer(arg);
+                layer.offscreenBuffer = renderer.startLayer(layer.width, layer.height);
+                layer.replayBakedOpsImpl((void*)&renderer, receivers);
+                renderer.endLayer();
             }
         }
 
         const LayerReorderer& fbo0 = mLayerReorderers[0];
-        StaticReceiver::startFrame(arg, fbo0.width, fbo0.height);
-        fbo0.replayBakedOpsImpl((void*)&arg, receivers);
-        StaticReceiver::endFrame(arg);
+        renderer.startFrame(fbo0.width, fbo0.height);
+        fbo0.replayBakedOpsImpl((void*)&renderer, receivers);
+        renderer.endFrame();
     }
 
     void dump() const {
@@ -178,7 +179,7 @@
 
     void deferImpl(const DisplayList& displayList);
 
-    void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers);
+    void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers);
 
     /**
      * Declares all OpReorderer::onXXXXOp() methods for every RecordedOp type.
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 6c31b42..7874d85 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -29,7 +29,7 @@
 namespace android {
 namespace uirenderer {
 
-class Layer;
+class OffscreenBuffer;
 class RenderNode;
 struct Vertex;
 
@@ -137,12 +137,12 @@
 };
 
 struct LayerOp : RecordedOp {
-    LayerOp(BASE_PARAMS, Layer** layerHandle)
+    LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
             : SUPER(LayerOp)
             , layerHandle(layerHandle) {}
     // Records a handle to the Layer object, since the Layer itself won't be
     // constructed until after this operation is constructed.
-    Layer** layerHandle;
+    OffscreenBuffer** layerHandle;
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp
index 382c0bd..43f170f 100644
--- a/libs/hwui/microbench/OpReordererBench.cpp
+++ b/libs/hwui/microbench/OpReordererBench.cpp
@@ -60,10 +60,10 @@
         StartBenchmarkTiming();
         for (int i = 0; i < iters; i++) {
             OpReorderer reorderer(200, 200, *sReorderingDisplayList);
-            MicroBench::DoNotOptimize(&reorderer);
 
-            BakedOpRenderer::Info info(caches, renderState, true);
-            reorderer.replayBakedOps<BakedOpRenderer>(info);
+            BakedOpRenderer renderer(caches, renderState, true);
+            reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+            MicroBench::DoNotOptimize(&renderer);
         }
         StopBenchmarkTiming();
     });
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 87a7996..3cda170 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -16,24 +16,25 @@
 #ifndef RENDERSTATE_H
 #define RENDERSTATE_H
 
-#include <set>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <utils/Mutex.h>
-#include <utils/Functor.h>
-#include <utils/RefBase.h>
-#include <private/hwui/DrawGlInfo.h>
-#include <renderstate/Blend.h>
-
 #include "AssetAtlas.h"
 #include "Caches.h"
 #include "Glop.h"
+#include "renderstate/Blend.h"
 #include "renderstate/MeshState.h"
 #include "renderstate/PixelBufferState.h"
 #include "renderstate/Scissor.h"
 #include "renderstate/Stencil.h"
 #include "utils/Macros.h"
 
+#include <set>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <ui/Region.h>
+#include <utils/Mutex.h>
+#include <utils/Functor.h>
+#include <utils/RefBase.h>
+#include <private/hwui/DrawGlInfo.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -49,6 +50,8 @@
 // wrapper of Caches for users to migrate to.
 class RenderState {
     PREVENT_COPY_AND_ASSIGN(RenderState);
+    friend class renderthread::RenderThread;
+    friend class Caches;
 public:
     void onGLContextCreated();
     void onGLContextDestroyed();
@@ -61,7 +64,6 @@
     GLuint genFramebuffer();
     void deleteFramebuffer(GLuint fbo);
 
-
     void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info);
 
     void debugOverdraw(bool enable, bool clear);
@@ -96,10 +98,8 @@
     Stencil& stencil() { return *mStencil; }
 
     void dump();
-private:
-    friend class renderthread::RenderThread;
-    friend class Caches;
 
+private:
     void interruptForFunctorInvoke();
     void resumeFromFunctorInvoke();
     void assertOnGLThread();
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 7c0f0b6..1c6ac8c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -319,11 +319,11 @@
 
 #if HWUI_NEW_OPS
     OpReorderer reorderer(dirty, frame.width(), frame.height(), mRenderNodes);
-    BakedOpRenderer::Info info(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
+    BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
     // TODO: profiler().draw(mCanvas);
-    reorderer.replayBakedOps<BakedOpRenderer>(info);
+    reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
 
-    bool drew = info.didDraw;
+    bool drew = renderer.didDraw();
 
 #else
     mCanvas->prepareDirty(frame.width(), frame.height(),
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index af9c3f2..ffb575f 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -27,80 +27,67 @@
 namespace android {
 namespace uirenderer {
 
+
 /**
- * Class that redirects static operation dispatch to virtual methods on a Client class.
+ * Virtual class implemented by each test to redirect static operation / state transitions to
+ * virtual methods.
  *
- * The client is recreated for every op (so data cannot be persisted between operations), but the
- * virtual dispatch allows for default behaviors to be specified without enumerating each operation
- * for every test.
+ * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
+ * and allows Renderer vs Dispatching behavior to be merged.
  *
  * onXXXOp methods fail by default - tests should override ops they expect
  * startLayer fails by default - tests should override if expected
  * startFrame/endFrame do nothing by default - tests should override to intercept
  */
-template<class CustomClient, class Arg>
-class TestReceiver {
+class TestRendererBase {
 public:
-#define CLIENT_METHOD(Type) \
-    virtual void on##Type(Arg&, const Type&, const BakedOpState&) { ADD_FAILURE(); }
-    class Client {
-    public:
-        virtual ~Client() {};
-        MAP_OPS(CLIENT_METHOD)
+    virtual ~TestRendererBase() {}
+    virtual OffscreenBuffer* startLayer(uint32_t width, uint32_t height) { ADD_FAILURE(); return nullptr; }
+    virtual void endLayer() { ADD_FAILURE(); }
+    virtual void startFrame(uint32_t width, uint32_t height) {}
+    virtual void endFrame() {}
 
-        virtual Layer* startLayer(Arg& info, uint32_t width, uint32_t height) { ADD_FAILURE(); return nullptr; }
-        virtual void endLayer(Arg& info) { ADD_FAILURE(); }
-        virtual void startFrame(Arg& info, uint32_t width, uint32_t height) {}
-        virtual void endFrame(Arg& info) {}
-    };
+    // define virtual defaults for direct
+#define BASE_OP_METHOD(Type) \
+    virtual void on##Type(const Type&, const BakedOpState&) { ADD_FAILURE(); }
+    MAP_OPS(BASE_OP_METHOD)
+    int getIndex() { return mIndex; }
 
+protected:
+    int mIndex = 0;
+};
+
+/**
+ * Dispatches all static methods to similar formed methods on renderer, which fail by default but
+ * are overriden by subclasses per test.
+ */
+class TestDispatcher {
+public:
 #define DISPATCHER_METHOD(Type) \
-    static void on##Type(Arg& arg, const Type& op, const BakedOpState& state) { \
-        CustomClient client; client.on##Type(arg, op, state); \
+    static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
+        renderer.on##Type(op, state); \
     }
-    MAP_OPS(DISPATCHER_METHOD)
-
-    static Layer* startLayer(Arg& info, uint32_t width, uint32_t height) {
-        CustomClient client;
-        return client.startLayer(info, width, height);
-    }
-    static void endLayer(Arg& info) {
-        CustomClient client;
-        client.endLayer(info);
-    }
-    static void startFrame(Arg& info, uint32_t width, uint32_t height) {
-        CustomClient client;
-        client.startFrame(info, width, height);
-    }
-    static void endFrame(Arg& info) {
-        CustomClient client;
-        client.endFrame(info);
-    }
+    MAP_OPS(DISPATCHER_METHOD);
 };
 
-class Info {
-public:
-    int index = 0;
-};
 
-// Receiver class which will fail if it receives any ops
-class FailReceiver : public TestReceiver<FailReceiver, Info>::Client {};
+class FailRenderer : public TestRendererBase {};
 
-class SimpleReceiver : public TestReceiver<SimpleReceiver, Info>::Client {
+class SimpleTestRenderer : public TestRendererBase {
 public:
-    void startFrame(Info& info, uint32_t width, uint32_t height) override {
-        EXPECT_EQ(0, info.index++);
+    void startFrame(uint32_t width, uint32_t height) override {
+        EXPECT_EQ(0, mIndex++);
         EXPECT_EQ(100u, width);
         EXPECT_EQ(200u, height);
     }
-    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
-        EXPECT_EQ(1, info.index++);
+    void onRectOp(const RectOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(1, mIndex++);
     }
-    void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
-        EXPECT_EQ(2, info.index++);
+    void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(2, mIndex++);
     }
-    void endFrame(Info& info) override {
-        EXPECT_EQ(3, info.index++);
+    void endFrame() override {
+        EXPECT_EQ(3, mIndex++);
     }
 };
 TEST(OpReorderer, simple) {
@@ -111,9 +98,9 @@
     });
     OpReorderer reorderer(100, 200, *dl);
 
-    Info info;
-    reorderer.replayBakedOps<TestReceiver<SimpleReceiver, Info>>(info);
-    EXPECT_EQ(4, info.index); // 2 ops + start + end
+    SimpleTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
 }
 
 
@@ -126,19 +113,19 @@
     });
     OpReorderer reorderer(200, 200, *dl);
 
-    Info info;
-    reorderer.replayBakedOps<TestReceiver<FailReceiver, Info>>(info);
+    FailRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
 }
 
 
 static int SIMPLE_BATCHING_LOOPS = 5;
-class SimpleBatchingReceiver : public TestReceiver<SimpleBatchingReceiver, Info>::Client {
+class SimpleBatchingTestRenderer : public TestRendererBase {
 public:
-    void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
-        EXPECT_TRUE(info.index++ >= SIMPLE_BATCHING_LOOPS);
+    void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+        EXPECT_TRUE(mIndex++ >= SIMPLE_BATCHING_LOOPS);
     }
-    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
-        EXPECT_TRUE(info.index++ < SIMPLE_BATCHING_LOOPS);
+    void onRectOp(const RectOp& op, const BakedOpState& state) override {
+        EXPECT_TRUE(mIndex++ < SIMPLE_BATCHING_LOOPS);
     }
 };
 TEST(OpReorderer, simpleBatching) {
@@ -158,15 +145,15 @@
 
     OpReorderer reorderer(200, 200, *dl);
 
-    Info info;
-    reorderer.replayBakedOps<TestReceiver<SimpleBatchingReceiver, Info>>(info);
-    EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, info.index); // 2 x loops ops, because no merging (TODO: force no merging)
+    SimpleBatchingTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, renderer.getIndex()); // 2 x loops ops, because no merging (TODO: force no merging)
 }
 
-class RenderNodeReceiver : public TestReceiver<RenderNodeReceiver, Info>::Client {
+class RenderNodeTestRenderer : public TestRendererBase {
 public:
-    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
-        switch(info.index++) {
+    void onRectOp(const RectOp& op, const BakedOpState& state) override {
+        switch(mIndex++) {
         case 0:
             EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clippedBounds);
             EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
@@ -207,14 +194,14 @@
 
     OpReorderer reorderer(SkRect::MakeWH(200, 200), 200, 200, nodes);
 
-    Info info;
-    reorderer.replayBakedOps<TestReceiver<RenderNodeReceiver, Info>>(info);
+    RenderNodeTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
 }
 
-class ClippedReceiver : public TestReceiver<ClippedReceiver, Info>::Client {
+class ClippedTestRenderer : public TestRendererBase {
 public:
-    void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
-        EXPECT_EQ(0, info.index++);
+    void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(0, mIndex++);
         EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
         EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect);
         EXPECT_TRUE(state.computedState.transform.isIdentity());
@@ -232,24 +219,24 @@
     OpReorderer reorderer(SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
             200, 200, nodes);
 
-    Info info;
-    reorderer.replayBakedOps<TestReceiver<ClippedReceiver, Info>>(info);
+    ClippedTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
 }
 
 
-class SaveLayerSimpleReceiver : public TestReceiver<SaveLayerSimpleReceiver, Info>::Client {
+class SaveLayerSimpleTestRenderer : public TestRendererBase {
 public:
-    Layer* startLayer(Info& info, uint32_t width, uint32_t height) override {
-        EXPECT_EQ(0, info.index++);
+    OffscreenBuffer* startLayer(uint32_t width, uint32_t height) override {
+        EXPECT_EQ(0, mIndex++);
         EXPECT_EQ(180u, width);
         EXPECT_EQ(180u, height);
         return nullptr;
     }
-    void endLayer(Info& info) override {
-        EXPECT_EQ(2, info.index++);
+    void endLayer() override {
+        EXPECT_EQ(2, mIndex++);
     }
-    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
-        EXPECT_EQ(1, info.index++);
+    void onRectOp(const RectOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(1, mIndex++);
         EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
         EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
         EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
@@ -258,8 +245,8 @@
         expectedTransform.loadTranslate(-10, -10, 0);
         EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
     }
-    void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
-        EXPECT_EQ(3, info.index++);
+    void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(3, mIndex++);
         EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
         EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
         EXPECT_TRUE(state.computedState.transform.isIdentity());
@@ -274,9 +261,9 @@
 
     OpReorderer reorderer(200, 200, *dl);
 
-    Info info;
-    reorderer.replayBakedOps<TestReceiver<SaveLayerSimpleReceiver, Info>>(info);
-    EXPECT_EQ(4, info.index);
+    SaveLayerSimpleTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(4, renderer.getIndex());
 }
 
 
@@ -285,46 +272,46 @@
  * - startLayer1, rect1, drawLayer2, endLayer1
  * - startFrame, layerOp1, endFrame
  */
-class SaveLayerNestedReceiver : public TestReceiver<SaveLayerNestedReceiver, Info>::Client {
+class SaveLayerNestedTestRenderer : public TestRendererBase {
 public:
-    Layer* startLayer(Info& info, uint32_t width, uint32_t height) override {
-        const int index = info.index++;
+    OffscreenBuffer* startLayer(uint32_t width, uint32_t height) override {
+        const int index = mIndex++;
         if (index == 0) {
             EXPECT_EQ(400u, width);
             EXPECT_EQ(400u, height);
-            return (Layer*) 0x400;
+            return (OffscreenBuffer*) 0x400;
         } else if (index == 3) {
             EXPECT_EQ(800u, width);
             EXPECT_EQ(800u, height);
-            return (Layer*) 0x800;
+            return (OffscreenBuffer*) 0x800;
         } else { ADD_FAILURE(); }
-        return (Layer*) nullptr;
+        return (OffscreenBuffer*) nullptr;
     }
-    void endLayer(Info& info) override {
-        int index = info.index++;
+    void endLayer() override {
+        int index = mIndex++;
         EXPECT_TRUE(index == 2 || index == 6);
     }
-    void startFrame(Info& info, uint32_t width, uint32_t height) override {
-        EXPECT_EQ(7, info.index++);
+    void startFrame(uint32_t width, uint32_t height) override {
+        EXPECT_EQ(7, mIndex++);
     }
-    void endFrame(Info& info) override {
-        EXPECT_EQ(9, info.index++);
+    void endFrame() override {
+        EXPECT_EQ(9, mIndex++);
     }
-    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
-        const int index = info.index++;
+    void onRectOp(const RectOp& op, const BakedOpState& state) override {
+        const int index = mIndex++;
         if (index == 1) {
             EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
         } else if (index == 4) {
             EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
         } else { ADD_FAILURE(); }
     }
-    void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
-        const int index = info.index++;
+    void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+        const int index = mIndex++;
         if (index == 5) {
-            EXPECT_EQ((Layer*)0x400, *op.layerHandle);
+            EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
             EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
         } else if (index == 8) {
-            EXPECT_EQ((Layer*)0x800, *op.layerHandle);
+            EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
             EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
         } else { ADD_FAILURE(); }
     }
@@ -345,9 +332,9 @@
 
     OpReorderer reorderer(800, 800, *dl);
 
-    Info info;
-    reorderer.replayBakedOps<TestReceiver<SaveLayerNestedReceiver, Info>>(info);
-    EXPECT_EQ(10, info.index);
+    SaveLayerNestedTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(10, renderer.getIndex());
 }
 
 TEST(OpReorderer, saveLayerContentRejection) {
@@ -363,10 +350,10 @@
         canvas.restore();
     });
     OpReorderer reorderer(200, 200, *dl);
-    Info info;
 
+    FailRenderer renderer;
     // should see no ops, even within the layer, since the layer should be rejected
-    reorderer.replayBakedOps<TestReceiver<FailReceiver, Info>>(info);
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
 }
 
 } // namespace uirenderer
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index ba867e1..5476651 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -249,7 +249,7 @@
      */
     public @NonNull String getRoot() {
         if (!isConnected()) {
-            throw new IllegalStateException("getSessionToken() called while not connected (state="
+            throw new IllegalStateException("getRoot() called while not connected (state="
                     + getStateLabel(mState) + ")");
         }
         return mRootId;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index cf4c9cb..bf045f9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -34,14 +34,16 @@
  */
 class DockRegion {
     public static TaskStack.DockState[] PHONE_LANDSCAPE = {
+            // We only allow docking to the left for now on small devices
             TaskStack.DockState.LEFT
     };
     public static TaskStack.DockState[] PHONE_PORTRAIT = {
-            // We only allow docking to the top for now
+            // We only allow docking to the top for now on small devices
             TaskStack.DockState.TOP
     };
     public static TaskStack.DockState[] TABLET_LANDSCAPE = {
-            TaskStack.DockState.LEFT
+            TaskStack.DockState.LEFT,
+            TaskStack.DockState.RIGHT
     };
     public static TaskStack.DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dbbce49..dfe5751 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12106,7 +12106,8 @@
             AppsQueryHelper queryHelper = new AppsQueryHelper(mContext);
             Set<String> enableApps = new HashSet<>();
             enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
-                            | AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM,
+                            | AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM
+                            | AppsQueryHelper.GET_DEFAULT_IMES,
                             /* systemAppsOnly */ true, UserHandle.SYSTEM));
             ArraySet<String> wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps();
             enableApps.addAll(wlApps);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 62553cf..47995a7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5438,8 +5438,8 @@
                 if (visible) {
                     // TODO(multi-display): support multiple displays
                     if (mCircularDisplayMask == null) {
-                        int screenOffset = mContext.getResources().getDimensionPixelSize(
-                                com.android.internal.R.dimen.circular_display_mask_offset);
+                        int screenOffset = mContext.getResources().getInteger(
+                                com.android.internal.R.integer.config_windowOutsetBottom);
                         int maskThickness = mContext.getResources().getDimensionPixelSize(
                                 com.android.internal.R.dimen.circular_display_mask_thickness);