Merge "Fix empty region if the scale is smaller than 1"
diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md
index d5b2953..6ac17d8 100644
--- a/cmds/dumpstate/README.md
+++ b/cmds/dumpstate/README.md
@@ -26,6 +26,18 @@
 mmm -j frameworks/native/cmds/dumpstate && adb push ${OUT}/system/bin/dumpstate system/bin && adb shell am bug-report
 ```
 
+Make sure that the device is remounted before running the above command.
+* If you're working with `userdebug` variant, you may need to run the following to remount your device:
+
+  ```
+  adb root && adb remount -R && adb wait-for-device && adb root && adb remount
+  ```
+* If you're working with `eng` variant, you may need to run the following to remount your device:
+
+  ```
+  adb root && adb remount
+  ```
+
 ## To build, deploy, and run unit tests
 
 First create `/data/nativetest64`:
@@ -37,13 +49,13 @@
 Then run:
 
 ```
-mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest/dumpstate_test/dumpstate_test
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test
 ```
 
 And to run just one test (for example, `DumpstateTest.RunCommandNoArgs`):
 
 ```
-mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs
 ```
 
 ## To take quick bugreports
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index dc7288e..df80651 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1864,6 +1864,17 @@
             do_text_file = true;
         } else {
             do_text_file = false;
+            // If the user has changed the suffix, we need to change the zip file name.
+            std::string new_path = ds.GetPath(".zip");
+            if (ds.path_ != new_path) {
+                MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
+                if (rename(ds.path_.c_str(), new_path.c_str())) {
+                    MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
+                           strerror(errno));
+                } else {
+                    ds.path_ = new_path;
+                }
+            }
         }
     }
     if (do_text_file) {
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index c169b76..c70bc3e 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -45,6 +45,7 @@
         OpaqueFlagChange            opaque_flag             = 13;
         SecureFlagChange            secure_flag             = 14;
         DeferredTransactionChange   deferred_transaction    = 15;
+        CornerRadiusChange          corner_radius           = 16;
     }
 }
 
@@ -62,6 +63,10 @@
     required float alpha = 1;
 }
 
+message CornerRadiusChange {
+    required float corner_radius = 1;
+}
+
 message LayerChange {
     required uint32 layer = 1;
 }
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 6602546..384f21f 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -385,6 +385,9 @@
             case SurfaceChange::SurfaceChangeCase::kCrop:
                 setCrop(transaction, change.id(), change.crop());
                 break;
+            case SurfaceChange::SurfaceChangeCase::kCornerRadius:
+                setCornerRadius(transaction, change.id(), change.corner_radius());
+                break;
             case SurfaceChange::SurfaceChangeCase::kMatrix:
                 setMatrix(transaction, change.id(), change.matrix());
                 break;
@@ -489,6 +492,13 @@
     t.setCrop_legacy(mLayers[id], r);
 }
 
+void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t,
+        layer_id id, const CornerRadiusChange& cc) {
+    ALOGV("Layer %d: Setting Corner Radius -- cornerRadius=%d", id, cc.corner_radius());
+
+    t.setCornerRadius(mLayers[id], cc.corner_radius());
+}
+
 void Replayer::setMatrix(SurfaceComposerClient::Transaction& t,
         layer_id id, const MatrixChange& mc) {
     ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 68390d3..120dd9b 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -92,6 +92,8 @@
             layer_id id, const LayerChange& lc);
     void setCrop(SurfaceComposerClient::Transaction& t,
             layer_id id, const CropChange& cc);
+    void setCornerRadius(SurfaceComposerClient::Transaction& t,
+            layer_id id, const CornerRadiusChange& cc);
     void setMatrix(SurfaceComposerClient::Transaction& t,
             layer_id id, const MatrixChange& mc);
     void setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 13b960a..d6021c0 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -40,7 +40,11 @@
     <feature name="android.software.voice_recognizers" notLowRam="true" />
     <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
+    <feature name="android.software.input_methods" />
     <feature name="android.software.print" />
+    <feature name="android.software.companion_device_setup" />
+    <feature name="android.software.autofill" />
+    <feature name="android.software.cant_save_state" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 59d67f3..cfd2b40 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -769,7 +769,13 @@
     /** all apps */
     AKEYCODE_ALL_APPS = 284,
     /** refresh key */
-    AKEYCODE_REFRESH = 285
+    AKEYCODE_REFRESH = 285,
+    /** Thumbs up key. Apps can use this to let user upvote content. */
+    AKEYCODE_THUMBS_UP = 286,
+    /** Thumbs down key. Apps can use this to let user downvote content. */
+    AKEYCODE_THUMBS_DOWN = 287,
+    /** Consumed by system to switch current viewer profile. */
+    AKEYCODE_PROFILE_SWITCH = 288
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 6d072a3..59d16d1 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -325,6 +325,9 @@
     DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
     DEFINE_KEYCODE(ALL_APPS),
     DEFINE_KEYCODE(REFRESH),
+    DEFINE_KEYCODE(THUMBS_UP),
+    DEFINE_KEYCODE(THUMBS_DOWN),
+    DEFINE_KEYCODE(PROFILE_SWITCH),
 
     { nullptr, 0 }
 };
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 14ce4cb..d799c5f 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -39,8 +39,6 @@
         "libutils",
     ],
 
-    cpp_std: "c++17",
-
     version_script: "libbinder_ndk.map.txt",
 }
 
@@ -52,10 +50,12 @@
         "include_ndk/android/*.h",
     ],
     license: "NOTICE",
+    draft: true,
 }
 
 ndk_library {
     name: "libbinder_ndk",
     symbol_file: "libbinder_ndk.map.txt",
     first_version: "29",
+    draft: true,
 }
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index 67481cf..b29b6e7 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -22,7 +22,6 @@
     strip: {
         none: true,
     },
-    cpp_std: "c++17",
     cflags: [
         "-O0",
         "-g",
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 9f30060..407eecb 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -82,6 +82,7 @@
 
     memcpy(output.writeInplace(16 * sizeof(float)),
            colorTransform.asArray(), 16 * sizeof(float));
+    output.writeFloat(cornerRadius);
 
     if (output.writeVectorSize(listenerCallbacks) == NO_ERROR) {
         for (const auto& [listener, callbackIds] : listenerCallbacks) {
@@ -149,6 +150,7 @@
     }
 
     colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
+    cornerRadius = input.readFloat();
 
     int32_t listenersSize = input.readInt32();
     for (int32_t i = 0; i < listenersSize; i++) {
@@ -270,6 +272,10 @@
         what |= eCropChanged_legacy;
         crop_legacy = other.crop_legacy;
     }
+    if (other.what & eCornerRadiusChanged) {
+        what |= eCornerRadiusChanged;
+        cornerRadius = other.cornerRadius;
+    }
     if (other.what & eDeferTransaction_legacy) {
         what |= eDeferTransaction_legacy;
         barrierHandle_legacy = other.barrierHandle_legacy;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 41e5abb..9dfccc7 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -475,6 +475,18 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCornerRadius(
+        const sp<SurfaceControl>& sc, float cornerRadius) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eCornerRadiusChanged;
+    s->cornerRadius = cornerRadius;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
                                                                  const sp<IBinder>& handle,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index cdb2309..3cfee9e 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -82,6 +82,7 @@
         eColorTransformChanged = 0x10000000,
         eListenerCallbacksChanged = 0x20000000,
         eInputInfoChanged = 0x40000000,
+        eCornerRadiusChanged = 0x80000000,
     };
 
     layer_state_t()
@@ -97,6 +98,7 @@
             mask(0),
             reserved(0),
             crop_legacy(Rect::INVALID_RECT),
+            cornerRadius(0.0f),
             frameNumber_legacy(0),
             overrideScalingMode(-1),
             transform(0),
@@ -135,6 +137,7 @@
     uint8_t reserved;
     matrix22_t matrix;
     Rect crop_legacy;
+    float cornerRadius;
     sp<IBinder> barrierHandle_legacy;
     sp<IBinder> reparentHandle;
     uint64_t frameNumber_legacy;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index cf55b6b..7d05512 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -265,6 +265,7 @@
         Transaction& setMatrix(const sp<SurfaceControl>& sc,
                 float dsdx, float dtdx, float dtdy, float dsdy);
         Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
+        Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
         Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
         // Defers applying any changes made in this transaction until the Layer
         // identified by handle reaches the given frameNumber. If the Layer identified
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8cb8649..fc676f1 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -16,7 +16,6 @@
 
 cc_library {
     name: "libinput",
-    cpp_std: "c++17",
     host_supported: true,
     cflags: [
         "-Wall",
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
index 477e54e..139570a 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -38,7 +38,8 @@
         for (const auto& info : inputInfo) {
             info.write(data);
         }
-        remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply);
+        remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply,
+                IBinder::FLAG_ONEWAY);
     }
 
     virtual void registerInputChannel(const sp<InputChannel>& channel) {
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index fba319d..d847884 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -51,10 +51,6 @@
         "-Wno-unused-function",
     ],
 
-    cppflags: [
-        "-std=c++1z"
-    ],
-
     version_script: "libnativewindow.map.txt",
 
     srcs: [
diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp
index 6a40c6c..f5387f2 100644
--- a/libs/renderengine/Mesh.cpp
+++ b/libs/renderengine/Mesh.cpp
@@ -33,13 +33,15 @@
         return;
     }
 
-    size_t stride = vertexSize + texCoordSize;
+    const size_t CROP_COORD_SIZE = 2;
+    size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE;
     size_t remainder = (stride * vertexCount) / vertexCount;
     // Since all of the input parameters are unsigned, if stride is less than
     // either vertexSize or texCoordSize, it must have overflowed. remainder
     // will be equal to stride as long as stride * vertexCount doesn't overflow.
     if ((stride < vertexSize) || (remainder != stride)) {
-        ALOGE("Overflow in Mesh(..., %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize);
+        ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize,
+              CROP_COORD_SIZE);
         mVertices.resize(1);
         mVertices[0] = 0.0f;
         mVertexCount = 0;
@@ -71,6 +73,13 @@
     return mVertices.data() + mVertexSize;
 }
 
+float const* Mesh::getCropCoords() const {
+    return mVertices.data() + mVertexSize + mTexCoordsSize;
+}
+float* Mesh::getCropCoords() {
+    return mVertices.data() + mVertexSize + mTexCoordsSize;
+}
+
 size_t Mesh::getVertexCount() const {
     return mVertexCount;
 }
diff --git a/libs/renderengine/gl/GLES20RenderEngine.cpp b/libs/renderengine/gl/GLES20RenderEngine.cpp
index b10b52b..7adda83 100644
--- a/libs/renderengine/gl/GLES20RenderEngine.cpp
+++ b/libs/renderengine/gl/GLES20RenderEngine.cpp
@@ -626,16 +626,18 @@
 }
 
 void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque,
-                                            bool disableTexture, const half4& color) {
+                                            bool disableTexture, const half4& color,
+                                            float cornerRadius) {
     mState.isPremultipliedAlpha = premultipliedAlpha;
     mState.isOpaque = opaque;
     mState.color = color;
+    mState.cornerRadius = cornerRadius;
 
     if (disableTexture) {
         mState.textureEnabled = false;
     }
 
-    if (color.a < 1.0f || !opaque) {
+    if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
         glEnable(GL_BLEND);
         glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     } else {
@@ -703,6 +705,10 @@
     glDisable(GL_BLEND);
 }
 
+void GLES20RenderEngine::setupCornerRadiusCropSize(float width, float height) {
+    mState.cropSize = half2(width, height);
+}
+
 void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
     ATRACE_CALL();
     if (mesh.getTexCoordsSize()) {
@@ -714,6 +720,12 @@
     glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
                           mesh.getByteStride(), mesh.getPositions());
 
+    if (mState.cornerRadius > 0.0f) {
+        glEnableVertexAttribArray(Program::cropCoords);
+        glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
+                              mesh.getByteStride(), mesh.getCropCoords());
+    }
+
     // By default, DISPLAY_P3 is the only supported wide color output. However,
     // when HDR content is present, hardware composer may be able to handle
     // BT2020 data space, in that case, the output data space is set to be
@@ -831,6 +843,10 @@
     if (mesh.getTexCoordsSize()) {
         glDisableVertexAttribArray(Program::texCoords);
     }
+
+    if (mState.cornerRadius > 0.0f) {
+        glDisableVertexAttribArray(Program::cropCoords);
+    }
 }
 
 size_t GLES20RenderEngine::getMaxTextureSize() const {
diff --git a/libs/renderengine/gl/GLES20RenderEngine.h b/libs/renderengine/gl/GLES20RenderEngine.h
index 77dba62..a9f8cad 100644
--- a/libs/renderengine/gl/GLES20RenderEngine.h
+++ b/libs/renderengine/gl/GLES20RenderEngine.h
@@ -83,13 +83,14 @@
     void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
                                   ui::Transform::orientation_flags rotation) override;
     void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
-                            const half4& color) override;
+                            const half4& color, float cornerRadius) override;
     void setupLayerTexturing(const Texture& texture) override;
     void setupLayerBlackedOut() override;
     void setupFillWithColor(float r, float g, float b, float a) override;
     void setColorTransform(const mat4& colorTransform) override;
     void disableTexturing() override;
     void disableBlending() override;
+    void setupCornerRadiusCropSize(float width, float height) override;
 
     // HDR and color management related functions and state
     void setSourceY410BT2020(bool enable) override;
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index 7d2ea90..fe9d909 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -36,6 +36,7 @@
     glAttachShader(programId, fragmentId);
     glBindAttribLocation(programId, position, "position");
     glBindAttribLocation(programId, texCoords, "texCoords");
+    glBindAttribLocation(programId, cropCoords, "cropCoords");
     glLinkProgram(programId);
 
     GLint status;
@@ -66,6 +67,8 @@
         mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
         mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
         mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
+        mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
+        mCropCenterLoc = glGetUniformLocation(programId, "cropCenter");
 
         // set-up the default values for our uniforms
         glUseProgram(programId);
@@ -135,6 +138,12 @@
     if (mDisplayMaxLuminanceLoc >= 0) {
         glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
     }
+    if (mCornerRadiusLoc >= 0) {
+        glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
+    }
+    if (mCropCenterLoc >= 0) {
+        glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f);
+    }
     // these uniforms are always present
     glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray());
 }
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index 99bf0f0..bc9cf08 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -36,7 +36,16 @@
 class Program {
 public:
     // known locations for position and texture coordinates
-    enum { position = 0, texCoords = 1 };
+    enum {
+        /* position of each vertex for vertex shader */
+        position = 0,
+
+        /* UV coordinates for texture mapping */
+        texCoords = 1,
+
+        /* Crop coordinates, in pixels */
+        cropCoords = 2
+    };
 
     Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
     ~Program() = default;
@@ -85,6 +94,12 @@
     /* location of transform matrix */
     GLint mInputTransformMatrixLoc;
     GLint mOutputTransformMatrixLoc;
+
+    /* location of corner radius uniform */
+    GLint mCornerRadiusLoc;
+
+    /* location of surface crop origin uniform, for rounded corner clipping */
+    GLint mCropCenterLoc;
 };
 
 } // namespace gl
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 464fc15..d0916ad 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -79,7 +79,8 @@
 
 void ProgramCache::primeCache(bool useColorManagement) {
     uint32_t shaderCount = 0;
-    uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK;
+    uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK
+        | Key::ROUNDED_CORNERS_MASK;
     // Prime the cache for all combinations of the above masks,
     // leaving off the experimental color matrix mask options.
 
@@ -136,12 +137,15 @@
             .set(Key::OPACITY_MASK,
                  description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
             .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
-                 description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON
-                                                       : Key::INPUT_TRANSFORM_MATRIX_OFF)
+                 description.hasInputTransformMatrix()
+                         ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
             .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
                  description.hasOutputTransformMatrix() || description.hasColorMatrix()
                          ? Key::OUTPUT_TRANSFORM_MATRIX_ON
-                         : Key::OUTPUT_TRANSFORM_MATRIX_OFF);
+                         : Key::OUTPUT_TRANSFORM_MATRIX_OFF)
+            .set(Key::ROUNDED_CORNERS_MASK,
+                 description.cornerRadius > 0
+                         ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
 
     needs.set(Key::Y410_BT2020_MASK,
               description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
@@ -513,6 +517,10 @@
         vs << "attribute vec4 texCoords;"
            << "varying vec2 outTexCoords;";
     }
+    if (needs.hasRoundedCorners()) {
+        vs << "attribute lowp vec4 cropCoords;";
+        vs << "varying lowp vec2 outCropCoords;";
+    }
     vs << "attribute vec4 position;"
        << "uniform mat4 projection;"
        << "uniform mat4 texture;"
@@ -520,6 +528,9 @@
     if (needs.isTexturing()) {
         vs << "outTexCoords = (texture * texCoords).st;";
     }
+    if (needs.hasRoundedCorners()) {
+        vs << "outCropCoords = cropCoords.st;";
+    }
     vs << dedent << "}";
     return vs.getString();
 }
@@ -541,6 +552,27 @@
            << "varying vec2 outTexCoords;";
     }
 
+    if (needs.hasRoundedCorners()) {
+        // Rounded corners implementation using a signed distance function.
+        fs << R"__SHADER__(
+            uniform float cornerRadius;
+            uniform vec2 cropCenter;
+            varying vec2 outCropCoords;
+
+            /**
+             * This function takes the current crop coordinates and calculates an alpha value based
+             * on the corner radius and distance from the crop center.
+             */
+            float applyCornerRadius(vec2 cropCoords)
+            {
+                vec2 position = cropCoords - cropCenter;
+                vec2 dist = abs(position) + vec2(cornerRadius) - cropCenter;
+                float plane = length(max(dist, vec2(0.0)));
+                return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0);
+            }
+            )__SHADER__";
+    }
+
     if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
         fs << "uniform vec4 color;";
     }
@@ -639,6 +671,14 @@
         }
     }
 
+    if (needs.hasRoundedCorners()) {
+        if (needs.isPremultiplied()) {
+            fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));";
+        } else {
+            fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);";
+        }
+    }
+
     fs << dedent << "}";
     return fs.getString();
 }
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index d60fee6..653aaf0 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -78,31 +78,36 @@
             TEXTURE_EXT = 1 << TEXTURE_SHIFT,
             TEXTURE_2D = 2 << TEXTURE_SHIFT,
 
-            INPUT_TRANSFORM_MATRIX_SHIFT = 5,
+            ROUNDED_CORNERS_SHIFT = 5,
+            ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT,
+            ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT,
+            ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT,
+
+            INPUT_TRANSFORM_MATRIX_SHIFT = 6,
             INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
             INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT,
             INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
 
-            OUTPUT_TRANSFORM_MATRIX_SHIFT = 6,
+            OUTPUT_TRANSFORM_MATRIX_SHIFT = 7,
             OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
             OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
             OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
 
-            INPUT_TF_SHIFT = 7,
+            INPUT_TF_SHIFT = 8,
             INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
             INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
             INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
             INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
             INPUT_TF_HLG = 3 << INPUT_TF_SHIFT,
 
-            OUTPUT_TF_SHIFT = 9,
+            OUTPUT_TF_SHIFT = 10,
             OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
             OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
             OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
             OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
             OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT,
 
-            Y410_BT2020_SHIFT = 11,
+            Y410_BT2020_SHIFT = 12,
             Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
             Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
             Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
@@ -121,6 +126,9 @@
         inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
         inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
         inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
+        inline bool hasRoundedCorners() const {
+            return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
+        }
         inline bool hasInputTransformMatrix() const {
             return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
         }
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
index fe9022d..7618424 100644
--- a/libs/renderengine/include/renderengine/Mesh.h
+++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -62,14 +62,22 @@
         return VertexArray<TYPE>(getTexCoords(), mStride);
     }
 
+    template <typename TYPE>
+    VertexArray<TYPE> getCropCoordArray() {
+        return VertexArray<TYPE>(getCropCoords(), mStride);
+    }
+
     Primitive getPrimitive() const;
 
     // returns a pointer to the vertices positions
     float const* getPositions() const;
 
-    // returns a pointer to the vertices  texture coordinates
+    // returns a pointer to the vertices texture coordinates
     float const* getTexCoords() const;
 
+    // returns a pointer to the vertices crop coordinates
+    float const* getCropCoords() const;
+
     // number of vertices in this mesh
     size_t getVertexCount() const;
 
@@ -92,6 +100,7 @@
 
     float* getPositions();
     float* getTexCoords();
+    float* getCropCoords();
 
     std::vector<float> mVertices;
     size_t mVertexCount;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 22891c4..bb7f4df 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -114,10 +114,20 @@
     virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
                                           ui::Transform::orientation_flags rotation) = 0;
     virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
-                                    const half4& color) = 0;
+                                    const half4& color, float cornerRadius) = 0;
     virtual void setupLayerTexturing(const Texture& texture) = 0;
     virtual void setupLayerBlackedOut() = 0;
     virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
+    // Sets up the crop size for corner radius clipping.
+    //
+    // Having corner radius will force GPU composition on the layer and its children, drawing it
+    // with a special shader. The shader will receive the radius and the crop rectangle as input,
+    // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1.
+    // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop
+    // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
+    // in local layer coordinate space, so we have to take the layer transform into account when
+    // walking up the tree.
+    virtual void setupCornerRadiusCropSize(float width, float height) = 0;
 
     // Set a color transform matrix that is applied in linear space right before OETF.
     virtual void setColorTransform(const mat4& /* colorTransform */) = 0;
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index eadd656..bd2055f 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -50,6 +50,12 @@
     // whether this layer is marked as opaque
     bool isOpaque = true;
 
+    // corner radius of the layer
+    float cornerRadius = 0;
+
+    // Size of the rounded rectangle we are cropping to
+    half2 cropSize;
+
     // Texture this layer uses
     Texture texture;
     bool textureEnabled = false;
diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS
index d4393d6..81099e8 100644
--- a/libs/sensor/OWNERS
+++ b/libs/sensor/OWNERS
@@ -1,2 +1,3 @@
 arthuri@google.com
 bduddie@google.com
+bstack@google.com
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index b7ad4e5..00f30a6 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -53,6 +53,7 @@
     ],
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
+        "libcutils",
         "libhidlbase",
         "libhwbinder",
         "libpdx_default_transport",
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index d30636f..7c85e66 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -19,6 +19,7 @@
 #include <android/frameworks/bufferhub/1.0/IBufferClient.h>
 #include <android/frameworks/bufferhub/1.0/IBufferHub.h>
 #include <android/hardware_buffer.h>
+#include <cutils/native_handle.h>
 #include <gtest/gtest.h>
 #include <hidl/ServiceManagement.h>
 #include <hwbinder/IPCThreadState.h>
@@ -35,7 +36,7 @@
 const int kUsage = 0;
 const size_t kUserMetadataSize = 0;
 
-using dvr::BufferHubDefs::IsBufferGained;
+using dvr::BufferHubDefs::IsBufferReleased;
 using dvr::BufferHubDefs::kFirstClientBitMask;
 using dvr::BufferHubDefs::kMetadataHeaderSize;
 using frameworks::bufferhub::V1_0::BufferHubStatus;
@@ -118,9 +119,9 @@
     // We use client_state_mask() to tell those two instances apart.
     EXPECT_NE(bufferStateMask1, bufferStateMask2);
 
-    // Both buffer instances should be in gained state.
-    EXPECT_TRUE(IsBufferGained(b1->buffer_state()));
-    EXPECT_TRUE(IsBufferGained(b2->buffer_state()));
+    // Both buffer instances should be in released state currently.
+    EXPECT_TRUE(IsBufferReleased(b1->buffer_state()));
+    EXPECT_TRUE(IsBufferReleased(b2->buffer_state()));
 
     // TODO(b/112338294): rewrite test after migration
     return;
@@ -144,7 +145,7 @@
     EXPECT_TRUE(bufferHub->allocateBuffer(desc, kUserMetadataSize, callback).isOk());
 }
 
-TEST_F(BufferHubBufferTest, DuplicateBuffer) {
+TEST_F(BufferHubBufferTest, DuplicateAndImportBuffer) {
     // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
     sp<IBufferHub> bufferhub = IBufferHub::getService();
     ASSERT_NE(nullptr, bufferhub.get());
@@ -170,11 +171,63 @@
         token = outToken;
         ret = status;
     };
-    EXPECT_TRUE(client->duplicate(dup_cb).isOk());
+    ASSERT_TRUE(client->duplicate(dup_cb).isOk());
     EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
     ASSERT_NE(token.getNativeHandle(), nullptr);
     EXPECT_EQ(token->numInts, 1);
     EXPECT_EQ(token->numFds, 0);
+
+    sp<IBufferClient> client2;
+    IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
+        ret = status;
+        client2 = outClient;
+    };
+    ASSERT_TRUE(bufferhub->importBuffer(token, import_cb).isOk());
+    EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
+    EXPECT_NE(nullptr, client2.get());
+    // TODO(b/116681016): once BufferNode.id() is exposed via BufferHubBuffer, check origin.id =
+    // improted.id here.
+}
+
+// nullptr must not crash the service
+TEST_F(BufferHubBufferTest, ImportNullToken) {
+    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
+    sp<IBufferHub> bufferhub = IBufferHub::getService();
+    ASSERT_NE(nullptr, bufferhub.get());
+
+    hidl_handle nullToken;
+    sp<IBufferClient> client;
+    BufferHubStatus ret;
+    IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
+        client = outClient;
+        ret = status;
+    };
+    ASSERT_TRUE(bufferhub->importBuffer(nullToken, import_cb).isOk());
+    EXPECT_EQ(ret, BufferHubStatus::INVALID_TOKEN);
+    EXPECT_EQ(nullptr, client.get());
+}
+
+// This test has a very little chance to fail (number of existing tokens / 2 ^ 32)
+TEST_F(BufferHubBufferTest, ImportInvalidToken) {
+    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
+    sp<IBufferHub> bufferhub = IBufferHub::getService();
+    ASSERT_NE(nullptr, bufferhub.get());
+
+    native_handle_t* tokenHandle = native_handle_create(/*numFds=*/0, /*numInts=*/1);
+    tokenHandle->data[0] = 0;
+
+    hidl_handle invalidToken(tokenHandle);
+    sp<IBufferClient> client;
+    BufferHubStatus ret;
+    IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
+        client = outClient;
+        ret = status;
+    };
+    ASSERT_TRUE(bufferhub->importBuffer(invalidToken, import_cb).isOk());
+    EXPECT_EQ(ret, BufferHubStatus::INVALID_TOKEN);
+    EXPECT_EQ(nullptr, client.get());
+
+    native_handle_delete(tokenHandle);
 }
 
 } // namespace
diff --git a/libs/ui/tests/BufferHubMetadata_test.cpp b/libs/ui/tests/BufferHubMetadata_test.cpp
index 70f86b3..14422bf 100644
--- a/libs/ui/tests/BufferHubMetadata_test.cpp
+++ b/libs/ui/tests/BufferHubMetadata_test.cpp
@@ -17,7 +17,7 @@
 #include <gtest/gtest.h>
 #include <ui/BufferHubMetadata.h>
 
-using android::dvr::BufferHubDefs::IsBufferGained;
+using android::dvr::BufferHubDefs::IsBufferReleased;
 
 namespace android {
 namespace dvr {
@@ -52,19 +52,13 @@
   BufferHubDefs::MetadataHeader* mh1 = m1.metadata_header();
   EXPECT_NE(mh1, nullptr);
 
-  // TODO(b/111976433): Update this test once BufferHub state machine gets
-  // updated. In the old model, buffer starts in the gained state (i.e.
-  // valued 0). In the new model, buffer states in the released state.
-  EXPECT_TRUE(IsBufferGained(mh1->fence_state.load()));
+  EXPECT_TRUE(IsBufferReleased(mh1->buffer_state.load()));
 
   EXPECT_TRUE(m2.IsValid());
   BufferHubDefs::MetadataHeader* mh2 = m2.metadata_header();
   EXPECT_NE(mh2, nullptr);
 
-  // TODO(b/111976433): Update this test once BufferHub state machine gets
-  // updated. In the old model, buffer starts in the gained state (i.e.
-  // valued 0). In the new model, buffer states in the released state.
-  EXPECT_TRUE(IsBufferGained(mh2->fence_state.load()));
+  EXPECT_TRUE(IsBufferReleased(mh2->buffer_state.load()));
 }
 
 TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) {
diff --git a/libs/vr/libbufferhub/buffer_client_impl.cpp b/libs/vr/libbufferhub/buffer_client_impl.cpp
index 30cbb9f..efa9c28 100644
--- a/libs/vr/libbufferhub/buffer_client_impl.cpp
+++ b/libs/vr/libbufferhub/buffer_client_impl.cpp
@@ -26,14 +26,14 @@
   Parcel data, reply;
   status_t ret =
       data.writeInterfaceToken(IBufferClient::getInterfaceDescriptor());
-  if (ret != NO_ERROR) {
+  if (ret != OK) {
     ALOGE("BpBufferClient::isValid: failed to write into parcel; errno=%d",
           ret);
     return false;
   }
 
   ret = remote()->transact(IS_VALID, data, &reply);
-  if (ret == NO_ERROR) {
+  if (ret == OK) {
     return reply.readBool();
   } else {
     ALOGE("BpBufferClient::isValid: failed to transact; errno=%d", ret);
@@ -45,16 +45,16 @@
   Parcel data, reply;
   status_t ret =
       data.writeInterfaceToken(IBufferClient::getInterfaceDescriptor());
-  if (ret != NO_ERROR) {
+  if (ret != OK) {
     ALOGE("BpBufferClient::duplicate: failed to write into parcel; errno=%d",
           ret);
     return ret;
   }
 
   ret = remote()->transact(DUPLICATE, data, &reply);
-  if (ret == NO_ERROR) {
+  if (ret == OK) {
     *outToken = reply.readUint64();
-    return NO_ERROR;
+    return OK;
   } else {
     ALOGE("BpBufferClient::duplicate: failed to transact; errno=%d", ret);
     return ret;
@@ -72,7 +72,7 @@
       CHECK_INTERFACE(IBufferClient, data, reply);
       uint64_t token = 0;
       status_t ret = duplicate(&token);
-      if (ret != NO_ERROR) {
+      if (ret != OK) {
         return ret;
       }
       return reply->writeUint64(token);
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index 73ca69b..2d9a42b 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -23,11 +23,13 @@
 using android::sp;
 using android::dvr::ConsumerBuffer;
 using android::dvr::ProducerBuffer;
-using android::dvr::BufferHubDefs::IsBufferAcquired;
-using android::dvr::BufferHubDefs::IsBufferGained;
-using android::dvr::BufferHubDefs::IsBufferPosted;
+using android::dvr::BufferHubDefs::AnyClientAcquired;
+using android::dvr::BufferHubDefs::AnyClientGained;
+using android::dvr::BufferHubDefs::AnyClientPosted;
 using android::dvr::BufferHubDefs::IsBufferReleased;
-using android::dvr::BufferHubDefs::kConsumerStateMask;
+using android::dvr::BufferHubDefs::IsClientAcquired;
+using android::dvr::BufferHubDefs::IsClientPosted;
+using android::dvr::BufferHubDefs::IsClientReleased;
 using android::dvr::BufferHubDefs::kFirstClientBitMask;
 using android::dvr::BufferHubDefs::kMetadataHeaderSize;
 using android::pdx::LocalChannelHandle;
@@ -52,58 +54,49 @@
   std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<ConsumerBuffer> c =
+  std::unique_ptr<ConsumerBuffer> c1 =
       ConsumerBuffer::Import(p->CreateConsumer());
-  ASSERT_TRUE(c.get() != nullptr);
+  ASSERT_TRUE(c1.get() != nullptr);
   // Check that consumers can spawn other consumers.
   std::unique_ptr<ConsumerBuffer> c2 =
-      ConsumerBuffer::Import(c->CreateConsumer());
+      ConsumerBuffer::Import(c1->CreateConsumer());
   ASSERT_TRUE(c2.get() != nullptr);
 
-  // Producer state mask is unique, i.e. 1.
+  // Checks the state masks of client p, c1 and c2.
   EXPECT_EQ(p->client_state_mask(), kFirstClientBitMask);
-  // Consumer state mask cannot have producer bit on.
-  EXPECT_EQ(c->client_state_mask() & kFirstClientBitMask, 0U);
-  // Consumer state mask must be a single, i.e. power of 2.
-  EXPECT_NE(c->client_state_mask(), 0U);
-  EXPECT_EQ(c->client_state_mask() & (c->client_state_mask() - 1), 0U);
-  // Consumer state mask cannot have producer bit on.
-  EXPECT_EQ(c2->client_state_mask() & kFirstClientBitMask, 0U);
-  // Consumer state mask must be a single, i.e. power of 2.
-  EXPECT_NE(c2->client_state_mask(), 0U);
-  EXPECT_EQ(c2->client_state_mask() & (c2->client_state_mask() - 1), 0U);
-  // Each consumer should have unique bit.
-  EXPECT_EQ(c->client_state_mask() & c2->client_state_mask(), 0U);
+  EXPECT_EQ(c1->client_state_mask(), kFirstClientBitMask << 1);
+  EXPECT_EQ(c2->client_state_mask(), kFirstClientBitMask << 2);
 
   // Initial state: producer not available, consumers not available.
   EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-  EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
   EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
 
+  EXPECT_EQ(0, p->GainAsync());
   EXPECT_EQ(0, p->Post(LocalHandle()));
 
   // New state: producer not available, consumers available.
   EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-  EXPECT_EQ(1, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(1, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
   EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
 
   LocalHandle fence;
-  EXPECT_EQ(0, c->Acquire(&fence));
-  EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, c1->Acquire(&fence));
+  EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
   EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
 
   EXPECT_EQ(0, c2->Acquire(&fence));
   EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
-  EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
 
-  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_EQ(0, c1->Release(LocalHandle()));
   EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
   EXPECT_EQ(0, c2->Discard());
-
   EXPECT_EQ(1, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
   EXPECT_EQ(0, p->Gain(&fence));
   EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-  EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
   EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
 }
 
@@ -144,7 +137,8 @@
   // No events should be signaled initially.
   ASSERT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 0));
 
-  // Post the producer and check for consumer signal.
+  // Gain and post the producer and check for consumer signal.
+  EXPECT_EQ(0, p->GainAsync());
   EXPECT_EQ(0, p->Post({}));
   ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(),
                           kPollTimeoutMs));
@@ -189,7 +183,7 @@
     EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U);
     client_state_masks |= cs[i]->client_state_mask();
   }
-  EXPECT_EQ(client_state_masks, kFirstClientBitMask | kConsumerStateMask);
+  EXPECT_EQ(client_state_masks, ~0ULL);
 
   // The 64th creation will fail with out-of-memory error.
   auto state = p->CreateConsumer();
@@ -204,7 +198,6 @@
     // The released state mask will be reused.
     EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U);
     client_state_masks |= cs[i]->client_state_mask();
-    EXPECT_EQ(client_state_masks, kFirstClientBitMask | kConsumerStateMask);
   }
 }
 
@@ -217,24 +210,21 @@
   ASSERT_TRUE(c.get() != nullptr);
 
   LocalHandle fence;
+  EXPECT_EQ(0, p->GainAsync());
 
-  // The producer buffer starts in gained state.
-
-  // Acquire, release, and gain in gained state should fail.
+  // Acquire and gain in gained state should fail.
   EXPECT_EQ(-EBUSY, c->Acquire(&fence));
-  EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
   EXPECT_EQ(-EALREADY, p->Gain(&fence));
 
   // Post in gained state should succeed.
   EXPECT_EQ(0, p->Post(LocalHandle()));
 
-  // Post, release, and gain in posted state should fail.
+  // Post and gain in posted state should fail.
   EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
-  EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
   EXPECT_EQ(-EBUSY, p->Gain(&fence));
 
   // Acquire in posted state should succeed.
-  EXPECT_LE(0, c->Acquire(&fence));
+  EXPECT_EQ(0, c->Acquire(&fence));
 
   // Acquire, post, and gain in acquired state should fail.
   EXPECT_EQ(-EBUSY, c->Acquire(&fence));
@@ -245,17 +235,15 @@
   EXPECT_EQ(0, c->Release(LocalHandle()));
   EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
 
-  // Release, acquire, and post in released state should fail.
-  EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
+  // Acquire and post in released state should fail.
   EXPECT_EQ(-EBUSY, c->Acquire(&fence));
   EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
 
   // Gain in released state should succeed.
   EXPECT_EQ(0, p->Gain(&fence));
 
-  // Acquire, release, and gain in gained state should fail.
+  // Acquire and gain in gained state should fail.
   EXPECT_EQ(-EBUSY, c->Acquire(&fence));
-  EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
   EXPECT_EQ(-EALREADY, p->Gain(&fence));
 }
 
@@ -269,24 +257,21 @@
 
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
+  EXPECT_EQ(0, p->GainAsync());
 
-  // The producer buffer starts in gained state.
-
-  // Acquire, release, and gain in gained state should fail.
+  // Acquire and gain in gained state should fail.
   EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
-  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
   EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
 
   // Post in gained state should succeed.
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
   EXPECT_EQ(p->buffer_state(), c->buffer_state());
-  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+  EXPECT_TRUE(AnyClientPosted(p->buffer_state()));
 
-  // Post, release, and gain in posted state should fail.
+  // Post and gain in posted state should fail.
   EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
   EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
 
@@ -295,7 +280,7 @@
   EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
   EXPECT_EQ(p->buffer_state(), c->buffer_state());
-  EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+  EXPECT_TRUE(AnyClientAcquired(p->buffer_state()));
 
   // Acquire, post, and gain in acquired state should fail.
   EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
@@ -310,8 +295,7 @@
   EXPECT_EQ(p->buffer_state(), c->buffer_state());
   EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
 
-  // Release, acquire, and post in released state should fail.
-  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+  // Acquire and post in released state should fail.
   EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
   EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
@@ -320,12 +304,11 @@
   EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
   EXPECT_EQ(p->buffer_state(), c->buffer_state());
-  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+  EXPECT_TRUE(AnyClientGained(p->buffer_state()));
 
-  // Acquire, release, and gain in gained state should fail.
+  // Acquire and gain in gained state should fail.
   EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
-  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
   EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
 }
@@ -334,9 +317,12 @@
   std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-
-  // The producer buffer starts in gained state. Post the buffer.
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  ASSERT_EQ(0, p->GainAsync());
   ASSERT_EQ(0, p->Post(LocalHandle()));
+  ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
 
   // Gain in posted state should only succeed with gain_posted_buffer = true.
   LocalHandle invalid_fence;
@@ -348,9 +334,12 @@
   std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-
-  // The producer buffer starts in gained state. Post the buffer.
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  ASSERT_EQ(0, p->GainAsync());
   ASSERT_EQ(0, p->Post(LocalHandle()));
+  ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
 
   // GainAsync in posted state should only succeed with gain_posted_buffer
   // equals true.
@@ -360,54 +349,49 @@
   EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence, true));
 }
 
-TEST_F(LibBufferHubTest, TestZeroConsumer) {
+TEST_F(LibBufferHubTest, TestGainPostedBuffer_noConsumer) {
   std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
+  ASSERT_EQ(0, p->GainAsync());
+  ASSERT_EQ(0, p->Post(LocalHandle()));
+  // Producer state bit is in released state after post. The overall state of
+  // the buffer is also released because there is no consumer of this buffer.
+  ASSERT_TRUE(IsBufferReleased(p->buffer_state()));
 
-  DvrNativeBufferMetadata metadata;
+  // Gain in released state should succeed.
   LocalHandle invalid_fence;
-
-  // Newly created.
-  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
-  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
-
-  // The buffer should stay in posted stay until a consumer picks it up.
-  EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-
-  // A new consumer should still be able to acquire the buffer immediately.
-  std::unique_ptr<ConsumerBuffer> c =
-      ConsumerBuffer::Import(p->CreateConsumer());
-  ASSERT_TRUE(c.get() != nullptr);
-  EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
-  EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+  EXPECT_EQ(0, p->Gain(&invalid_fence, false));
 }
 
 TEST_F(LibBufferHubTest, TestMaxConsumers) {
   std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
+  uint64_t producer_state_mask = p->client_state_mask();
 
   std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs;
-  for (size_t i = 0; i < kMaxConsumerCount; i++) {
+  for (size_t i = 0; i < kMaxConsumerCount; ++i) {
     cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
     ASSERT_TRUE(cs[i].get() != nullptr);
-    EXPECT_TRUE(IsBufferGained(cs[i]->buffer_state()));
+    EXPECT_TRUE(IsBufferReleased(cs[i]->buffer_state()));
+    EXPECT_NE(producer_state_mask, cs[i]->client_state_mask());
   }
 
+  EXPECT_EQ(0, p->GainAsync());
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
 
   // Post the producer should trigger all consumers to be available.
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
-  for (size_t i = 0; i < kMaxConsumerCount; i++) {
+  EXPECT_TRUE(IsClientReleased(p->buffer_state(), p->client_state_mask()));
+  for (size_t i = 0; i < kMaxConsumerCount; ++i) {
     EXPECT_TRUE(
-        IsBufferPosted(cs[i]->buffer_state(), cs[i]->client_state_mask()));
+        IsClientPosted(cs[i]->buffer_state(), cs[i]->client_state_mask()));
     EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(kPollTimeoutMs)));
     EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence));
-    EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+    EXPECT_TRUE(
+        IsClientAcquired(p->buffer_state(), cs[i]->client_state_mask()));
   }
 
   // All consumers have to release before the buffer is considered to be
@@ -430,44 +414,57 @@
   std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+  EXPECT_EQ(0, p->GainAsync());
+  EXPECT_TRUE(AnyClientGained(p->buffer_state()));
 
   std::unique_ptr<ConsumerBuffer> c =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
-  EXPECT_TRUE(IsBufferGained(c->buffer_state()));
+  EXPECT_TRUE(AnyClientGained(c->buffer_state()));
 
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
 
   // Post the gained buffer should signal already created consumer.
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+  EXPECT_TRUE(AnyClientPosted(p->buffer_state()));
   EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
   EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
-  EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+  EXPECT_TRUE(AnyClientAcquired(c->buffer_state()));
 }
 
-TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) {
+TEST_F(LibBufferHubTest, TestCreateTheFirstConsumerAfterPostingBuffer) {
   std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+  EXPECT_EQ(0, p->GainAsync());
+  EXPECT_TRUE(AnyClientGained(p->buffer_state()));
 
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
 
   // Post the gained buffer before any consumer gets created.
+  // The buffer should be in released state because it is not expected to be
+  // read by any clients.
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+  EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
 
-  // Newly created consumer should be automatically sigalled.
+  // Newly created consumer will not be signalled for the posted buffer before
+  // its creation. It cannot acquire the buffer immediately.
   std::unique_ptr<ConsumerBuffer> c =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
-  EXPECT_TRUE(IsBufferPosted(c->buffer_state()));
+  EXPECT_FALSE(IsClientPosted(c->buffer_state(), c->client_state_mask()));
+  EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+
+  // Producer should be able to gain back and post the buffer
+  EXPECT_EQ(0, p->GainAsync());
+  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+
+  // Consumer should be able to pick up the buffer this time.
   EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
-  EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+  EXPECT_TRUE(IsClientAcquired(c->buffer_state(), c->client_state_mask()));
 }
 
 TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) {
@@ -479,6 +476,7 @@
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c1.get() != nullptr);
 
+  EXPECT_EQ(0, p->GainAsync());
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
 
@@ -503,7 +501,7 @@
 
   EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
   EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
-  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+  EXPECT_TRUE(AnyClientGained(p->buffer_state()));
 }
 
 TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
@@ -517,6 +515,7 @@
   std::unique_ptr<ConsumerBuffer> c =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
   Metadata m = {1, 3};
   EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(Metadata)));
   EXPECT_LE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
@@ -545,6 +544,7 @@
   std::unique_ptr<ConsumerBuffer> c =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   // It is illegal to post metadata larger than originally requested during
   // buffer allocation.
@@ -573,6 +573,7 @@
   std::unique_ptr<ConsumerBuffer> c =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   Metadata m = {1, 3};
   EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(m)));
@@ -598,6 +599,7 @@
   std::unique_ptr<ConsumerBuffer> c =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   int64_t sequence = 3;
   EXPECT_EQ(0, p->Post(LocalHandle(), &sequence, sizeof(sequence)));
@@ -613,6 +615,7 @@
   std::unique_ptr<ConsumerBuffer> c =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   LocalHandle fence;
 
@@ -627,6 +630,7 @@
   std::unique_ptr<ConsumerBuffer> c =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   int64_t sequence = 3;
   EXPECT_NE(0, p->Post(LocalHandle(), &sequence, sizeof(sequence)));
@@ -648,6 +652,7 @@
   std::unique_ptr<ConsumerBuffer> c =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   DvrNativeBufferMetadata meta;
   LocalHandle f1(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
@@ -711,44 +716,94 @@
   ASSERT_TRUE(c1.get() != nullptr);
   const uint64_t client_state_mask1 = c1->client_state_mask();
 
+  EXPECT_EQ(0, p->GainAsync());
   DvrNativeBufferMetadata meta;
   EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
 
   LocalHandle fence;
   EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
-  EXPECT_LE(0, c1->AcquireAsync(&meta, &fence));
-  // Destroy the consumer now will make it orphaned and the buffer is still
-  // acquired.
-  c1 = nullptr;
-  EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, c1->AcquireAsync(&meta, &fence));
 
+  // Destroy the consumer who has acquired but not released the buffer.
+  c1 = nullptr;
+
+  // The buffer is now available for the producer to gain.
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
+  // Newly added consumer is not able to acquire the buffer.
   std::unique_ptr<ConsumerBuffer> c2 =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c2.get() != nullptr);
   const uint64_t client_state_mask2 = c2->client_state_mask();
   EXPECT_NE(client_state_mask1, client_state_mask2);
+  EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(-EBUSY, c2->AcquireAsync(&meta, &fence));
 
-  // The new consumer is available for acquire.
+  // Producer should be able to gain.
+  EXPECT_EQ(0, p->GainAsync(&meta, &fence, false));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireLastPosted) {
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<ConsumerBuffer> c1 =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c1.get() != nullptr);
+  const uint64_t client_state_mask1 = c1->client_state_mask();
+
+  EXPECT_EQ(0, p->GainAsync());
+  DvrNativeBufferMetadata meta;
+  EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
+  EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
+
+  // c2 is created when the buffer is in posted state. buffer state for c1 is
+  // posted. Thus, c2 should be automatically set to posted and able to acquire.
+  std::unique_ptr<ConsumerBuffer> c2 =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c2.get() != nullptr);
+  const uint64_t client_state_mask2 = c2->client_state_mask();
+  EXPECT_NE(client_state_mask1, client_state_mask2);
   EXPECT_LT(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
-  EXPECT_LE(0, c2->AcquireAsync(&meta, &fence));
-  // Releasing the consumer makes the buffer gainable.
-  EXPECT_EQ(0, c2->ReleaseAsync(&meta, LocalHandle()));
+  LocalHandle invalid_fence;
+  EXPECT_EQ(0, c2->AcquireAsync(&meta, &invalid_fence));
 
-  // The buffer is now available for the producer to gain.
-  EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, c1->AcquireAsync(&meta, &invalid_fence));
 
-  // But if another consumer is created in released state.
+  // c3 is created when the buffer is in acquired state. buffer state for c1 and
+  // c2 are acquired. Thus, c3 should be automatically set to posted and able to
+  // acquire.
   std::unique_ptr<ConsumerBuffer> c3 =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c3.get() != nullptr);
   const uint64_t client_state_mask3 = c3->client_state_mask();
+  EXPECT_NE(client_state_mask1, client_state_mask3);
   EXPECT_NE(client_state_mask2, client_state_mask3);
-  // The consumer buffer is not acquirable.
-  EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
-  EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence));
+  EXPECT_LT(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, c3->AcquireAsync(&meta, &invalid_fence));
 
-  // Producer should be able to gain no matter what.
-  EXPECT_EQ(0, p->GainAsync(&meta, &fence));
+  // Releasing c2 and c3 in normal ways.
+  EXPECT_EQ(0, c2->Release(LocalHandle()));
+  EXPECT_EQ(0, c3->ReleaseAsync(&meta, LocalHandle()));
+
+  // Destroy the c1 who has not released the buffer.
+  c1 = nullptr;
+
+  // The buffer is now available for the producer to gain.
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
+  // C4 is created in released state. Thus, it cannot gain the just posted
+  // buffer.
+  std::unique_ptr<ConsumerBuffer> c4 =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c4.get() != nullptr);
+  const uint64_t client_state_mask4 = c4->client_state_mask();
+  EXPECT_NE(client_state_mask3, client_state_mask4);
+  EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &invalid_fence));
+
+  // Producer should be able to gain.
+  EXPECT_EQ(0, p->GainAsync(&meta, &invalid_fence));
 }
 
 TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) {
@@ -767,6 +822,7 @@
   int p_id = p->id();
 
   // Detach in posted state should fail.
+  EXPECT_EQ(0, p->GainAsync());
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
   EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
   auto s1 = p->Detach();
@@ -869,7 +925,8 @@
   ASSERT_TRUE(p1.get() != nullptr);
   int p1_id = p1->id();
 
-  // Detached the producer.
+  // Detached the producer from gained state.
+  EXPECT_EQ(0, p1->GainAsync());
   auto status_or_handle = p1->Detach();
   EXPECT_TRUE(status_or_handle.ok());
   LocalChannelHandle h1 = status_or_handle.take();
@@ -919,8 +976,8 @@
   EXPECT_NE(b1->client_state_mask(), b2->client_state_mask());
 
   // Both buffer instances should be in gained state.
-  EXPECT_TRUE(IsBufferGained(b1->buffer_state()));
-  EXPECT_TRUE(IsBufferGained(b2->buffer_state()));
+  EXPECT_TRUE(IsBufferReleased(b1->buffer_state()));
+  EXPECT_TRUE(IsBufferReleased(b2->buffer_state()));
 
   // TODO(b/112338294) rewrite test after migration
   return;
diff --git a/libs/vr/libbufferhub/buffer_hub_base.cpp b/libs/vr/libbufferhub/buffer_hub_base.cpp
index 68cc766..2dc427a 100644
--- a/libs/vr/libbufferhub/buffer_hub_base.cpp
+++ b/libs/vr/libbufferhub/buffer_hub_base.cpp
@@ -26,6 +26,8 @@
       cid_(-1) {}
 
 BufferHubBase::~BufferHubBase() {
+  // buffer_state and fence_state are not reset here. They will be used to
+  // clean up epoll fd if necessary in ProducerChannel::RemoveConsumer method.
   if (metadata_header_ != nullptr) {
     metadata_buffer_.Unlock();
   }
diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp
index 8e630ec..62fb5fd 100644
--- a/libs/vr/libbufferhub/consumer_buffer.cpp
+++ b/libs/vr/libbufferhub/consumer_buffer.cpp
@@ -35,17 +35,41 @@
   if (!out_meta)
     return -EINVAL;
 
-  // Only check producer bit and this consumer buffer's particular consumer bit.
-  // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit
-  // is not set.
-  uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
-  if (!BufferHubDefs::IsBufferPosted(buffer_state, client_state_mask())) {
-    ALOGE("ConsumerBuffer::LocalAcquire: not posted, id=%d state=%" PRIx64
-          " client_state_mask=%" PRIx64 ".",
-          id(), buffer_state, client_state_mask());
+  // The buffer can be acquired iff the buffer state for this client is posted.
+  uint64_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+                                     client_state_mask())) {
+    ALOGE(
+        "%s: Failed to acquire the buffer. The buffer is not posted, id=%d "
+        "state=%" PRIx64 " client_state_mask=%" PRIx64 ".",
+        __FUNCTION__, id(), current_buffer_state, client_state_mask());
     return -EBUSY;
   }
 
+  // Change the buffer state for this consumer from posted to acquired.
+  uint64_t updated_buffer_state = current_buffer_state ^ client_state_mask();
+  while (!buffer_state_->compare_exchange_weak(
+      current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+      std::memory_order_acquire)) {
+    ALOGD(
+        "%s Failed to acquire the buffer. Current buffer state was changed to "
+        "%" PRIx64
+        " when trying to acquire the buffer and modify the buffer state to "
+        "%" PRIx64 ". About to try again if the buffer is still posted.",
+        __FUNCTION__, current_buffer_state, updated_buffer_state);
+    if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+                                       client_state_mask())) {
+      ALOGE(
+          "%s: Failed to acquire the buffer. The buffer is no longer posted, "
+          "id=%d state=%" PRIx64 " client_state_mask=%" PRIx64 ".",
+          __FUNCTION__, id(), current_buffer_state, client_state_mask());
+      return -EBUSY;
+    }
+    // The failure of compare_exchange_weak updates current_buffer_state.
+    updated_buffer_state = current_buffer_state ^ client_state_mask();
+  }
+
   // Copy the canonical metadata.
   void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
   memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata));
@@ -64,8 +88,6 @@
     *out_fence = shared_acquire_fence_.Duplicate();
   }
 
-  // Set the consumer bit unique to this consumer.
-  BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, client_state_mask());
   return 0;
 }
 
@@ -118,12 +140,26 @@
   if (const int error = CheckMetadata(meta->user_metadata_size))
     return error;
 
-  // Check invalid state transition.
-  uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
-  if (!BufferHubDefs::IsBufferAcquired(buffer_state)) {
-    ALOGE("ConsumerBuffer::LocalRelease: not acquired id=%d state=%" PRIx64 ".",
-          id(), buffer_state);
-    return -EBUSY;
+  // Set the buffer state of this client to released if it is not already in
+  // released state.
+  uint64_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  if (BufferHubDefs::IsClientReleased(current_buffer_state,
+                                      client_state_mask())) {
+    return 0;
+  }
+  uint64_t updated_buffer_state = current_buffer_state & (~client_state_mask());
+  while (!buffer_state_->compare_exchange_weak(
+      current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+      std::memory_order_acquire)) {
+    ALOGD(
+        "%s: Failed to release the buffer. Current buffer state was changed to "
+        "%" PRIx64
+        " when trying to release the buffer and modify the buffer state to "
+        "%" PRIx64 ". About to try again.",
+        __FUNCTION__, current_buffer_state, updated_buffer_state);
+    // The failure of compare_exchange_weak updates current_buffer_state.
+    updated_buffer_state = current_buffer_state & (~client_state_mask());
   }
 
   // On release, only the user requested metadata is copied back into the shared
@@ -141,8 +177,6 @@
   if (const int error = UpdateSharedFence(release_fence, shared_release_fence_))
     return error;
 
-  // For release operation, the client don't need to change the state as it's
-  // bufferhubd's job to flip the produer bit once all consumers are released.
   return 0;
 }
 
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
index 650da97..400def7 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
@@ -20,52 +20,104 @@
 static constexpr uint32_t kMetadataUsage =
     GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
 
-// Single producuer multiple (up to 63) consumers ownership signal.
+// Single buffer clients (up to 32) ownership signal.
 // 64-bit atomic unsigned int.
+// Each client takes 2 bits. The first bit locates in the first 32 bits of
+// buffer_state; the second bit locates in the last 32 bits of buffer_state.
+// Client states:
+// Gained state 11. Exclusive write state.
+// Posted state 10.
+// Acquired state 01. Shared read state.
+// Released state 00.
 //
-// MSB           LSB
-//  |             |
-//  v             v
-// [C62|...|C1|C0|P]
-// Gain'ed state:     [..|0|0|0] -> Exclusively Writable.
-// Post'ed state:     [..|0|0|1]
-// Acquired'ed state: [..|X|X|1] -> At least one bit is set in higher 63 bits
-// Released'ed state: [..|X|X|0] -> At least one bit is set in higher 63 bits
-static constexpr int kMaxNumberOfClients = 64;
-static constexpr uint64_t kFirstClientBitMask = 1ULL;
-static constexpr uint64_t kConsumerStateMask = ~kFirstClientBitMask;
+//  MSB                        LSB
+//   |                          |
+//   v                          v
+// [C31|...|C1|C0|C31| ... |C1|C0]
 
-static inline void ModifyBufferState(std::atomic<uint64_t>* buffer_state,
-                                     uint64_t clear_mask, uint64_t set_mask) {
-  uint64_t old_state;
-  uint64_t new_state;
-  do {
-    old_state = buffer_state->load(std::memory_order_acquire);
-    new_state = (old_state & ~clear_mask) | set_mask;
-  } while (!buffer_state->compare_exchange_weak(old_state, new_state));
+// Maximum number of clients a buffer can have.
+static constexpr int kMaxNumberOfClients = 32;
+
+// Definition of bit masks.
+//  MSB                            LSB
+//   | kHighBitsMask | kLowbitsMask |
+//   v               v              v
+// [b63|   ...   |b32|b31|   ...  |b0]
+
+// The location of lower 32 bits in the 64-bit buffer state.
+static constexpr uint64_t kLowbitsMask = (1ULL << kMaxNumberOfClients) - 1ULL;
+
+// The location of higher 32 bits in the 64-bit buffer state.
+static constexpr uint64_t kHighBitsMask = ~kLowbitsMask;
+
+// The client bit mask of the first client.
+static constexpr uint64_t kFirstClientBitMask =
+    (1ULL << kMaxNumberOfClients) + 1ULL;
+
+// Returns true if any of the client is in gained state.
+static inline bool AnyClientGained(uint64_t state) {
+  uint64_t high_bits = state >> kMaxNumberOfClients;
+  uint64_t low_bits = state & kLowbitsMask;
+  return high_bits == low_bits && low_bits != 0ULL;
 }
 
-static inline bool IsBufferGained(uint64_t state) { return state == 0; }
-
-static inline bool IsBufferPosted(uint64_t state,
-                                  uint64_t consumer_bit = kConsumerStateMask) {
-  return (state & kFirstClientBitMask) && !(state & consumer_bit);
+// Returns true if the input client is in gained state.
+static inline bool IsClientGained(uint64_t state, uint64_t client_bit_mask) {
+  return state == client_bit_mask;
 }
 
-static inline bool IsBufferAcquired(uint64_t state) {
-  return (state & kFirstClientBitMask) && (state & kConsumerStateMask);
+// Returns true if any of the client is in posted state.
+static inline bool AnyClientPosted(uint64_t state) {
+  uint64_t high_bits = state >> kMaxNumberOfClients;
+  uint64_t low_bits = state & kLowbitsMask;
+  uint64_t posted_or_acquired = high_bits ^ low_bits;
+  return posted_or_acquired & high_bits;
 }
 
-static inline bool IsBufferReleased(uint64_t state) {
-  return !(state & kFirstClientBitMask) && (state & kConsumerStateMask);
+// Returns true if the input client is in posted state.
+static inline bool IsClientPosted(uint64_t state, uint64_t client_bit_mask) {
+  uint64_t client_bits = state & client_bit_mask;
+  if (client_bits == 0ULL)
+    return false;
+  uint64_t low_bits = client_bits & kLowbitsMask;
+  return low_bits == 0ULL;
 }
 
-static inline uint64_t FindNextAvailableClientStateMask(uint64_t bits) {
-  return ~bits - (~bits & (~bits - 1));
+// Return true if any of the client is in acquired state.
+static inline bool AnyClientAcquired(uint64_t state) {
+  uint64_t high_bits = state >> kMaxNumberOfClients;
+  uint64_t low_bits = state & kLowbitsMask;
+  uint64_t posted_or_acquired = high_bits ^ low_bits;
+  return posted_or_acquired & low_bits;
 }
 
-static inline uint64_t FindFirstClearedBit() {
-  return FindNextAvailableClientStateMask(kFirstClientBitMask);
+// Return true if the input client is in acquired state.
+static inline bool IsClientAcquired(uint64_t state, uint64_t client_bit_mask) {
+  uint64_t client_bits = state & client_bit_mask;
+  if (client_bits == 0ULL)
+    return false;
+  uint64_t high_bits = client_bits & kHighBitsMask;
+  return high_bits == 0ULL;
+}
+
+// Returns true if all clients are in released state.
+static inline bool IsBufferReleased(uint64_t state) { return state == 0ULL; }
+
+// Returns true if the input client is in released state.
+static inline bool IsClientReleased(uint64_t state, uint64_t client_bit_mask) {
+  return (state & client_bit_mask) == 0ULL;
+}
+
+// Returns the next available buffer client's client_state_masks.
+// @params union_bits. Union of all existing clients' client_state_masks.
+static inline uint64_t FindNextAvailableClientStateMask(uint64_t union_bits) {
+  uint64_t low_union = union_bits & kLowbitsMask;
+  if (low_union == kLowbitsMask)
+    return 0ULL;
+  uint64_t incremented = low_union + 1ULL;
+  uint64_t difference = incremented ^ low_union;
+  uint64_t new_low_bit = (difference + 1ULL) >> 1;
+  return new_low_bit + (new_low_bit << kMaxNumberOfClients);
 }
 
 struct __attribute__((packed, aligned(8))) MetadataHeader {
@@ -74,9 +126,21 @@
   // part is subject for future updates, it's not stable cross Android version,
   // so don't have it visible from outside of the Android platform (include Apps
   // and vendor HAL).
+
+  // Every client takes up one bit from the higher 32 bits and one bit from the
+  // lower 32 bits in buffer_state.
   std::atomic<uint64_t> buffer_state;
+
+  // Every client takes up one bit in fence_state. Only the lower 32 bits are
+  // valid. The upper 32 bits are there for easier manipulation, but the value
+  // should be ignored.
   std::atomic<uint64_t> fence_state;
+
+  // Every client takes up one bit from the higher 32 bits and one bit from the
+  // lower 32 bits in active_clients_bit_mask.
   std::atomic<uint64_t> active_clients_bit_mask;
+
+  // The index of the buffer queue where the buffer belongs to.
   uint64_t queue_index;
 
   // Public data format, which should be updated with caution. See more details
diff --git a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
index 7349779..7aa50b1 100644
--- a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
+++ b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
@@ -48,9 +48,8 @@
   // Asynchronously acquires a bufer.
   int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
 
-  // This should be called after a successful Acquire call. If the fence is
-  // valid the fence determines the buffer usage, otherwise the buffer is
-  // released immediately.
+  // Releases the buffer from any buffer state. If the fence is valid the fence
+  // determines the buffer usage, otherwise the buffer is released immediately.
   // This returns zero or a negative unix error code.
   int Release(const LocalHandle& release_fence);
   int ReleaseAsync();
diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
index 860f08a..ed38e7f 100644
--- a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -24,7 +24,7 @@
   IonBuffer& operator=(IonBuffer&& other) noexcept;
 
   // Returns check this IonBuffer holds a valid Gralloc buffer.
-  bool IsValid() const { return buffer_ && buffer_->initCheck() == NO_ERROR; }
+  bool IsValid() const { return buffer_ && buffer_->initCheck() == OK; }
 
   // Frees the underlying native handle and leaves the instance initialized to
   // empty.
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
index 1295531..1965410 100644
--- a/libs/vr/libbufferhub/ion_buffer.cpp
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -205,7 +205,7 @@
 
   status_t err =
       buffer_->lock(usage, Rect(x, y, x + width, y + height), address);
-  if (err != NO_ERROR)
+  if (err != OK)
     return -EINVAL;
   else
     return 0;
@@ -220,7 +220,7 @@
 
   status_t err =
       buffer_->lockYCbCr(usage, Rect(x, y, x + width, y + height), yuv);
-  if (err != NO_ERROR)
+  if (err != OK)
     return -EINVAL;
   else
     return 0;
@@ -231,7 +231,7 @@
   ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle());
 
   status_t err = buffer_->unlock();
-  if (err != NO_ERROR)
+  if (err != OK)
     return -EINVAL;
   else
     return 0;
diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp
index f36e169..1bfdc8f 100644
--- a/libs/vr/libbufferhub/producer_buffer.cpp
+++ b/libs/vr/libbufferhub/producer_buffer.cpp
@@ -79,14 +79,43 @@
   if (const int error = CheckMetadata(meta->user_metadata_size))
     return error;
 
-  // Check invalid state transition.
-  uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
-  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
-    ALOGE("ProducerBuffer::LocalPost: not gained, id=%d state=%" PRIx64 ".",
-          id(), buffer_state);
+  // The buffer can be posted iff the buffer state for this client is gained.
+  uint64_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  if (!BufferHubDefs::IsClientGained(current_buffer_state,
+                                     client_state_mask())) {
+    ALOGE("%s: not gained, id=%d state=%" PRIx64 ".", __FUNCTION__, id(),
+          current_buffer_state);
     return -EBUSY;
   }
 
+  // Set the producer client buffer state to released, other clients' buffer
+  // state to posted.
+  uint64_t current_active_clients_bit_mask =
+      active_clients_bit_mask_->load(std::memory_order_acquire);
+  uint64_t updated_buffer_state = current_active_clients_bit_mask &
+                                  (~client_state_mask()) &
+                                  BufferHubDefs::kHighBitsMask;
+  while (!buffer_state_->compare_exchange_weak(
+      current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+      std::memory_order_acquire)) {
+    ALOGD(
+        "%s: Failed to post the buffer. Current buffer state was changed to "
+        "%" PRIx64
+        " when trying to post the buffer and modify the buffer state to "
+        "%" PRIx64
+        ". About to try again if the buffer is still gained by this client.",
+        __FUNCTION__, current_buffer_state, updated_buffer_state);
+    if (!BufferHubDefs::IsClientGained(current_buffer_state,
+                                       client_state_mask())) {
+      ALOGE(
+          "%s: Failed to post the buffer. The buffer is no longer gained, "
+          "id=%d state=%" PRIx64 ".",
+          __FUNCTION__, id(), current_buffer_state);
+      return -EBUSY;
+    }
+  }
+
   // Copy the canonical metadata.
   void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
   memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata));
@@ -101,10 +130,6 @@
   if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_))
     return error;
 
-  // Set the producer bit atomically to transit into posted state.
-  // The producer state bit mask is kFirstClientBitMask for now.
-  BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL,
-                                   BufferHubDefs::kFirstClientBitMask);
   return 0;
 }
 
@@ -136,25 +161,50 @@
 
 int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta,
                               LocalHandle* out_fence, bool gain_posted_buffer) {
-  uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
-  ALOGD_IF(TRACE, "ProducerBuffer::LocalGain: buffer=%d, state=%" PRIx64 ".",
-           id(), buffer_state);
-
   if (!out_meta)
     return -EINVAL;
 
-  if (BufferHubDefs::IsBufferGained(buffer_state)) {
-    // We don't want to log error when gaining a newly allocated
-    // buffer.
-    ALOGI("ProducerBuffer::LocalGain: already gained id=%d.", id());
+  uint64_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  ALOGD_IF(TRACE, "%s: buffer=%d, state=%" PRIx64 ".", __FUNCTION__, id(),
+           current_buffer_state);
+
+  if (BufferHubDefs::IsClientGained(current_buffer_state,
+                                    client_state_mask())) {
+    ALOGI("%s: already gained id=%d.", __FUNCTION__, id());
     return -EALREADY;
   }
-  if (BufferHubDefs::IsBufferAcquired(buffer_state) ||
-      (BufferHubDefs::IsBufferPosted(buffer_state) && !gain_posted_buffer)) {
-    ALOGE("ProducerBuffer::LocalGain: not released id=%d state=%" PRIx64 ".",
-          id(), buffer_state);
+  if (BufferHubDefs::AnyClientAcquired(current_buffer_state) ||
+      (BufferHubDefs::AnyClientPosted(current_buffer_state) &&
+       !gain_posted_buffer)) {
+    ALOGE("%s: not released id=%d state=%" PRIx64 ".", __FUNCTION__, id(),
+          current_buffer_state);
     return -EBUSY;
   }
+  // Change the buffer state to gained state.
+  uint64_t updated_buffer_state = client_state_mask();
+  while (!buffer_state_->compare_exchange_weak(
+      current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+      std::memory_order_acquire)) {
+    ALOGD(
+        "%s: Failed to gain the buffer. Current buffer state was changed to "
+        "%" PRIx64
+        " when trying to gain the buffer and modify the buffer state to "
+        "%" PRIx64
+        ". About to try again if the buffer is still not read by other "
+        "clients.",
+        __FUNCTION__, current_buffer_state, updated_buffer_state);
+
+    if (BufferHubDefs::AnyClientAcquired(current_buffer_state) ||
+        (BufferHubDefs::AnyClientPosted(current_buffer_state) &&
+         !gain_posted_buffer)) {
+      ALOGE(
+          "%s: Failed to gain the buffer. The buffer is no longer released. "
+          "id=%d state=%" PRIx64 ".",
+          __FUNCTION__, id(), current_buffer_state);
+      return -EBUSY;
+    }
+  }
 
   // Canonical metadata is undefined on Gain. Except for user_metadata and
   // release_fence_mask. Fill in the user_metadata_ptr in address space of the
@@ -169,16 +219,20 @@
     out_meta->user_metadata_ptr = 0;
   }
 
-  uint64_t fence_state = fence_state_->load(std::memory_order_acquire);
+  uint64_t current_fence_state = fence_state_->load(std::memory_order_acquire);
+  uint64_t current_active_clients_bit_mask =
+      active_clients_bit_mask_->load(std::memory_order_acquire);
   // If there is an release fence from consumer, we need to return it.
-  if (fence_state & BufferHubDefs::kConsumerStateMask) {
+  // TODO(b/112007999) add an atomic variable in metadata header in shared
+  // memory to indicate which client is the last producer of the buffer.
+  // Currently, assume the first client is the only producer to the buffer.
+  if (current_fence_state & current_active_clients_bit_mask &
+      (~BufferHubDefs::kFirstClientBitMask)) {
     *out_fence = shared_release_fence_.Duplicate();
     out_meta->release_fence_mask =
-        fence_state & BufferHubDefs::kConsumerStateMask;
+        current_fence_state & current_active_clients_bit_mask;
   }
 
-  // Clear out all bits and the buffer is now back to gained state.
-  buffer_state_->store(0ULL);
   return 0;
 }
 
@@ -232,7 +286,8 @@
   // TODO(b/112338294) Keep here for reference. Remove it after new logic is
   // written.
   /* uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
-  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+  if (!BufferHubDefs::IsClientGained(
+      buffer_state, BufferHubDefs::kFirstClientStateMask)) {
     // Can only detach a ProducerBuffer when it's in gained state.
     ALOGW("ProducerBuffer::Detach: The buffer (id=%d, state=0x%" PRIx64
           ") is not in gained state.",
diff --git a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
index b2b4d7a..b6813eb 100644
--- a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
+++ b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
@@ -66,7 +66,7 @@
         reply->writeStrongBinder(
             IGraphicBufferProducer::asBinder(new_queue->producer));
         buffer_queues_.push_back(new_queue);
-        return NO_ERROR;
+        return OK;
       }
       default:
         return UNKNOWN_TRANSACTION;
@@ -89,7 +89,7 @@
                                                    /*waitForFence=*/false);
       }
 
-      if (ret != NO_ERROR) {
+      if (ret != OK) {
         LOG(ERROR) << "Failed to acquire next buffer.";
         return;
       }
@@ -99,7 +99,7 @@
         ret = buffer_item_consumer_->releaseBuffer(buffer);
       }
 
-      if (ret != NO_ERROR) {
+      if (ret != OK) {
         LOG(ERROR) << "Failed to release buffer.";
         return;
       }
@@ -171,14 +171,14 @@
     Parcel data;
     Parcel reply;
     int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply);
-    if (error != NO_ERROR) {
+    if (error != OK) {
       LOG(ERROR) << "Failed to get buffer queue over binder.";
       return nullptr;
     }
 
     sp<IBinder> binder;
     error = reply.readNullableStrongBinder(&binder);
-    if (error != NO_ERROR) {
+    if (error != OK) {
       LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
       return nullptr;
     }
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index f7942d0..9c4f73f 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -532,7 +532,8 @@
 Status<size_t> ProducerQueue::InsertBuffer(
     const std::shared_ptr<BufferProducer>& buffer) {
   if (buffer == nullptr ||
-      !BufferHubDefs::IsBufferGained(buffer->buffer_state())) {
+      !BufferHubDefs::IsClientGained(buffer->buffer_state(),
+                                     buffer->client_state_mask())) {
     ALOGE(
         "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
         "gained state.");
@@ -637,7 +638,7 @@
             static_cast<int>(*slot));
       return ErrorStatus(EIO);
     }
-    if (!BufferHubDefs::IsBufferAcquired(buffer->buffer_state())) {
+    if (!BufferHubDefs::AnyClientAcquired(buffer->buffer_state())) {
       *slot = *iter;
       unavailable_buffers_slot_.erase(iter);
       unavailable_buffers_slot_.push_back(*slot);
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
index 2cd7c45..f705749 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
@@ -35,7 +35,7 @@
   }
 
   status_t res = parcel->writeUint32(Magic);
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic.");
     return res;
   }
@@ -53,10 +53,10 @@
   }
 
   uint32_t out_magic = 0;
-  status_t res = NO_ERROR;
+  status_t res = OK;
 
   res = parcel->readUint32(&out_magic);
-  if (res != NO_ERROR)
+  if (res != OK)
     return res;
 
   if (out_magic != Magic) {
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
index c58f55f..fd6ca43 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -112,7 +112,7 @@
 
     // Consumer acquires a buffer.
     auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(c1_status.ok());
+    EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
     auto c1 = c1_status.take();
     ASSERT_NE(c1, nullptr);
     EXPECT_EQ(mi.index, i);
@@ -334,6 +334,7 @@
   std::shared_ptr<BufferProducer> p1 = BufferProducer::Create(
       kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0);
   ASSERT_TRUE(p1 != nullptr);
+  ASSERT_EQ(p1->GainAsync(), 0);
 
   // Inserting a posted buffer will fail.
   DvrNativeBufferMetadata meta;
@@ -345,9 +346,10 @@
   // Inserting a gained buffer will succeed.
   std::shared_ptr<BufferProducer> p2 = BufferProducer::Create(
       kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage);
+  ASSERT_EQ(p2->GainAsync(), 0);
   ASSERT_TRUE(p2 != nullptr);
   status_or_slot = producer_queue_->InsertBuffer(p2);
-  EXPECT_TRUE(status_or_slot.ok());
+  EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage();
   // This is the first buffer inserted, should take slot 0.
   size_t slot = status_or_slot.get();
   EXPECT_EQ(slot, 0);
@@ -585,7 +587,7 @@
     mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
     EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
     auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(c1_status.ok());
+    EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
     auto c1 = c1_status.take();
     ASSERT_NE(c1, nullptr);
 
@@ -689,7 +691,7 @@
 
   size_t cs1, cs2;
   auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence);
-  ASSERT_TRUE(c1_status.ok());
+  ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
   auto c1 = c1_status.take();
   ASSERT_NE(c1, nullptr);
   ASSERT_EQ(consumer_queue_->count(), 0U);
@@ -905,7 +907,7 @@
     ASSERT_NE(producer_buffer, nullptr);
     ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
     consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(consumer_status.ok());
+    ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage();
   }
 
   status = producer_queue_->FreeAllBuffers();
@@ -948,7 +950,7 @@
   Parcel parcel;
   status_t res;
   res = output_parcelable.writeToParcel(&parcel);
-  EXPECT_EQ(res, NO_ERROR);
+  EXPECT_EQ(res, OK);
 
   // After written into parcelable, the output_parcelable is still valid has
   // keeps the producer channel alive.
@@ -970,7 +972,7 @@
   EXPECT_FALSE(input_parcelable.IsValid());
 
   res = input_parcelable.readFromParcel(&parcel);
-  EXPECT_EQ(res, NO_ERROR);
+  EXPECT_EQ(res, OK);
   EXPECT_TRUE(input_parcelable.IsValid());
 
   EXPECT_EQ(producer_queue_, nullptr);
@@ -999,7 +1001,7 @@
 
   // Make sure the buffer can be dequeued from consumer side.
   auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
-  EXPECT_TRUE(s4.ok());
+  EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage();
   EXPECT_EQ(consumer_queue_->capacity(), 1U);
 
   auto consumer = s4.take();
@@ -1040,7 +1042,7 @@
   EXPECT_FALSE(input_parcelable.IsValid());
 
   res = input_parcelable.readFromParcel(&parcel);
-  EXPECT_EQ(res, NO_ERROR);
+  EXPECT_EQ(res, OK);
   EXPECT_TRUE(input_parcelable.IsValid());
 
   consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle());
@@ -1066,7 +1068,7 @@
 
   // Make sure the buffer can be dequeued from consumer side.
   auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
-  EXPECT_TRUE(s3.ok());
+  EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage();
   EXPECT_EQ(consumer_queue_->capacity(), 1U);
 
   auto consumer = s3.take();
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
index 4f10f83..8cc7081 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -108,8 +108,8 @@
   void ConnectProducer() {
     IGraphicBufferProducer::QueueBufferOutput output;
     // Can connect the first time.
-    ASSERT_EQ(NO_ERROR, mProducer->connect(kDummyListener, kTestApi,
-                                           kTestControlledByApp, &output));
+    ASSERT_EQ(OK, mProducer->connect(kDummyListener, kTestApi,
+                                     kTestControlledByApp, &output));
   }
 
   // Dequeue a buffer in a 'correct' fashion.
@@ -170,7 +170,7 @@
 TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) {
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
-  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
 }
 
 TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) {
@@ -186,26 +186,24 @@
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
   int32_t value = -1;
-  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
   EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value));
 
-  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
   EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value));
 
-  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
   EXPECT_EQ(kDefaultFormat, value);
 
-  EXPECT_EQ(NO_ERROR,
-            mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
   EXPECT_LE(0, value);
   EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value);
 
-  EXPECT_EQ(NO_ERROR,
+  EXPECT_EQ(OK,
             mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value));
   EXPECT_FALSE(value);  // Can't run behind when we haven't touched the queue
 
-  EXPECT_EQ(NO_ERROR,
-            mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
   EXPECT_EQ(kDefaultConsumerUsageBits, value);
 }
 
@@ -243,14 +241,14 @@
 
   // Request the buffer (pre-requisite for queueing)
   sp<GraphicBuffer> buffer;
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   // A generic "valid" input
   IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
   IGraphicBufferProducer::QueueBufferOutput output;
 
   // Queue the buffer back into the BQ
-  ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+  ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
 
   EXPECT_EQ(kDefaultWidth, output.width);
   EXPECT_EQ(kDefaultHeight, output.height);
@@ -313,7 +311,7 @@
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
 
   sp<GraphicBuffer> buffer;
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   sp<Fence> nullFence = NULL;
 
@@ -332,7 +330,7 @@
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
 
   sp<GraphicBuffer> buffer;
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   IGraphicBufferProducer::QueueBufferInput input =
       QueueBufferInputBuilder().setScalingMode(-1).build();
@@ -353,7 +351,7 @@
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
 
   sp<GraphicBuffer> buffer;
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   IGraphicBufferProducer::QueueBufferInput input =
       QueueBufferInputBuilder()
@@ -372,7 +370,7 @@
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
 
   // Should be able to cancel buffer after a dequeue.
-  EXPECT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+  EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
 }
 
 TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
@@ -380,16 +378,15 @@
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
   int minUndequeuedBuffers;
-  ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                                       &minUndequeuedBuffers));
+  ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                                 &minUndequeuedBuffers));
 
   const int minBuffers = 1;
   const int maxBuffers =
       BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
 
-  ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
-      << "async mode: " << false;
-  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(minBuffers))
+  ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
+  ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(minBuffers))
       << "bufferCount: " << minBuffers;
 
   // Should now be able to dequeue up to minBuffers times
@@ -399,14 +396,14 @@
     ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
   }
 
-  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers));
+  ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers));
 
   // queue the first buffer to enable max dequeued buffer count checking
   IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
   IGraphicBufferProducer::QueueBufferOutput output;
   sp<GraphicBuffer> buffer;
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
-  ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
 
   sp<Fence> fence;
   for (int i = 0; i < maxBuffers; ++i) {
@@ -414,25 +411,24 @@
   }
 
   // Cancel a buffer, so we can decrease the buffer count
-  ASSERT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+  ASSERT_EQ(OK, mProducer->cancelBuffer(slot, fence));
 
   // Should now be able to decrease the max dequeued count by 1
-  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
+  ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
 }
 
 TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) {
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
   int minUndequeuedBuffers;
-  ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                                       &minUndequeuedBuffers));
+  ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                                 &minUndequeuedBuffers));
 
   const int minBuffers = 1;
   const int maxBuffers =
       BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
 
-  ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
-      << "async mode: " << false;
+  ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
   // Buffer count was out of range
   EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0))
       << "bufferCount: " << 0;
@@ -440,7 +436,7 @@
       << "bufferCount: " << maxBuffers + 1;
 
   // Set max dequeue count to 2
-  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(2));
+  ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
   // Dequeue 2 buffers
   int slot = -1;
   sp<Fence> fence;
@@ -478,7 +474,7 @@
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
 
   // Shouldn't be able to request buffer after disconnect.
-  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
   ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer));
 }
 
@@ -489,14 +485,14 @@
 
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   // A generic "valid" input
   IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
   IGraphicBufferProducer::QueueBufferOutput output;
 
   // Shouldn't be able to queue buffer after disconnect.
-  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
   ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output));
 }
 
@@ -507,10 +503,10 @@
 
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   // Shouldn't be able to cancel buffer after disconnect.
-  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
   ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE));
 }
 
@@ -524,32 +520,32 @@
 
   constexpr int maxDequeuedBuffers = 1;
   int minUndequeuedBuffers;
-  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                                       &minUndequeuedBuffers));
-  EXPECT_EQ(NO_ERROR, mProducer->setAsyncMode(false));
-  EXPECT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                                 &minUndequeuedBuffers));
+  EXPECT_EQ(OK, mProducer->setAsyncMode(false));
+  EXPECT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
 
   int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers;
 
   // Dequeue, request, and queue all buffers.
   for (int i = 0; i < maxCapacity; i++) {
     EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-    EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
-    EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+    EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+    EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
   }
 
   // Disconnect then reconnect.
-  EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
   EXPECT_NO_FATAL_FAILURE(ConnectProducer());
 
   // Dequeue, request, and queue all buffers.
   for (int i = 0; i < maxCapacity; i++) {
     EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-    EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
-    EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+    EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+    EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
   }
 
-  EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
 }
 
 TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) {
@@ -568,21 +564,21 @@
   EXPECT_TRUE(dummy_producer_parcelable.IsValid());
 
   // Disconnect producer can be taken out, but only to an invalid parcelable.
-  ASSERT_EQ(mProducer->disconnect(kTestApi), NO_ERROR);
+  ASSERT_EQ(mProducer->disconnect(kTestApi), OK);
   EXPECT_EQ(mProducer->TakeAsParcelable(&dummy_producer_parcelable), BAD_VALUE);
   EXPECT_FALSE(producer_parcelable.IsValid());
-  EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), NO_ERROR);
+  EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK);
   EXPECT_TRUE(producer_parcelable.IsValid());
 
   // Should still be able to query buffer dimension after disconnect.
   int32_t value = -1;
-  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
   EXPECT_EQ(static_cast<uint32_t>(value), kDefaultWidth);
 
-  EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), NO_ERROR);
+  EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), OK);
   EXPECT_EQ(static_cast<uint32_t>(value), kDefaultHeight);
 
-  EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), NO_ERROR);
+  EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), OK);
   EXPECT_EQ(value, kDefaultFormat);
 
   // But connect to API will fail.
@@ -598,7 +594,7 @@
   ASSERT_TRUE(new_producer != nullptr);
   EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi,
                                   kTestControlledByApp, &output),
-            NO_ERROR);
+            OK);
 }
 
 }  // namespace
diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp
index 43b1196..4668b98 100644
--- a/libs/vr/libdisplay/vsync_service.cpp
+++ b/libs/vr/libdisplay/vsync_service.cpp
@@ -13,12 +13,12 @@
       CHECK_INTERFACE(IVsyncCallback, data, reply);
       int64_t vsync_timestamp = 0;
       status_t result = data.readInt64(&vsync_timestamp);
-      if (result != NO_ERROR) {
+      if (result != OK) {
         ALOGE("onVsync failed to readInt64: %d", result);
         return result;
       }
       onVsync(vsync_timestamp);
-      return NO_ERROR;
+      return OK;
     }
     default: {
       return BBinder::onTransact(code, data, reply, flags);
@@ -36,18 +36,18 @@
     Parcel data, reply;
     status_t result = data.writeInterfaceToken(
         IVsyncCallback::getInterfaceDescriptor());
-    if (result != NO_ERROR) {
+    if (result != OK) {
       ALOGE("onVsync failed to writeInterfaceToken: %d", result);
       return result;
     }
     result = data.writeInt64(vsync_timestamp);
-    if (result != NO_ERROR) {
+    if (result != OK) {
       ALOGE("onVsync failed to writeInt64: %d", result);
       return result;
     }
     result = remote()->transact(
         BnVsyncCallback::ON_VSYNC, data, &reply, TF_ONE_WAY);
-    if (result != NO_ERROR) {
+    if (result != OK) {
       ALOGE("onVsync failed to transact: %d", result);
       return result;
     }
@@ -65,23 +65,23 @@
       CHECK_INTERFACE(IVsyncService, data, reply);
       sp<IBinder> callback;
       status_t result = data.readStrongBinder(&callback);
-      if (result != NO_ERROR) {
+      if (result != OK) {
         ALOGE("registerCallback failed to readStrongBinder: %d", result);
         return result;
       }
       registerCallback(interface_cast<IVsyncCallback>(callback));
-      return NO_ERROR;
+      return OK;
     }
     case UNREGISTER_CALLBACK: {
       CHECK_INTERFACE(IVsyncService, data, reply);
       sp<IBinder> callback;
       status_t result = data.readStrongBinder(&callback);
-      if (result != NO_ERROR) {
+      if (result != OK) {
         ALOGE("unregisterCallback failed to readStrongBinder: %d", result);
         return result;
       }
       unregisterCallback(interface_cast<IVsyncCallback>(callback));
-      return NO_ERROR;
+      return OK;
     }
     default: {
       return BBinder::onTransact(code, data, reply, flags);
@@ -99,18 +99,18 @@
     Parcel data, reply;
     status_t result = data.writeInterfaceToken(
         IVsyncService::getInterfaceDescriptor());
-    if (result != NO_ERROR) {
+    if (result != OK) {
       ALOGE("registerCallback failed to writeInterfaceToken: %d", result);
       return result;
     }
     result = data.writeStrongBinder(IInterface::asBinder(callback));
-    if (result != NO_ERROR) {
+    if (result != OK) {
       ALOGE("registerCallback failed to writeStrongBinder: %d", result);
       return result;
     }
     result = remote()->transact(
         BnVsyncService::REGISTER_CALLBACK, data, &reply);
-    if (result != NO_ERROR) {
+    if (result != OK) {
       ALOGE("registerCallback failed to transact: %d", result);
       return result;
     }
@@ -121,18 +121,18 @@
     Parcel data, reply;
     status_t result = data.writeInterfaceToken(
         IVsyncService::getInterfaceDescriptor());
-    if (result != NO_ERROR) {
+    if (result != OK) {
       ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result);
       return result;
     }
     result = data.writeStrongBinder(IInterface::asBinder(callback));
-    if (result != NO_ERROR) {
+    if (result != OK) {
       ALOGE("unregisterCallback failed to writeStrongBinder: %d", result);
       return result;
     }
     result = remote()->transact(
         BnVsyncService::UNREGISTER_CALLBACK, data, &reply);
-    if (result != NO_ERROR) {
+    if (result != OK) {
       ALOGE("unregisterCallback failed to transact: %d", result);
       return result;
     }
diff --git a/libs/vr/libpdx_uds/channel_parcelable.cpp b/libs/vr/libpdx_uds/channel_parcelable.cpp
index e7bce27..5156846 100644
--- a/libs/vr/libpdx_uds/channel_parcelable.cpp
+++ b/libs/vr/libpdx_uds/channel_parcelable.cpp
@@ -36,7 +36,7 @@
 }
 
 status_t ChannelParcelable::writeToParcel(Parcel* parcel) const {
-  status_t res = NO_ERROR;
+  status_t res = OK;
 
   if (!IsValid()) {
     ALOGE("ChannelParcelable::writeToParcel: Invalid channel parcel.");
@@ -44,20 +44,20 @@
   }
 
   res = parcel->writeUint32(kUdsMagicParcelHeader);
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE("ChannelParcelable::writeToParcel: Cannot write magic: res=%d.", res);
     return res;
   }
 
   res = parcel->writeFileDescriptor(data_fd_.Get());
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE("ChannelParcelable::writeToParcel: Cannot write data fd: res=%d.",
           res);
     return res;
   }
 
   res = parcel->writeFileDescriptor(pollin_event_fd_.Get());
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE(
         "ChannelParcelable::writeToParcel: Cannot write pollin event fd: "
         "res=%d.",
@@ -66,7 +66,7 @@
   }
 
   res = parcel->writeFileDescriptor(pollhup_event_fd_.Get());
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE(
         "ChannelParcelable::writeToParcel: Cannot write pollhup event fd: "
         "res=%d.",
@@ -79,7 +79,7 @@
 
 status_t ChannelParcelable::readFromParcel(const Parcel* parcel) {
   uint32_t magic = 0;
-  status_t res = NO_ERROR;
+  status_t res = OK;
 
   if (IsValid()) {
     ALOGE(
@@ -89,7 +89,7 @@
   }
 
   res = parcel->readUint32(&magic);
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE("ChannelParcelable::readFromParcel: Failed to read magic: res=%d.",
           res);
     return res;
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 07904fb..4f8bdbf 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -85,9 +85,6 @@
         "-Wno-error=sign-compare", // to fix later
         "-Wno-unused-variable",
     ],
-    cppflags: [
-        "-std=c++1z"
-    ],
     shared_libs: sharedLibraries,
     whole_static_libs: staticLibraries,
     header_libs: headerLibraries,
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 5f6455c..6d259bd 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -1098,7 +1098,7 @@
   if (FindCallback(callback) == callbacks_.cend()) {
     callbacks_.push_back(callback);
   }
-  return NO_ERROR;
+  return OK;
 }
 
 status_t HardwareComposer::VsyncService::unregisterCallback(
@@ -1108,7 +1108,7 @@
   if (iter != callbacks_.cend()) {
     callbacks_.erase(iter);
   }
-  return NO_ERROR;
+  return OK;
 }
 
 void HardwareComposer::VsyncService::OnVsync(int64_t vsync_timestamp) {
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
index 1d5740f..0eb7fec 100644
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
@@ -72,17 +72,17 @@
     Parcel data, reply;
     status_t result =
         data.writeInterfaceToken(surface_flinger_->getInterfaceDescriptor());
-    if (result != NO_ERROR) {
+    if (result != OK) {
       return std::nullopt;
     }
     result = IInterface::asBinder(surface_flinger_)
                  ->transact(kIsVrFlingerActiveTransactionCode, data, &reply);
-    if (result != NO_ERROR) {
+    if (result != OK) {
       return std::nullopt;
     }
     bool vr_flinger_active;
     result = reply.readBool(&vr_flinger_active);
-    if (result != NO_ERROR) {
+    if (result != OK) {
       return std::nullopt;
     }
     return vr_flinger_active;
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index 1a38dd8..6f97f0d 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -51,10 +51,44 @@
     return Void();
 }
 
-Return<void> BufferHubService::importBuffer(const hidl_handle& /*nativeHandle*/,
+Return<void> BufferHubService::importBuffer(const hidl_handle& tokenHandle,
                                             importBuffer_cb _hidl_cb) {
-    // TODO(b/118614157): implement buffer import
-    _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::NO_ERROR);
+    if (!tokenHandle.getNativeHandle() || tokenHandle->numFds != 0 || tokenHandle->numInts != 1) {
+        // nullptr handle or wrong format
+        _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::INVALID_TOKEN);
+        return Void();
+    }
+
+    uint32_t token = tokenHandle->data[0];
+
+    wp<BufferClient> originClientWp;
+    {
+        std::lock_guard<std::mutex> lock(mTokenMapMutex);
+        auto iter = mTokenMap.find(token);
+        if (iter == mTokenMap.end()) {
+            // Invalid token
+            _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::INVALID_TOKEN);
+            return Void();
+        }
+
+        originClientWp = iter->second;
+        mTokenMap.erase(iter);
+    }
+
+    // Check if original client is dead
+    sp<BufferClient> originClient = originClientWp.promote();
+    if (!originClient) {
+        // Should not happen since token should be removed if already gone
+        ALOGE("%s: original client %p gone!", __FUNCTION__, originClientWp.unsafe_get());
+        _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::BUFFER_FREED);
+        return Void();
+    }
+
+    sp<BufferClient> client = new BufferClient(*originClient);
+
+    std::lock_guard<std::mutex> lock(mClientListMutex);
+    mClientList.push_back(client);
+    _hidl_cb(/*bufferClient=*/client, /*status=*/BufferHubStatus::NO_ERROR);
     return Void();
 }
 
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
index 5456ec3..769ec86 100644
--- a/services/bufferhub/include/bufferhub/BufferClient.h
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -37,15 +37,19 @@
 class BufferClient : public IBufferClient {
 public:
     // Creates a server-side buffer client from an existing BufferNode. Note that
-    // this funciton takes ownership of the shared_ptr.
+    // this function takes ownership of the shared_ptr.
     // Returns a raw pointer to the BufferClient on success, nullptr on failure.
     static BufferClient* create(BufferHubService* service, const std::shared_ptr<BufferNode>& node);
 
+    // Creates a BufferClient from an existing BufferClient. Will share the same BufferNode.
+    explicit BufferClient(const BufferClient& other)
+          : mService(other.mService), mBufferNode(other.mBufferNode) {}
+
     Return<void> duplicate(duplicate_cb _hidl_cb) override;
 
 private:
     BufferClient(wp<BufferHubService> service, const std::shared_ptr<BufferNode>& node)
-          : mService(service), mBufferNode(node){};
+          : mService(service), mBufferNode(node) {}
 
     wp<BufferHubService> mService;
     std::shared_ptr<BufferNode> mBufferNode;
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
index e3f657f..6535659 100644
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -42,7 +42,7 @@
     Return<void> allocateBuffer(const HardwareBufferDescription& description,
                                 const uint32_t userMetadataSize,
                                 allocateBuffer_cb _hidl_cb) override;
-    Return<void> importBuffer(const hidl_handle& nativeHandle, importBuffer_cb _hidl_cb) override;
+    Return<void> importBuffer(const hidl_handle& tokenHandle, importBuffer_cb _hidl_cb) override;
 
     // Non-binder functions
     // Internal help function for IBufferClient::duplicate.
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 250bbee..47bed65 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -21,7 +21,6 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-    cppflags: ["-std=c++1z"],
     srcs: [
         ":gpuservice_sources",
     ],
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 0e258c8..1dca1b6 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -3093,7 +3093,7 @@
                     continue;
                 }
 
-                if (windowHandle->getInfo()->hasFocus) {
+                if (windowHandle->getInfo()->hasFocus && windowHandle->getInfo()->visible) {
                     newFocusedWindowHandle = windowHandle;
                 }
                 if (windowHandle == mLastHoverWindowHandle) {
diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS
index d4393d6..81099e8 100644
--- a/services/sensorservice/OWNERS
+++ b/services/sensorservice/OWNERS
@@ -1,2 +1,3 @@
 arthuri@google.com
 bduddie@google.com
+bstack@google.com
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 1007b3d..22e4d1e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -9,7 +9,6 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-    cppflags: ["-std=c++1z"],
 }
 
 cc_defaults {
@@ -143,6 +142,7 @@
         "Scheduler/LayerHistory.cpp",
         "Scheduler/MessageQueue.cpp",
         "Scheduler/Scheduler.cpp",
+        "Scheduler/SchedulerUtils.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceInterceptor.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index b862278..fd40f6c 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -641,15 +641,21 @@
     texCoords[2] = vec2(right, 1.0f - bottom);
     texCoords[3] = vec2(right, 1.0f - top);
 
+    const auto roundedCornerState = getRoundedCornerState();
+    const auto cropRect = roundedCornerState.cropRect;
+    setupRoundedCornersCropCoordinates(win, cropRect);
+
     auto& engine(mFlinger->getRenderEngine());
     engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), false /* disableTexture */,
-                              getColor());
+                              getColor(), roundedCornerState.radius);
     engine.setSourceDataSpace(mCurrentDataSpace);
 
     if (isHdrY410()) {
         engine.setSourceY410BT2020(true);
     }
 
+    engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight());
+
     engine.drawMesh(getBE().mMesh);
     engine.disableBlending();
 
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 9c34308..f27f6aa 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -45,9 +45,18 @@
         renderengine::Mesh mesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2);
         computeGeometry(renderArea, mesh, useIdentityTransform);
         auto& engine(mFlinger->getRenderEngine());
+
+        Rect win{computeBounds()};
+
+        const auto roundedCornerState = getRoundedCornerState();
+        const auto cropRect = roundedCornerState.cropRect;
+        setupRoundedCornersCropCoordinates(win, cropRect);
+
         engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */,
-                                  true /* disableTexture */, color);
+                                  true /* disableTexture */, color, roundedCornerState.radius);
+
         engine.setSourceDataSpace(mCurrentDataSpace);
+        engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight());
         engine.drawMesh(mesh);
         engine.disableBlending();
     }
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index b919da7..5b641a2 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1050,6 +1050,23 @@
     return error;
 }
 
+Error Composer::getDisplayCapabilities(Display display,
+                                       std::vector<DisplayCapability>* outCapabilities) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError;
+    mClient_2_3->getDisplayCapabilities(display,
+                                        [&](const auto& tmpError, const auto& tmpCapabilities) {
+                                            error = tmpError;
+                                            if (error != Error::NONE) {
+                                                return;
+                                            }
+                                            *outCapabilities = tmpCapabilities;
+                                        });
+    return error;
+}
+
 CommandReader::~CommandReader()
 {
     resetData();
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index c39171e..61af348 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -48,24 +48,20 @@
 using types::V1_0::ColorTransform;
 using types::V1_0::Hdr;
 using types::V1_0::Transform;
-
 using types::V1_1::PixelFormat;
 using types::V1_1::RenderIntent;
 using types::V1_2::ColorMode;
 using types::V1_2::Dataspace;
-
 using V2_1::Config;
 using V2_1::Display;
 using V2_1::Error;
 using V2_1::IComposerCallback;
 using V2_1::Layer;
-
 using V2_3::CommandReaderBase;
 using V2_3::CommandWriterBase;
-
 using V2_3::IComposer;
 using V2_3::IComposerClient;
-
+using DisplayCapability = IComposerClient::DisplayCapability;
 using PerFrameMetadata = IComposerClient::PerFrameMetadata;
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
 
@@ -196,6 +192,8 @@
     virtual Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
                                                         Dataspace* outDataspace,
                                                         uint8_t* outComponentMask) = 0;
+    virtual Error getDisplayCapabilities(Display display,
+                                         std::vector<DisplayCapability>* outCapabilities) = 0;
 };
 
 namespace impl {
@@ -398,6 +396,8 @@
     Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
                                                 Dataspace* outDataspace,
                                                 uint8_t* outComponentMask) override;
+    Error getDisplayCapabilities(Display display,
+                                 std::vector<DisplayCapability>* outCapabilities) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index b4ac506..2df2d3b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -234,6 +234,22 @@
         mId(id),
         mIsConnected(false),
         mType(type) {
+    std::vector<Hwc2::DisplayCapability> tmpCapabilities;
+    auto error = static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
+    if (error == Error::None) {
+        for (auto capability : tmpCapabilities) {
+            mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability));
+        }
+    } else if (error == Error::Unsupported) {
+        if (capabilities.count(Capability::SkipClientColorTransform)) {
+            mDisplayCapabilities.emplace(DisplayCapability::SkipClientColorTransform);
+        }
+        bool dozeSupport = false;
+        error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
+        if (error == Error::None && dozeSupport) {
+            mDisplayCapabilities.emplace(DisplayCapability::Doze);
+        }
+    }
     ALOGV("Created display %" PRIu64, id);
 }
 
@@ -507,15 +523,8 @@
     return Error::None;
 }
 
-Error Display::supportsDoze(bool* outSupport) const
-{
-    bool intSupport = false;
-    auto intError = mComposer.getDozeSupport(mId, &intSupport);
-    auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
-    *outSupport = static_cast<bool>(intSupport);
+Error Display::supportsDoze(bool* outSupport) const {
+    *outSupport = mDisplayCapabilities.count(DisplayCapability::Doze) > 0;
     return Error::None;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 607d9ef..8c0f50c 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -272,6 +272,9 @@
     hwc2_display_t getId() const { return mId; }
     bool isConnected() const { return mIsConnected; }
     void setConnected(bool connected);  // For use by Device only
+    const std::unordered_set<DisplayCapability>& getCapabilities() const {
+        return mDisplayCapabilities;
+    };
 
 private:
     int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
@@ -298,6 +301,7 @@
     DisplayType mType;
     std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
     std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+    std::unordered_set<DisplayCapability> mDisplayCapabilities;
 };
 
 // Convenience C++ class to access hwc2_device_t Layer functions directly.
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index c93b4d6..a752a7d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -95,6 +95,15 @@
     return mHwcDevice->getCapabilities().count(capability) > 0;
 }
 
+bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId,
+                                      HWC2::DisplayCapability capability) const {
+    if (!displayId) {
+        return false;
+    }
+    RETURN_IF_INVALID_DISPLAY(*displayId, false);
+    return mDisplayData.at(*displayId).hwcDisplay->getCapabilities().count(capability) > 0;
+}
+
 void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) {
     bool valid = true;
     switch (from) {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 1e5f971..0375f74 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -59,6 +59,8 @@
                                       DisplayIdentificationData* outData) const;
 
     bool hasCapability(HWC2::Capability capability) const;
+    bool hasDisplayCapability(const std::optional<DisplayId>& displayId,
+                              HWC2::DisplayCapability capability) const;
 
     // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
     std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 58fd866..aa53382 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -101,6 +101,7 @@
     mCurrentState.dataspace = ui::Dataspace::UNKNOWN;
     mCurrentState.hdrMetadata.validTypes = 0;
     mCurrentState.surfaceDamageRegion.clear();
+    mCurrentState.cornerRadius = 0.0f;
     mCurrentState.api = -1;
     mCurrentState.hasColorTransform = false;
 
@@ -400,6 +401,22 @@
     return activeCrop;
 }
 
+void Layer::setupRoundedCornersCropCoordinates(Rect win,
+                                               const FloatRect& roundedCornersCrop) const {
+    // Translate win by the rounded corners rect coordinates, to have all values in
+    // layer coordinate space.
+    win.left -= roundedCornersCrop.left;
+    win.right -= roundedCornersCrop.left;
+    win.top -= roundedCornersCrop.top;
+    win.bottom -= roundedCornersCrop.top;
+
+    renderengine::Mesh::VertexArray<vec2> cropCoords(getBE().mMesh.getCropCoordArray<vec2>());
+    cropCoords[0] = vec2(win.left, win.top);
+    cropCoords[1] = vec2(win.left, win.top + win.getHeight());
+    cropCoords[2] = vec2(win.right, win.top + win.getHeight());
+    cropCoords[3] = vec2(win.right, win.top);
+}
+
 FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) const {
     // the content crop is the area of the content that gets scaled to the
     // layer's size. This is in buffer space.
@@ -1220,6 +1237,17 @@
     return true;
 }
 
+bool Layer::setCornerRadius(float cornerRadius) {
+    if (mCurrentState.cornerRadius == cornerRadius)
+        return false;
+
+    mCurrentState.sequence++;
+    mCurrentState.cornerRadius = cornerRadius;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
         bool allowNonRectPreservingTransforms) {
     ui::Transform t;
@@ -1918,6 +1946,26 @@
     return half4(color.r, color.g, color.b, getAlpha());
 }
 
+Layer::RoundedCornerState Layer::getRoundedCornerState() const {
+    const auto& p = mDrawingParent.promote();
+    if (p != nullptr) {
+        RoundedCornerState parentState = p->getRoundedCornerState();
+        if (parentState.radius > 0) {
+            ui::Transform t = getActiveTransform(getDrawingState());
+            t = t.inverse();
+            parentState.cropRect = t.transform(parentState.cropRect);
+            // The rounded corners shader only accepts 1 corner radius for performance reasons,
+            // but a transform matrix can define horizontal and vertical scales.
+            // Let's take the average between both of them and pass into the shader, practically we
+            // never do this type of transformation on windows anyway.
+            parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+            return parentState;
+        }
+    }
+    const float radius = getDrawingState().cornerRadius;
+    return radius > 0 ? RoundedCornerState(computeBounds(), radius) : RoundedCornerState();
+}
+
 void Layer::commitChildList() {
     for (size_t i = 0; i < mCurrentChildren.size(); i++) {
         const auto& child = mCurrentChildren[i];
@@ -1978,6 +2026,7 @@
     size->set_h(state.active_legacy.h);
 
     LayerProtoHelper::writeToProto(state.crop_legacy, layerInfo->mutable_crop());
+    layerInfo->set_corner_radius(getRoundedCornerState().radius);
 
     layerInfo->set_is_opaque(isOpaque(state));
     layerInfo->set_invalidate(contentDirty);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index a4f9c93..687fc0a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -125,6 +125,17 @@
         inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); }
     };
 
+    struct RoundedCornerState {
+        RoundedCornerState() = default;
+        RoundedCornerState(FloatRect cropRect, float radius)
+              : cropRect(cropRect), radius(radius) {}
+
+        // Rounded rectangle in local layer coordinate space.
+        FloatRect cropRect = FloatRect();
+        // Radius of the rounded rectangle.
+        float radius = 0.0f;
+    };
+
     struct State {
         Geometry active_legacy;
         Geometry requested_legacy;
@@ -167,6 +178,7 @@
         SortedVector<wp<Layer>> zOrderRelatives;
 
         half4 color;
+        float cornerRadius;
 
         bool inputInfoChanged;
         InputWindowInfo inputInfo;
@@ -255,6 +267,13 @@
 
     virtual bool setAlpha(float alpha);
     virtual bool setColor(const half3& color);
+
+    // Set rounded corner radius for this layer and its children.
+    //
+    // We only support 1 radius per layer in the hierarchy, where parent layers have precedence.
+    // The shape of the rounded corner rectangle is specified by the crop rectangle of the layer
+    // from which we inferred the rounded corner radius.
+    virtual bool setCornerRadius(float cornerRadius);
     virtual bool setTransparentRegionHint(const Region& transparent);
     virtual bool setFlags(uint8_t flags, uint8_t mask);
     virtual bool setLayerStack(uint32_t layerStack);
@@ -573,6 +592,13 @@
     half getAlpha() const;
     half4 getColor() const;
 
+    // Returns how rounded corners should be drawn for this layer.
+    // This will traverse the hierarchy until it reaches its root, finding topmost rounded
+    // corner definition and converting it into current layer's coordinates.
+    // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
+    // ignored.
+    RoundedCornerState getRoundedCornerState() const;
+
     void traverseInReverseZOrder(LayerVector::StateSet stateSet,
                                  const LayerVector::Visitor& visitor);
     void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -645,11 +671,15 @@
     // IGraphicBufferProducer client, as that should not affect child clipping.
     // Returns in screen space.
     Rect computeInitialCrop(const sp<const DisplayDevice>& display) const;
+    /**
+     * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
+     * crop coordinates, transforming them into layer space.
+     */
+    void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
 
     // drawing
     void clearWithOpenGL(const RenderArea& renderArea, float r, float g, float b,
                          float alpha) const;
-
     void setParent(const sp<Layer>& layer);
 
     LayerVector makeTraversalList(LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 1f47949..d5ccbe1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -26,6 +26,8 @@
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 
+#include "SchedulerUtils.h"
+
 namespace android {
 
 LayerHistory::LayerHistory() {}
@@ -38,7 +40,7 @@
 
 void LayerHistory::incrementCounter() {
     mCounter++;
-    mCounter = mCounter % ARRAY_SIZE;
+    mCounter = mCounter % scheduler::ARRAY_SIZE;
     // Clear all the previous data from the history. This is a ring buffer, so we are
     // reusing memory.
     mElements[mCounter].clear();
@@ -47,7 +49,8 @@
 const std::unordered_map<std::string, nsecs_t>& LayerHistory::get(size_t index) const {
     // For the purposes of the layer history, the index = 0 always needs to start at the
     // current counter, and then decrement to access the layers in correct historical order.
-    return mElements.at((ARRAY_SIZE + (mCounter - (index % ARRAY_SIZE))) % ARRAY_SIZE);
+    return mElements.at((scheduler::ARRAY_SIZE + (mCounter - (index % scheduler::ARRAY_SIZE))) %
+                        scheduler::ARRAY_SIZE);
 }
 
 } // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 76c1352..c6fab07 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -25,6 +25,8 @@
 
 #include <utils/Timers.h>
 
+#include "SchedulerUtils.h"
+
 namespace android {
 
 /*
@@ -52,12 +54,11 @@
     const std::unordered_map<std::string, nsecs_t>& get(size_t index) const;
     // Returns the total size of the ring buffer. The value is always the same regardless
     // of how many slots we filled in.
-    static constexpr size_t getSize() { return ARRAY_SIZE; }
+    static constexpr size_t getSize() { return scheduler::ARRAY_SIZE; }
 
 private:
     size_t mCounter = 0;
-    static constexpr size_t ARRAY_SIZE = 30;
-    std::array<std::unordered_map<std::string, nsecs_t>, ARRAY_SIZE> mElements;
+    std::array<std::unordered_map<std::string, nsecs_t>, scheduler::ARRAY_SIZE> mElements;
 };
 
 } // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4d3ae33..4457f72 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -18,6 +18,7 @@
 
 #include "Scheduler.h"
 
+#include <algorithm>
 #include <cinttypes>
 #include <cstdint>
 #include <memory>
@@ -38,6 +39,7 @@
 #include "EventControlThread.h"
 #include "EventThread.h"
 #include "InjectVSyncSource.h"
+#include "SchedulerUtils.h"
 
 namespace android {
 
@@ -222,11 +224,6 @@
     mLayerHistory.incrementCounter();
 }
 
-nsecs_t Scheduler::calculateAverage() const {
-    nsecs_t sum = std::accumulate(mTimeDifferences.begin(), mTimeDifferences.end(), 0);
-    return (sum / ARRAY_SIZE);
-}
-
 void Scheduler::updateFrameSkipping(const int64_t skipCount) {
     ATRACE_INT("FrameSkipCount", skipCount);
     if (mSkipCount != skipCount) {
@@ -243,6 +240,7 @@
 
     // Traverse through the layer history, and determine the differences in present times.
     nsecs_t newestPresentTime = framePresentTime;
+    std::string differencesText = "";
     for (int i = 1; i < mLayerHistory.getSize(); i++) {
         std::unordered_map<std::string, nsecs_t> layers = mLayerHistory.get(i);
         for (auto layer : layers) {
@@ -250,39 +248,31 @@
                 continue;
             }
             int64_t differenceMs = (newestPresentTime - layer.second) / 1000000;
-            ALOGD("%d Layer %s: %" PRId64, i, layerName.c_str(), differenceMs);
             // Dismiss noise.
             if (differenceMs > 10 && differenceMs < 60) {
                 differencesMs.push_back(differenceMs);
             }
+            IF_ALOGV() { differencesText += (std::to_string(differenceMs) + " "); }
             newestPresentTime = layer.second;
         }
     }
-    // Average is a good indicator for when 24fps videos are playing, because the frames come in
-    // 33, and 49 ms intervals with occasional 41ms.
-    int64_t average =
-            std::accumulate(differencesMs.begin(), differencesMs.end(), 0) / differencesMs.size();
-    const auto tag = "TimestampAvg_" + layerName;
-    ATRACE_INT(tag.c_str(), average);
+    ALOGV("Layer %s timestamp intervals: %s", layerName.c_str(), differencesText.c_str());
 
-    // Mode and median are good indicators for 30 and 60 fps videos, because the majority of frames
-    // come in 16, or 33 ms intervals.
-    // TODO(b/113612090): Calculate mode. Median is good for now, since we want a given interval to
-    // repeat at least ARRAY_SIZE/2 + 1 times.
-    if (differencesMs.size() > 0) {
+    if (!differencesMs.empty()) {
+        // Mean/Average is a good indicator for when 24fps videos are playing, because the frames
+        // come in 33, and 49 ms intervals with occasional 41ms.
+        const int64_t meanMs = scheduler::calculate_mean(differencesMs);
+        const auto tagMean = "TimestampMean_" + layerName;
+        ATRACE_INT(tagMean.c_str(), meanMs);
+
+        // Mode and median are good indicators for 30 and 60 fps videos, because the majority of
+        // frames come in 16, or 33 ms intervals.
         const auto tagMedian = "TimestampMedian_" + layerName;
-        ATRACE_INT(tagMedian.c_str(), calculateMedian(&differencesMs));
-    }
-}
+        ATRACE_INT(tagMedian.c_str(), scheduler::calculate_median(&differencesMs));
 
-int64_t Scheduler::calculateMedian(std::vector<int64_t>* v) {
-    if (!v || v->size() == 0) {
-        return 0;
+        const auto tagMode = "TimestampMode_" + layerName;
+        ATRACE_INT(tagMode.c_str(), scheduler::calculate_mode(differencesMs));
     }
-
-    size_t n = v->size() / 2;
-    nth_element(v->begin(), v->begin() + n, v->end());
-    return v->at(n);
 }
 
 void Scheduler::determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime) {
@@ -302,23 +292,21 @@
     }
     ATRACE_INT("TimestampDiff", differenceMs);
 
-    mTimeDifferences[mCounter % ARRAY_SIZE] = differenceMs;
+    mTimeDifferences[mCounter % scheduler::ARRAY_SIZE] = differenceMs;
     mCounter++;
-    nsecs_t average = calculateAverage();
-    ATRACE_INT("TimestampAverage", average);
+    int64_t mean = scheduler::calculate_mean(mTimeDifferences);
+    ATRACE_INT("AutoTimestampMean", mean);
 
     // TODO(b/113612090): This are current numbers from trial and error while running videos
     // from YouTube at 24, 30, and 60 fps.
-    if (average > 14 && average < 18) {
+    if (mean > 14 && mean < 18) {
         ATRACE_INT("FPS", 60);
-    } else if (average > 31 && average < 34) {
+    } else if (mean > 31 && mean < 34) {
         ATRACE_INT("FPS", 30);
-        updateFrameSkipping(1);
         return;
-    } else if (average > 39 && average < 42) {
+    } else if (mean > 39 && mean < 42) {
         ATRACE_INT("FPS", 24);
     }
-    updateFrameSkipping(0);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index adfc071..764ad00 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -27,6 +27,7 @@
 #include "EventThread.h"
 #include "InjectVSyncSource.h"
 #include "LayerHistory.h"
+#include "SchedulerUtils.h"
 
 namespace android {
 
@@ -111,8 +112,6 @@
     void incrementFrameCounter();
 
 protected:
-    friend class SchedulerTest;
-
     virtual std::unique_ptr<EventThread> makeEventThread(
             const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
             impl::EventThread::ResyncWithRateLimitCallback resyncCallback,
@@ -124,9 +123,6 @@
     // Collects the statistical mean (average) and median between timestamp
     // intervals for each frame for each layer.
     void determineLayerTimestampStats(const std::string layerName, const nsecs_t framePresentTime);
-    // Calculates the statistical median in the vector. Return 0 if the vector is empty. The
-    // function modifies the vector contents.
-    int64_t calculateMedian(std::vector<int64_t>* v);
     // Collects the average difference between timestamps for each frame regardless
     // of which layer the timestamp came from.
     void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime);
@@ -163,8 +159,7 @@
     // simulate 30Hz rendering, we skip every other frame, and this variable is set
     // to 1.
     int64_t mSkipCount = 0;
-    static constexpr size_t ARRAY_SIZE = 30;
-    std::array<int64_t, ARRAY_SIZE> mTimeDifferences;
+    std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{};
     size_t mCounter = 0;
 
     LayerHistory mLayerHistory;
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
new file mode 100644
index 0000000..191022d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "SchedulerUtils.h"
+
+#include <cinttypes>
+#include <numeric>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace scheduler {
+
+int64_t calculate_median(std::vector<int64_t>* v) {
+    if (!v || v->empty()) {
+        return 0;
+    }
+
+    size_t n = v->size() / 2;
+    nth_element(v->begin(), v->begin() + n, v->end());
+    return v->at(n);
+}
+
+int64_t calculate_mode(const std::vector<int64_t>& v) {
+    if (v.empty()) {
+        return 0;
+    }
+
+    // Create a map with all the counts for the indivicual values in the vector.
+    std::unordered_map<int64_t, int64_t> counts;
+    for (int64_t value : v) {
+        counts[value]++;
+    }
+
+    // Sort the map, and return the number with the highest count. If two numbers have
+    // the same count, first one is returned.
+    using ValueType = const decltype(counts)::value_type&;
+    const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
+    return std::max_element(counts.begin(), counts.end(), compareCounts)->first;
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
new file mode 100644
index 0000000..17c57db
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include <cinttypes>
+#include <numeric>
+#include <vector>
+
+namespace android {
+namespace scheduler {
+// This number is used to set the size of the arrays in scheduler that hold information
+// about layers.
+static constexpr size_t ARRAY_SIZE = 30;
+
+// Calculates the statistical mean (average) in the data structure (array, vector). The
+// function does not modify the contents of the array.
+template <typename T>
+auto calculate_mean(const T& v) {
+    using V = typename T::value_type;
+    V sum = std::accumulate(v.begin(), v.end(), 0);
+    return sum / static_cast<V>(v.size());
+}
+
+// Calculates the statistical median in the vector. Return 0 if the vector is empty. The
+// function modifies the vector contents.
+int64_t calculate_median(std::vector<int64_t>* v);
+
+// Calculates the statistical mode in the vector. Return 0 if the vector is empty.
+int64_t calculate_mode(const std::vector<int64_t>& v);
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index adc46cc..5b3c477 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1687,6 +1687,10 @@
                 layer->forceClientComposition(*displayId);
             }
 
+            if (layer->getRoundedCornerState().radius > 0.0f) {
+                layer->forceClientComposition(*displayId);
+            }
+
             if (layer->getForceClientComposition(*displayId)) {
                 ALOGV("[%s] Requesting Client composition", layer->getName().string());
                 layer->setCompositionType(*displayId, HWC2::Composition::Client);
@@ -2876,6 +2880,7 @@
                 // compute the opaque region
                 const int32_t layerOrientation = tr.getOrientation();
                 if (layer->getAlpha() == 1.0f && !translucent &&
+                        layer->getRoundedCornerState().radius == 0.0f &&
                         ((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
                     // the opaque region is the layer's footprint
                     opaqueRegion = visibleRegion;
@@ -3085,8 +3090,10 @@
                 display->getHdrCapabilities().getDesiredMaxLuminance());
 
         const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(displayId);
-        const bool skipClientColorTransform = getBE().mHwc->hasCapability(
-            HWC2::Capability::SkipClientColorTransform);
+        const bool skipClientColorTransform =
+                getBE().mHwc
+                        ->hasDisplayCapability(displayId,
+                                               HWC2::DisplayCapability::SkipClientColorTransform);
 
         // Compute the global color transform matrix.
         applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
@@ -3164,7 +3171,7 @@
                     const Layer::State& state(layer->getDrawingState());
                     if (layer->getClearClientTarget(*displayId) && !firstLayer &&
                         layer->isOpaque(state) && (layer->getAlpha() == 1.0f) &&
-                        hasClientComposition) {
+                        layer->getRoundedCornerState().radius == 0.0f && hasClientComposition) {
                         // never clear the very first layer since we're
                         // guaranteed the FB is already cleared
                         layer->clearWithOpenGL(renderArea);
@@ -3584,6 +3591,10 @@
         if (layer->setCrop_legacy(s.crop_legacy, !geometryAppliesWithResize))
             flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eCornerRadiusChanged) {
+        if (layer->setCornerRadius(s.cornerRadius))
+            flags |= eTraversalNeeded;
+    }
     if (what & layer_state_t::eLayerStackChanged) {
         ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
         // We only allow setting layer stacks for top level layers,
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 55cfa4b..7bfe033 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -109,6 +109,7 @@
                                layer->mCurrentState.activeTransparentRegion_legacy);
     addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
     addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
+    addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
     if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
         addDeferTransactionLocked(transaction, layerId,
                                   layer->mCurrentState.barrierLayer_legacy.promote(),
@@ -289,6 +290,14 @@
     setProtoRectLocked(protoRect, rect);
 }
 
+void SurfaceInterceptor::addCornerRadiusLocked(Transaction* transaction, int32_t layerId,
+                                       float cornerRadius)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    CornerRadiusChange* cornerRadiusChange(change->mutable_corner_radius());
+    cornerRadiusChange->set_corner_radius(cornerRadius);
+}
+
 void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
         const sp<const Layer>& layer, uint64_t frameNumber)
 {
@@ -350,6 +359,9 @@
     if (state.what & layer_state_t::eCropChanged_legacy) {
         addCropLocked(transaction, layerId, state.crop_legacy);
     }
+    if (state.what & layer_state_t::eCornerRadiusChanged) {
+        addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
+    }
     if (state.what & layer_state_t::eDeferTransaction_legacy) {
         sp<Layer> otherLayer = nullptr;
         if (state.barrierHandle_legacy != nullptr) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 394b99b..563a44c 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -144,6 +144,7 @@
     void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
     void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
     void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+    void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
     void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
             const sp<const Layer>& layer, uint64_t frameNumber);
     void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
index bef6b7c..b937f41 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
@@ -17,7 +17,6 @@
     },
 
     cppflags: [
-        "-std=c++1z",
         "-Werror",
         "-Wno-c++98-compat-pedantic",
         "-Wno-disabled-macro-expansion",
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index 1d7fb67..d020a39 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -121,6 +121,7 @@
     layer.appId = layerProto.app_id();
     layer.hwcCompositionType = layerProto.hwc_composition_type();
     layer.isProtected = layerProto.is_protected();
+    layer.cornerRadius = layerProto.corner_radius();
 
     return layer;
 }
@@ -294,6 +295,7 @@
                   size.y);
 
     StringAppendF(&result, "crop=%s, ", crop.to_string().c_str());
+    StringAppendF(&result, "cornerRadius=%f, ", cornerRadius);
     StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
     StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
     StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 6b3b497..a794ca5 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -114,6 +114,7 @@
         int32_t appId;
         int32_t hwcCompositionType;
         bool isProtected;
+        float cornerRadius;
 
         std::string to_string() const;
     };
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index c141ee3..b100438 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -87,6 +87,8 @@
   // If active_buffer is not null, record its transform.
   optional TransformProto buffer_transform = 39;
   optional int32 effective_scaling_mode = 40;
+  // Layer's corner radius.
+  optional float corner_radius = 41;
 }
 
 message PositionProto {
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 740d2fa..e506757 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -44,6 +44,7 @@
 constexpr uint32_t STACK_UPDATE = 1;
 constexpr uint64_t DEFERRED_UPDATE = 0;
 constexpr float ALPHA_UPDATE = 0.29f;
+constexpr float CORNER_RADIUS_UPDATE = 0.2f;
 constexpr float POSITION_UPDATE = 121;
 const Rect CROP_UPDATE(16, 16, 32, 32);
 
@@ -167,6 +168,7 @@
     bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
     bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
     bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
+    bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
     bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
     bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
     bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -198,6 +200,7 @@
     void alphaUpdate(Transaction&);
     void layerUpdate(Transaction&);
     void cropUpdate(Transaction&);
+    void cornerRadiusUpdate(Transaction&);
     void matrixUpdate(Transaction&);
     void overrideScalingModeUpdate(Transaction&);
     void transparentRegionHintUpdate(Transaction&);
@@ -313,6 +316,10 @@
     t.setAlpha(mBGSurfaceControl, ALPHA_UPDATE);
 }
 
+void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) {
+    t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
+}
+
 void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
     t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
 }
@@ -369,6 +376,7 @@
     runInTransaction(&SurfaceInterceptorTest::positionUpdate);
     runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
     runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
+    runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
     runInTransaction(&SurfaceInterceptorTest::layerUpdate);
     runInTransaction(&SurfaceInterceptorTest::cropUpdate);
     runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
@@ -430,6 +438,17 @@
     return foundAlpha;
 }
 
+bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change,
+                                                     bool foundCornerRadius) {
+    bool hasCornerRadius(change.corner_radius().corner_radius() == CORNER_RADIUS_UPDATE);
+    if (hasCornerRadius && !foundCornerRadius) {
+        foundCornerRadius = true;
+    } else if (hasCornerRadius && foundCornerRadius) {
+        [] () { FAIL(); }();
+    }
+    return foundCornerRadius;
+}
+
 bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
     bool hasLayer(change.layer().layer() == LAYER_UPDATE);
     if (hasLayer && !foundLayer) {
@@ -572,6 +591,9 @@
                         case SurfaceChange::SurfaceChangeCase::kCrop:
                             foundUpdate = cropUpdateFound(change, foundUpdate);
                             break;
+                        case SurfaceChange::SurfaceChangeCase::kCornerRadius:
+                            foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
+                            break;
                         case SurfaceChange::SurfaceChangeCase::kMatrix:
                             foundUpdate = matrixUpdateFound(change, foundUpdate);
                             break;
@@ -730,6 +752,11 @@
     captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
 }
 
+TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::cornerRadiusUpdate,
+            SurfaceChange::SurfaceChangeCase::kCornerRadius);
+}
+
 TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
     captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
 }
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 7e95d99..e414991 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -1225,6 +1225,28 @@
     }
 }
 
+TEST_P(LayerTypeTransactionTest, SetCornerRadius) {
+    sp<SurfaceControl> layer;
+    const uint8_t size = 64;
+    const uint8_t testArea = 4;
+    const float cornerRadius = 16.0f;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
+
+    Transaction()
+            .setCornerRadius(layer, cornerRadius)
+            .apply();
+    {
+        auto shot = screenshot();
+        // Transparent corners
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+        shot->expectColor(Rect(0, size - testArea, testArea, testArea), Color::BLACK);
+        shot->expectColor(Rect(size - testArea, 0, testArea, testArea), Color::BLACK);
+        shot->expectColor(Rect(size - testArea, size - testArea, testArea, testArea),
+            Color::BLACK);
+    }
+}
+
 TEST_F(LayerTransactionTest, SetColorBasic) {
     sp<SurfaceControl> bufferLayer;
     sp<SurfaceControl> colorLayer;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 58f1e9c..28f3ef2 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -31,6 +31,7 @@
         "EventThreadTest.cpp",
         "LayerHistoryTest.cpp",
         "SchedulerTest.cpp",
+        "SchedulerUtilsTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockDisplaySurface.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index cfaf495..5a6aa92 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -580,7 +580,8 @@
         EXPECT_CALL(*test->mRenderEngine,
                     setupLayerBlending(true, false, false,
                                        half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
-                                             LayerProperties::COLOR[2], LayerProperties::COLOR[3])))
+                                             LayerProperties::COLOR[2], LayerProperties::COLOR[3]),
+                                       0.0f))
                 .Times(1);
 
         EXPECT_CALL(*test->mRenderEngine, createImage())
@@ -626,7 +627,8 @@
         EXPECT_CALL(*test->mRenderEngine,
                     setupLayerBlending(true, false, true,
                                        half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
-                                             LayerProperties::COLOR[2], LayerProperties::COLOR[3])))
+                                             LayerProperties::COLOR[2], LayerProperties::COLOR[3]),
+                                       0.0f))
                 .Times(1);
         EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
         EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
@@ -690,7 +692,7 @@
         EXPECT_CALL(*test->mRenderEngine,
                     setupLayerBlending(true, false, false,
                                        half4(Base::COLOR[0], Base::COLOR[1], Base::COLOR[2],
-                                             Base::COLOR[3])))
+                                             Base::COLOR[3]), 0.0f))
                 .Times(1);
         EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
         EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 5aaf848..34cee3e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -2553,14 +2553,23 @@
  */
 
 // Used when we simulate a display that supports doze.
+template <typename Display>
 struct DozeIsSupportedVariant {
     static constexpr bool DOZE_SUPPORTED = true;
     static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
             IComposerClient::PowerMode::DOZE;
     static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
             IComposerClient::PowerMode::DOZE_SUSPEND;
+
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>(
+                                        {Hwc2::DisplayCapability::DOZE})),
+                                Return(Error::NONE)));
+    }
 };
 
+template <typename Display>
 // Used when we simulate a display that does not support doze.
 struct DozeNotSupportedVariant {
     static constexpr bool DOZE_SUPPORTED = false;
@@ -2568,6 +2577,12 @@
             IComposerClient::PowerMode::ON;
     static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
             IComposerClient::PowerMode::ON;
+
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+                                Return(Error::NONE)));
+    }
 };
 
 struct EventThreadBaseSupportedVariant {
@@ -2824,17 +2839,19 @@
 // A sample configuration for the primary display.
 // In addition to having event thread support, we emulate doze support.
 template <typename TransitionVariant>
-using PrimaryDisplayPowerCase = DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant,
-                                                 EventThreadIsSupportedVariant,
-                                                 DispSyncIsSupportedVariant, TransitionVariant>;
+using PrimaryDisplayPowerCase =
+        DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
+                         EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
+                         TransitionVariant>;
 
 // A sample configuration for the external display.
 // In addition to not having event thread support, we emulate not having doze
 // support.
 template <typename TransitionVariant>
-using ExternalDisplayPowerCase = DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant,
-                                                  EventThreadNotSupportedVariant,
-                                                  DispSyncNotSupportedVariant, TransitionVariant>;
+using ExternalDisplayPowerCase =
+        DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
+                         EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
+                         TransitionVariant>;
 
 class SetPowerModeInternalTest : public DisplayTransactionTest {
 public:
@@ -2856,6 +2873,7 @@
     // --------------------------------------------------------------------
     // Preconditions
 
+    Case::Doze::setupComposerCallExpectations(this);
     auto display =
             Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
     Case::setInitialPrimaryHWVsyncEnabled(this,
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index e967742..d0cf1b7 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -59,8 +59,6 @@
     SchedulerTest();
     ~SchedulerTest() override;
 
-    int64_t calculateMedian(std::vector<int64_t>* v);
-
     sp<Scheduler::ConnectionHandle> mConnectionHandle;
     mock::DispSync* mPrimaryDispSync = new mock::DispSync();
     mock::EventThread* mEventThread;
@@ -97,10 +95,6 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-int64_t SchedulerTest::calculateMedian(std::vector<int64_t>* v) {
-    return mScheduler->calculateMedian(v);
-}
-
 namespace {
 /* ------------------------------------------------------------------------
  * Test cases
@@ -190,38 +184,5 @@
     EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
     ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
 }
-
-TEST_F(SchedulerTest, calculateMedian) {
-    std::vector<int64_t> testVector;
-    // Calling the function on empty vector returns 0.
-    EXPECT_EQ(0, calculateMedian(&testVector));
-
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculateMedian(&testVector));
-    testVector.push_back(33);
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculateMedian(&testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(33, calculateMedian(&testVector));
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculateMedian(&testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(33, calculateMedian(&testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(33, calculateMedian(&testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(42, calculateMedian(&testVector));
-    testVector.push_back(60);
-    EXPECT_EQ(42, calculateMedian(&testVector));
-    testVector.push_back(60);
-    EXPECT_EQ(42, calculateMedian(&testVector));
-    testVector.push_back(33);
-    EXPECT_EQ(42, calculateMedian(&testVector));
-    testVector.push_back(33);
-    EXPECT_EQ(42, calculateMedian(&testVector));
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculateMedian(&testVector));
-}
-
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
new file mode 100644
index 0000000..5865579
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <array>
+
+#include "Scheduler/SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+class SchedulerUtilsTest : public testing::Test {
+public:
+    SchedulerUtilsTest() = default;
+    ~SchedulerUtilsTest() override = default;
+};
+
+namespace {
+TEST_F(SchedulerUtilsTest, calculate_mean) {
+    std::array<int64_t, 30> testArray{};
+    // Calling the function on empty array returns 0.
+    EXPECT_EQ(0, calculate_mean(testArray));
+
+    testArray[0] = 33;
+    EXPECT_EQ(1, calculate_mean(testArray));
+    testArray[1] = 33;
+    testArray[2] = 33;
+    EXPECT_EQ(3, calculate_mean(testArray));
+    testArray[3] = 42;
+    EXPECT_EQ(4, calculate_mean(testArray));
+    testArray[4] = 33;
+    EXPECT_EQ(5, calculate_mean(testArray));
+    testArray[5] = 42;
+    EXPECT_EQ(7, calculate_mean(testArray));
+    for (int i = 6; i < 30; i++) {
+        testArray[i] = 33;
+    }
+    EXPECT_EQ(33, calculate_mean(testArray));
+}
+
+TEST_F(SchedulerUtilsTest, calculate_median) {
+    std::vector<int64_t> testVector;
+    // Calling the function on empty vector returns 0.
+    EXPECT_EQ(0, calculate_median(&testVector));
+
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(33);
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(42, calculate_median(&testVector));
+    testVector.push_back(60);
+    EXPECT_EQ(42, calculate_median(&testVector));
+    testVector.push_back(60);
+    EXPECT_EQ(42, calculate_median(&testVector));
+    testVector.push_back(33);
+    EXPECT_EQ(42, calculate_median(&testVector));
+    testVector.push_back(33);
+    EXPECT_EQ(42, calculate_median(&testVector));
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_median(&testVector));
+}
+
+TEST_F(SchedulerUtilsTest, calculate_mode) {
+    std::vector<int64_t> testVector;
+    // Calling the function on empty vector returns 0.
+    EXPECT_EQ(0, calculate_mode(testVector));
+
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(33);
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(60);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(60);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(33);
+    // 5 occurences of 33.
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    // 5 occurences of 33, 5 occurences of 42. We choose the first one.
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    // 5 occurences of 33, 6 occurences of 42.
+    EXPECT_EQ(42, calculate_mode(testVector));
+    testVector.push_back(42);
+    // 5 occurences of 33, 7 occurences of 42.
+    EXPECT_EQ(42, calculate_mode(testVector));
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 03ef2f8..5a46c26 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -115,6 +115,7 @@
     MOCK_METHOD3(setLayerColorTransform, Error(Display, Layer, const float*));
     MOCK_METHOD4(getDisplayedContentSamplingAttributes,
                  Error(Display, PixelFormat*, Dataspace*, uint8_t*));
+    MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
index 90c3c20..a416808 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
@@ -56,10 +56,11 @@
     MOCK_CONST_METHOD0(checkErrors, void());
     MOCK_METHOD4(setViewportAndProjection,
                  void(size_t, size_t, Rect, ui::Transform::orientation_flags));
-    MOCK_METHOD4(setupLayerBlending, void(bool, bool, bool, const half4&));
+    MOCK_METHOD5(setupLayerBlending, void(bool, bool, bool, const half4&, float));
     MOCK_METHOD1(setupLayerTexturing, void(const Texture&));
     MOCK_METHOD0(setupLayerBlackedOut, void());
     MOCK_METHOD4(setupFillWithColor, void(float, float, float, float));
+    MOCK_METHOD2(setupCornerRadiusCropSize, void(float, float));
     MOCK_METHOD1(setColorTransform, void(const mat4&));
     MOCK_METHOD1(setSaturationMatrix, void(const mat4&));
     MOCK_METHOD0(disableTexturing, void());
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index b5e6bb4..0a59f29 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -75,5 +75,3 @@
     name: "bufferhubd",
     init_rc: ["bufferhubd.rc"],
 }
-
-subdirs = ["tests"]
\ No newline at end of file
diff --git a/services/vr/bufferhubd/IBufferHub.cpp b/services/vr/bufferhubd/IBufferHub.cpp
index 022a9cc..5980873 100644
--- a/services/vr/bufferhubd/IBufferHub.cpp
+++ b/services/vr/bufferhubd/IBufferHub.cpp
@@ -30,7 +30,7 @@
                                             uint32_t format, uint64_t usage,
                                             uint64_t user_metadata_size) {
   Parcel data, reply;
-  status_t ret = NO_ERROR;
+  status_t ret = OK;
   ret |= data.writeInterfaceToken(IBufferHub::getInterfaceDescriptor());
   ret |= data.writeUint32(width);
   ret |= data.writeUint32(height);
@@ -39,13 +39,13 @@
   ret |= data.writeUint64(usage);
   ret |= data.writeUint64(user_metadata_size);
 
-  if (ret != NO_ERROR) {
+  if (ret != OK) {
     ALOGE("BpBufferHub::createBuffer: failed to write into parcel");
     return nullptr;
   }
 
   ret = remote()->transact(CREATE_BUFFER, data, &reply);
-  if (ret == NO_ERROR) {
+  if (ret == OK) {
     return interface_cast<IBufferClient>(reply.readStrongBinder());
   } else {
     ALOGE("BpBufferHub::createBuffer: failed to transact; errno=%d", ret);
@@ -56,18 +56,18 @@
 status_t BpBufferHub::importBuffer(uint64_t token,
                                    sp<IBufferClient>* outClient) {
   Parcel data, reply;
-  status_t ret = NO_ERROR;
+  status_t ret = OK;
   ret |= data.writeInterfaceToken(IBufferHub::getInterfaceDescriptor());
   ret |= data.writeUint64(token);
-  if (ret != NO_ERROR) {
+  if (ret != OK) {
     ALOGE("BpBufferHub::importBuffer: failed to write into parcel");
     return ret;
   }
 
   ret = remote()->transact(IMPORT_BUFFER, data, &reply);
-  if (ret == NO_ERROR) {
+  if (ret == OK) {
     *outClient = interface_cast<IBufferClient>(reply.readStrongBinder());
-    return NO_ERROR;
+    return OK;
   } else {
     ALOGE("BpBufferHub::importBuffer: failed to transact; errno=%d", ret);
     return ret;
@@ -94,7 +94,7 @@
       uint64_t token = data.readUint64();
       sp<IBufferClient> client;
       status_t ret = importBuffer(token, &client);
-      if (ret == NO_ERROR) {
+      if (ret == OK) {
         return reply->writeStrongBinder(IInterface::asBinder(client));
       } else {
         return ret;
diff --git a/services/vr/bufferhubd/buffer_hub_binder.cpp b/services/vr/bufferhubd/buffer_hub_binder.cpp
index afd34aa..580433e 100644
--- a/services/vr/bufferhubd/buffer_hub_binder.cpp
+++ b/services/vr/bufferhubd/buffer_hub_binder.cpp
@@ -25,7 +25,7 @@
       String16(getServiceName()), service,
       /*allowIsolated =*/false,
       /*dump flags =*/IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
-  if (result != NO_ERROR) {
+  if (result != OK) {
     ALOGE("Publishing bufferhubd failed with error %d", result);
     return result;
   }
@@ -62,7 +62,7 @@
   }
 
   fclose(out);
-  return NO_ERROR;
+  return OK;
 }
 
 status_t BufferHubBinderService::registerToken(
@@ -72,7 +72,7 @@
   } while (token_map_.find(*outToken) != token_map_.end());
 
   token_map_.emplace(*outToken, node);
-  return NO_ERROR;
+  return OK;
 }
 
 sp<IBufferClient> BufferHubBinderService::createBuffer(
@@ -122,7 +122,7 @@
   token_map_.erase(iter);
   client_list_.push_back(client);
 
-  return NO_ERROR;
+  return OK;
 }
 
 }  // namespace dvr
diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp
index 3e10b26..e44cc2a 100644
--- a/services/vr/bufferhubd/bufferhubd.cpp
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -41,7 +41,7 @@
   dispatcher->AddService(pdx_service);
 
   ret = android::dvr::BufferHubBinderService::start(pdx_service);
-  CHECK_ERROR(ret != android::NO_ERROR, error,
+  CHECK_ERROR(ret != android::OK, error,
               "Failed to create bufferhub binder service\n");
 
   ret = dvrSetSchedulerClass(0, "graphics");
diff --git a/services/vr/bufferhubd/include/private/dvr/producer_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
index 3ad9c70..9aead79 100644
--- a/services/vr/bufferhubd/include/private/dvr/producer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
@@ -63,7 +63,7 @@
                                       LocalFence release_fence);
 
   void DecrementPendingConsumers();
-  void OnConsumerOrphaned(ConsumerChannel* channel);
+  void OnConsumerOrphaned(const uint64_t& consumer_state_mask);
 
   void AddConsumer(ConsumerChannel* channel);
   void RemoveConsumer(ConsumerChannel* channel);
@@ -91,7 +91,6 @@
   // highest bit is reserved for the producer and should not be set.
   uint64_t orphaned_consumer_bit_mask_{0ULL};
 
-  bool producer_owns_;
   LocalFence post_fence_;
   LocalFence returned_fence_;
   size_t user_metadata_size_;  // size of user requested buffer buffer size.
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index c6e8ea9..4b4301f 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -57,7 +57,6 @@
                                  int* error)
     : BufferHubChannel(service, channel_id, channel_id, kProducerType),
       pending_consumers_(0),
-      producer_owns_(true),
       user_metadata_size_(user_metadata_size),
       metadata_buf_size_(BufferHubDefs::kMetadataHeaderSize +
                          user_metadata_size) {
@@ -255,16 +254,16 @@
   // that release active_clients_bit_mask_ need to be visible here.
   uint64_t current_active_clients_bit_mask =
       active_clients_bit_mask_->load(std::memory_order_acquire);
-  uint64_t client_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
-      current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
-  if (client_state_mask == 0ULL) {
-    ALOGE(
-        "ProducerChannel::CreateConsumer: reached the maximum mumber of "
-        "consumers per producer: 63.");
+  uint64_t consumer_state_mask =
+      BufferHubDefs::FindNextAvailableClientStateMask(
+          current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
+  if (consumer_state_mask == 0ULL) {
+    ALOGE("%s: reached the maximum mumber of consumers per producer: 63.",
+          __FUNCTION__);
     return ErrorStatus(E2BIG);
   }
   uint64_t updated_active_clients_bit_mask =
-      current_active_clients_bit_mask | client_state_mask;
+      current_active_clients_bit_mask | consumer_state_mask;
   // Set the updated value only if the current value stays the same as what was
   // read before. If the comparison succeeds, update the value without
   // reordering anything before or after this read-modify-write in the current
@@ -276,24 +275,24 @@
   while (!active_clients_bit_mask_->compare_exchange_weak(
       current_active_clients_bit_mask, updated_active_clients_bit_mask,
       std::memory_order_acq_rel, std::memory_order_acquire)) {
-    ALOGE("Current active clients bit mask is changed to %" PRIx64
+    ALOGE("%s: Current active clients bit mask is changed to %" PRIx64
           ", which was expected to be %" PRIx64
           ". Trying to generate a new client state mask to resolve race "
           "condition.",
-          updated_active_clients_bit_mask, current_active_clients_bit_mask);
-    client_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
+          __FUNCTION__, updated_active_clients_bit_mask,
+          current_active_clients_bit_mask);
+    consumer_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
         current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
-    if (client_state_mask == 0ULL) {
-      ALOGE(
-          "ProducerChannel::CreateConsumer: reached the maximum mumber of "
-          "consumers per producer: 63.");
+    if (consumer_state_mask == 0ULL) {
+      ALOGE("%s: reached the maximum mumber of consumers per producer: %d.",
+            __FUNCTION__, (BufferHubDefs::kMaxNumberOfClients - 1));
       return ErrorStatus(E2BIG);
     }
     updated_active_clients_bit_mask =
-        current_active_clients_bit_mask | client_state_mask;
+        current_active_clients_bit_mask | consumer_state_mask;
   }
 
-  return {client_state_mask};
+  return {consumer_state_mask};
 }
 
 void ProducerChannel::RemoveConsumerClientMask(uint64_t consumer_state_mask) {
@@ -311,17 +310,14 @@
 
 Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(
     Message& message, uint64_t consumer_state_mask) {
-  ATRACE_NAME("ProducerChannel::CreateConsumer");
-  ALOGD_IF(TRACE,
-           "ProducerChannel::CreateConsumer: buffer_id=%d, producer_owns=%d",
-           buffer_id(), producer_owns_);
+  ATRACE_NAME(__FUNCTION__);
+  ALOGD_IF(TRACE, "%s: buffer_id=%d", __FUNCTION__, buffer_id());
 
   int channel_id;
   auto status = message.PushChannel(0, nullptr, &channel_id);
   if (!status) {
-    ALOGE(
-        "ProducerChannel::CreateConsumer: Failed to push consumer channel: %s",
-        status.GetErrorMessage().c_str());
+    ALOGE("%s: Failed to push consumer channel: %s", __FUNCTION__,
+          status.GetErrorMessage().c_str());
     RemoveConsumerClientMask(consumer_state_mask);
     return ErrorStatus(ENOMEM);
   }
@@ -331,24 +327,52 @@
       shared_from_this());
   const auto channel_status = service()->SetChannel(channel_id, consumer);
   if (!channel_status) {
-    ALOGE(
-        "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
-        "%s",
-        channel_status.GetErrorMessage().c_str());
+    ALOGE("%s: failed to set new consumer channel: %s.", __FUNCTION__,
+          channel_status.GetErrorMessage().c_str());
     RemoveConsumerClientMask(consumer_state_mask);
     return ErrorStatus(ENOMEM);
   }
 
   uint64_t current_buffer_state =
       buffer_state_->load(std::memory_order_acquire);
-  if (!producer_owns_ &&
-      (BufferHubDefs::IsBufferPosted(current_buffer_state) ||
-       BufferHubDefs::IsBufferAcquired(current_buffer_state))) {
-    // Signal the new consumer when adding it to a posted producer.
-    if (consumer->OnProducerPosted())
-      pending_consumers_++;
+  if (BufferHubDefs::IsBufferReleased(current_buffer_state) ||
+      BufferHubDefs::AnyClientGained(current_buffer_state)) {
+    return {status.take()};
   }
 
+  // Signal the new consumer when adding it to a posted producer.
+  bool update_buffer_state = true;
+  if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+                                     consumer_state_mask)) {
+    uint64_t updated_buffer_state =
+        current_buffer_state ^
+        (consumer_state_mask & BufferHubDefs::kHighBitsMask);
+    while (!buffer_state_->compare_exchange_weak(
+        current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+        std::memory_order_acquire)) {
+      ALOGI(
+          "%s: Failed to post to the new consumer. "
+          "Current buffer state was changed to %" PRIx64
+          " when trying to acquire the buffer and modify the buffer state to "
+          "%" PRIx64
+          ". About to try again if the buffer is still not gained nor fully "
+          "released.",
+          __FUNCTION__, current_buffer_state, updated_buffer_state);
+      if (BufferHubDefs::IsBufferReleased(current_buffer_state) ||
+          BufferHubDefs::AnyClientGained(current_buffer_state)) {
+        ALOGI("%s: buffer is gained or fully released, state=%" PRIx64 ".",
+              __FUNCTION__, current_buffer_state);
+        update_buffer_state = false;
+        break;
+      }
+      updated_buffer_state =
+          current_buffer_state ^
+          (consumer_state_mask & BufferHubDefs::kHighBitsMask);
+    }
+  }
+  if (update_buffer_state && consumer->OnProducerPosted())
+    pending_consumers_++;
+
   return {status.take()};
 }
 
@@ -366,10 +390,6 @@
                                              LocalFence acquire_fence) {
   ATRACE_NAME("ProducerChannel::OnProducerPost");
   ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id());
-  if (!producer_owns_) {
-    ALOGE("ProducerChannel::OnProducerPost: Not in gained state!");
-    return ErrorStatus(EBUSY);
-  }
 
   epoll_event event;
   event.events = 0;
@@ -400,7 +420,6 @@
            dummy_fence_count, buffer_id());
 
   post_fence_ = std::move(acquire_fence);
-  producer_owns_ = false;
 
   // Signal any interested consumers. If there are none, the buffer will stay
   // in posted state until a consumer comes online. This behavior guarantees
@@ -419,11 +438,6 @@
 Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) {
   ATRACE_NAME("ProducerChannel::OnGain");
   ALOGD_IF(TRACE, "ProducerChannel::OnGain: buffer_id=%d", buffer_id());
-  if (producer_owns_) {
-    ALOGE("ProducerChanneL::OnGain: Already in gained state: channel=%d",
-          channel_id());
-    return ErrorStatus(EALREADY);
-  }
 
   // There are still pending consumers, return busy.
   if (pending_consumers_ > 0) {
@@ -431,11 +445,13 @@
         "ProducerChannel::OnGain: Producer (id=%d) is gaining a buffer that "
         "still has %d pending consumer(s).",
         buffer_id(), pending_consumers_);
-    return ErrorStatus(EBUSY);
+    // TODO(b/77153033): add gain_posted_buffer to the impulse args, and
+    // return busy if the function does not allow gaining posted buffer.
+    // TODO(b/119331650): remove this if check if pending_consumers_ is removed.
+    // return ErrorStatus(EBUSY);
   }
 
   ClearAvailable();
-  producer_owns_ = true;
   post_fence_.close();
   return {std::move(returned_fence_)};
 }
@@ -449,10 +465,12 @@
            buffer_id());
 
   uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
-  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+  if (!BufferHubDefs::IsClientGained(
+      buffer_state, BufferHubDefs::kFirstClientStateMask)) {
     // Can only detach a BufferProducer when it's in gained state.
     ALOGW(
-        "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=0x%" PRIx64
+        "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=%"
+        PRIx64
         ") is not in gained state.",
         buffer_id(), buffer_state);
     return {};
@@ -485,12 +503,11 @@
   const auto channel_status =
       service()->SetChannel(channel_id, std::move(channel));
   if (!channel_status) {
-    // Technically, this should never fail, as we just pushed the channel. Note
-    // that LOG_FATAL will be stripped out in non-debug build.
+    // Technically, this should never fail, as we just pushed the channel.
+    // Note that LOG_FATAL will be stripped out in non-debug build.
     LOG_FATAL(
-        "ProducerChannel::OnProducerDetach: Failed to set new detached buffer "
-        "channel: %s.",
-        channel_status.GetErrorMessage().c_str());
+        "ProducerChannel::OnProducerDetach: Failed to set new detached "
+        "buffer channel: %s.", channel_status.GetErrorMessage().c_str());
   }
 
   return status;
@@ -500,13 +517,9 @@
   ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
   ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
            buffer_id());
-  if (producer_owns_) {
-    ALOGE("ProducerChannel::OnConsumerAcquire: Not in posted state!");
-    return ErrorStatus(EBUSY);
-  }
 
-  // Return a borrowed fd to avoid unnecessary duplication of the underlying fd.
-  // Serialization just needs to read the handle.
+  // Return a borrowed fd to avoid unnecessary duplication of the underlying
+  // fd. Serialization just needs to read the handle.
   return {std::move(post_fence_)};
 }
 
@@ -515,10 +528,6 @@
   ATRACE_NAME("ProducerChannel::OnConsumerRelease");
   ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: buffer_id=%d",
            buffer_id());
-  if (producer_owns_) {
-    ALOGE("ProducerChannel::OnConsumerRelease: Not in acquired state!");
-    return ErrorStatus(EBUSY);
-  }
 
   // Attempt to merge the fences if necessary.
   if (release_fence) {
@@ -539,28 +548,13 @@
   }
 
   DecrementPendingConsumers();
-  if (pending_consumers_ == 0) {
-    // Clear the producer bit atomically to transit into released state. This
-    // has to done by BufferHub as it requries synchronization among all
-    // consumers.
-    BufferHubDefs::ModifyBufferState(buffer_state_,
-                                     BufferHubDefs::kFirstClientBitMask, 0ULL);
-    ALOGD_IF(TRACE,
-             "ProducerChannel::OnConsumerRelease: releasing last consumer: "
-             "buffer_id=%d state=%" PRIx64 ".",
-             buffer_id(), buffer_state_->load(std::memory_order_acquire));
-
-    if (orphaned_consumer_bit_mask_) {
-      ALOGW(
-          "ProducerChannel::OnConsumerRelease: orphaned buffer detected "
-          "during the this acquire/release cycle: id=%d orphaned=0x%" PRIx64
-          " queue_index=%" PRIu64 ".",
-          buffer_id(), orphaned_consumer_bit_mask_,
-          metadata_header_->queue_index);
-      orphaned_consumer_bit_mask_ = 0;
-    }
-
-    SignalAvailable();
+  if (pending_consumers_ == 0 && orphaned_consumer_bit_mask_) {
+    ALOGW(
+        "%s: orphaned buffer detected during the this acquire/release cycle: "
+        "id=%d orphaned=0x%" PRIx64 " queue_index=%" PRIx64 ".",
+        __FUNCTION__, buffer_id(), orphaned_consumer_bit_mask_,
+        metadata_header_->queue_index);
+    orphaned_consumer_bit_mask_ = 0;
   }
 
   ALOGE_IF(
@@ -579,34 +573,43 @@
   }
 
   --pending_consumers_;
-  ALOGD_IF(TRACE,
-           "ProducerChannel::DecrementPendingConsumers: buffer_id=%d %d "
-           "consumers left",
+  ALOGD_IF(TRACE, "%s: buffer_id=%d %d consumers left", __FUNCTION__,
            buffer_id(), pending_consumers_);
+
+  if (pending_consumers_ == 0) {
+    ALOGD_IF(TRACE,
+             "%s: releasing last consumer: buffer_id=%d state=%" PRIx64 ".",
+             __FUNCTION__, buffer_id(),
+             buffer_state_->load(std::memory_order_acquire));
+    SignalAvailable();
+  }
 }
 
-void ProducerChannel::OnConsumerOrphaned(ConsumerChannel* channel) {
+void ProducerChannel::OnConsumerOrphaned(const uint64_t& consumer_state_mask) {
   // Ignore the orphaned consumer.
   DecrementPendingConsumers();
 
-  const uint64_t client_state_mask = channel->client_state_mask();
-  ALOGE_IF(orphaned_consumer_bit_mask_ & client_state_mask,
-           "ProducerChannel::OnConsumerOrphaned: Consumer "
-           "(client_state_mask=%" PRIx64 ") is already orphaned.",
-           client_state_mask);
-  orphaned_consumer_bit_mask_ |= client_state_mask;
+  // Remember the ignored consumer so that newly added consumer won't be
+  // taking the same state mask as this orphaned consumer.
+  ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_mask,
+           "%s: Consumer (consumer_state_mask=%" PRIx64
+           ") is already orphaned.",
+           __FUNCTION__, consumer_state_mask);
+  orphaned_consumer_bit_mask_ |= consumer_state_mask;
 
   // Atomically clear the fence state bit as an orphaned consumer will never
-  // signal a release fence. Also clear the buffer state as it won't be released
-  // as well.
-  fence_state_->fetch_and(~client_state_mask);
-  BufferHubDefs::ModifyBufferState(buffer_state_, client_state_mask, 0ULL);
+  // signal a release fence.
+  fence_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+
+  // Atomically set the buffer state of this consumer to released state.
+  buffer_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
 
   ALOGW(
-      "ProducerChannel::OnConsumerOrphaned: detected new orphaned consumer "
-      "buffer_id=%d client_state_mask=%" PRIx64 " queue_index=%" PRIu64
+      "%s: detected new orphaned consumer buffer_id=%d "
+      "consumer_state_mask=%" PRIx64 " queue_index=%" PRIx64
       " buffer_state=%" PRIx64 " fence_state=%" PRIx64 ".",
-      buffer_id(), client_state_mask, metadata_header_->queue_index,
+      __FUNCTION__, buffer_id(), consumer_state_mask,
+      metadata_header_->queue_index,
       buffer_state_->load(std::memory_order_acquire),
       fence_state_->load(std::memory_order_acquire));
 }
@@ -620,45 +623,64 @@
       std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
   // Restore the consumer state bit and make it visible in other threads that
   // acquire the active_clients_bit_mask_.
-  active_clients_bit_mask_->fetch_and(~channel->client_state_mask(),
-                                      std::memory_order_release);
+  uint64_t consumer_state_mask = channel->client_state_mask();
+  uint64_t current_active_clients_bit_mask =
+      active_clients_bit_mask_->load(std::memory_order_acquire);
+  uint64_t updated_active_clients_bit_mask =
+      current_active_clients_bit_mask & (~consumer_state_mask);
+  while (!active_clients_bit_mask_->compare_exchange_weak(
+      current_active_clients_bit_mask, updated_active_clients_bit_mask,
+      std::memory_order_acq_rel, std::memory_order_acquire)) {
+    ALOGI(
+        "%s: Failed to remove consumer state mask. Current active clients bit "
+        "mask is changed to %" PRIu64
+        " when trying to acquire and modify it to %" PRIu64
+        ". About to try again.",
+        __FUNCTION__, current_active_clients_bit_mask,
+        updated_active_clients_bit_mask);
+    updated_active_clients_bit_mask =
+        current_active_clients_bit_mask & (~consumer_state_mask);
+  }
 
-  const uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
-  if (BufferHubDefs::IsBufferPosted(buffer_state) ||
-      BufferHubDefs::IsBufferAcquired(buffer_state)) {
+  const uint64_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  if (BufferHubDefs::IsClientPosted(current_buffer_state,
+                                    consumer_state_mask) ||
+      BufferHubDefs::IsClientAcquired(current_buffer_state,
+                                      consumer_state_mask)) {
     // The consumer client is being destoryed without releasing. This could
     // happen in corner cases when the consumer crashes. Here we mark it
     // orphaned before remove it from producer.
-    OnConsumerOrphaned(channel);
+    OnConsumerOrphaned(consumer_state_mask);
+    return;
   }
 
-  if (BufferHubDefs::IsBufferReleased(buffer_state) ||
-      BufferHubDefs::IsBufferGained(buffer_state)) {
+  if (BufferHubDefs::IsClientReleased(current_buffer_state,
+                                      consumer_state_mask) ||
+      BufferHubDefs::AnyClientGained(current_buffer_state)) {
     // The consumer is being close while it is suppose to signal a release
     // fence. Signal the dummy fence here.
-    if (fence_state_->load(std::memory_order_acquire) &
-        channel->client_state_mask()) {
+    if (fence_state_->load(std::memory_order_acquire) & consumer_state_mask) {
       epoll_event event;
       event.events = EPOLLIN;
-      event.data.u64 = channel->client_state_mask();
+      event.data.u64 = consumer_state_mask;
       if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
                     dummy_fence_fd_.Get(), &event) < 0) {
         ALOGE(
-            "ProducerChannel::RemoveConsumer: Failed to modify the shared "
-            "release fence to include the dummy fence: %s",
-            strerror(errno));
+            "%s: Failed to modify the shared release fence to include the "
+            "dummy fence: %s",
+            __FUNCTION__, strerror(errno));
         return;
       }
-      ALOGW(
-          "ProducerChannel::RemoveConsumer: signal dummy release fence "
-          "buffer_id=%d",
-          buffer_id());
+      ALOGW("%s: signal dummy release fence buffer_id=%d", __FUNCTION__,
+            buffer_id());
       eventfd_write(dummy_fence_fd_.Get(), 1);
     }
   }
 }
 
-// Returns true if the given parameters match the underlying buffer parameters.
+// Returns true if the given parameters match the underlying buffer
+// parameters.
 bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height,
                                       uint32_t layer_count, uint32_t format,
                                       uint64_t usage,
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
index 6b5027c..6b33f50 100644
--- a/services/vr/bufferhubd/producer_queue_channel.cpp
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -319,7 +319,12 @@
     return ErrorStatus(EINVAL);
   }
   uint64_t buffer_state = producer_channel->buffer_state();
-  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+  // TODO(b/112007999) add an atomic variable in metadata header in shared
+  // memory to indicate which client is the last producer of the buffer.
+  // Currently, the first client is the only producer to the buffer.
+  // Thus, it checks whether the first client gains the buffer below.
+  if (!BufferHubDefs::IsClientGained(buffer_state,
+                                     BufferHubDefs::kFirstClientBitMask)) {
     // Rejects the request if the requested buffer is not in Gained state.
     ALOGE(
         "ProducerQueueChannel::InsertBuffer: The buffer (cid=%d, "
diff --git a/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
index 7c00fa6..94b422a 100644
--- a/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
+++ b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
@@ -47,24 +47,24 @@
 
   uint64_t token1 = 0ULL;
   status_t ret = bufferClient->duplicate(&token1);
-  EXPECT_EQ(ret, NO_ERROR);
+  EXPECT_EQ(ret, OK);
 
   // Tokens should be unique even corresponding to the same buffer
   uint64_t token2 = 0ULL;
   ret = bufferClient->duplicate(&token2);
-  EXPECT_EQ(ret, NO_ERROR);
+  EXPECT_EQ(ret, OK);
   EXPECT_NE(token2, token1);
 
   sp<IBufferClient> bufferClient1;
   ret = service->importBuffer(token1, &bufferClient1);
-  EXPECT_EQ(ret, NO_ERROR);
+  EXPECT_EQ(ret, OK);
   ASSERT_THAT(bufferClient1, NotNull());
   EXPECT_TRUE(bufferClient1->isValid());
 
   // Consumes the token to keep the service clean
   sp<IBufferClient> bufferClient2;
   ret = service->importBuffer(token2, &bufferClient2);
-  EXPECT_EQ(ret, NO_ERROR);
+  EXPECT_EQ(ret, OK);
   ASSERT_THAT(bufferClient2, NotNull());
   EXPECT_TRUE(bufferClient2->isValid());
 }
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 417460c..fb7932d 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -1016,7 +1016,7 @@
   std::lock_guard<std::mutex> guard(mutex_);
   if (callback_ != nullptr)
     callback_->onVsync(kDefaultDisplayId, vsync_timestamp);
-  return NO_ERROR;
+  return OK;
 }
 
 void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) {