Merge "Activity manager: native callback for process state"
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/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index aad9939..25e5247 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -313,9 +313,14 @@
         bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
                                 vold_decrypt == "1";
 
-        const std::string resolve_startup_string_arg  =
+        const std::string resolve_startup_string_arg =
                 MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
                                  "--resolve-startup-const-strings=%s");
+
+        const std::string image_block_size_arg =
+                MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
+                                 "--max-image-block-size=%s");
+
         const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
 
         std::string image_format_arg;
@@ -430,6 +435,7 @@
         AddRuntimeArg(dex2oat_Xmx_arg);
 
         AddArg(resolve_startup_string_arg);
+        AddArg(image_block_size_arg);
         AddArg(dex2oat_compiler_filter_arg);
         AddArg(dex2oat_threads_arg);
         AddArg(dex2oat_swap_fd);
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 561f5ba..d6021c0 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -36,12 +36,15 @@
     <feature name="android.hardware.type.automotive" />
 
     <!-- basic system services -->
-    <feature name="android.software.app_widgets" />
     <feature name="android.software.connectionservice" />
     <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/android/system_fonts.h b/include/android/system_fonts.h
index c541511..38f036e 100644
--- a/include/android/system_fonts.h
+++ b/include/android/system_fonts.h
@@ -24,7 +24,7 @@
  * The ASystemFontIterator_open method will give you an iterator which can iterate all system
  * installed font files as shown in the following example.
  *
- * <code>
+ * \code{.cpp}
  *   ASystemFontIterator* iterator = ASystemFontIterator_open();
  *   ASystemFont* font = NULL;
  *
@@ -50,7 +50,7 @@
  *
  *   // Use this font for your text rendering engine.
  *
- * </code>
+ * \endcode
  *
  * Available since API level 29.
  */
@@ -142,7 +142,7 @@
 /**
  * Close an opened system font iterator, freeing any related resources.
  *
- * \param a pointer of an iterator for the system fonts. Do nothing if NULL is passed.
+ * \param iterator a pointer of an iterator for the system fonts. Do nothing if NULL is passed.
  */
 void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29);
 
@@ -174,7 +174,7 @@
  * drawing Tofu character.
  *
  * Examples:
- * <code>
+ * \code{.cpp}
  *  // Simple font query for the ASCII character.
  *  std::vector<uint16_t> text = { 'A' };
  *  ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
@@ -202,15 +202,15 @@
  *  ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
  *      "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
  *  // runLength will be 1 and the font will points a Hebrew font.
- * </code>
+ * \endcode
  *
  * \param familyName a null character terminated font family name
  * \param weight a font weight value. Only from 0 to 1000 value is valid
  * \param italic true if italic, otherwise false.
  * \param languageTags a null character terminated comma separated IETF BCP47 compliant language
  *                     tags.
- * \param text a UTF-16 encoded text buffer to be rendered.
- * \param textLength a length of the given text buffer.
+ * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string.
+ * \param textLength a length of the given text buffer. This must not be zero.
  * \param runLengthOut if not null, the font run length will be filled.
  * \return a font to be used for given text and params. You need to release the returned font by
  *         ASystemFont_close when it is no longer needed.
@@ -239,7 +239,7 @@
  * The font file returned is guaranteed to be opend with O_RDONLY.
  * Note that the returned pointer is valid until ASystemFont_close() is called for the given font.
  *
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
  * \return a string of the font file path.
  */
 const char* _Nonnull ASystemFont_getFontFilePath(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -251,14 +251,11 @@
  * Here are pairs of the common names and their values.
  * <p>
  *  <table>
- *  <thead>
  *  <tr>
  *  <th align="center">Value</th>
  *  <th align="center">Name</th>
  *  <th align="center">NDK Definition</th>
  *  </tr>
- *  </thead>
- *  <tbody>
  *  <tr>
  *  <td align="center">100</td>
  *  <td align="center">Thin</td>
@@ -304,13 +301,13 @@
  *  <td align="center">Black (Heavy)</td>
  *  <td align="center">{@link ASYSTEM_FONT_WEIGHT_BLACK}</td>
  *  </tr>
- *  </tbody>
+ *  </table>
  * </p>
  * Note that the weight value may fall in between above values, e.g. 250 weight.
  *
  * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass)
  *
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
  * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned.
  */
 uint16_t ASystemFont_getWeight(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -318,7 +315,7 @@
 /**
  * Return true if the current font is italic, otherwise returns false.
  *
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
  * \return true if italic, otherwise false.
  */
 bool ASystemFont_isItalic(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -330,7 +327,7 @@
  *
  * Note that the returned pointer is valid until ASystemFont_close() is called.
  *
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
  * \return a IETF BCP47 compliant langauge tag or nullptr if not available.
  */
 const char* _Nullable ASystemFont_getLocale(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -342,7 +339,7 @@
  * returns a non-negative value as an font offset in the collection. This
  * always returns 0 if the target font file is a regular font.
  *
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
  * \return a font collection index.
  */
 size_t ASystemFont_getCollectionIndex(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -356,7 +353,7 @@
  *     'wght' 700, 'slnt' -12
  * In this case, ASystemFont_getAxisCount returns 2 and ASystemFont_getAxisTag
  * and ASystemFont_getAxisValue will return following values.
- * <code>
+ * \code{.cpp}
  *     ASystemFont* font = ASystemFontIterator_next(ite);
  *
  *     // Returns the number of axes
@@ -369,11 +366,11 @@
  *     // Returns the tag-value pair for the second axis.
  *     ASystemFont_getAxisTag(font, 1);  // Returns 'slnt'(0x736c6e74)
  *     ASystemFont_getAxisValue(font, 1);  // Returns -12.0
- * </code>
+ * \endcode
  *
  * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar)
  *
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
  * \return a number of font variation settings.
  */
 size_t ASystemFont_getAxisCount(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -384,8 +381,8 @@
  *
  * See ASystemFont_getAxisCount for more details.
  *
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
- * \param an index to the font variation settings. Passing value larger than or
+ * \param font a font object. Passing NULL is not allowed.
+ * \param axisIndex an index to the font variation settings. Passing value larger than or
  *        equal to {@link ASystemFont_getAxisCount} is not allowed.
  * \return an OpenType axis tag value for the given font variation setting.
  */
@@ -397,8 +394,8 @@
  *
  * See ASystemFont_getAxisCount for more details.
  *
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
- * \param an index to the font variation settings. Passing value larger than or
+ * \param font a font object. Passing NULL is not allowed.
+ * \param axisIndex an index to the font variation settings. Passing value larger than or
  *         equal to {@link ASYstemFont_getAxisCount} is not allwed.
  * \return a float value for the given font variation setting.
  */
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 0de3bb7..fa456bb 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -20,6 +20,8 @@
 #include <android-base/stringprintf.h>
 #include <ui/DisplayInfo.h>
 #include <input/Input.h>
+#include <inttypes.h>
+#include <optional>
 
 using android::base::StringPrintf;
 
@@ -66,13 +68,17 @@
     int32_t deviceWidth;
     int32_t deviceHeight;
     std::string uniqueId;
+    // The actual (hardware) port that the associated display is connected to.
+    // Not all viewports will have this specified.
+    std::optional<uint8_t> physicalPort;
     ViewportType type;
 
     DisplayViewport() :
             displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
             logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
             physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
-            deviceWidth(0), deviceHeight(0), uniqueId(), type(ViewportType::VIEWPORT_INTERNAL) {
+            deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt),
+            type(ViewportType::VIEWPORT_INTERNAL) {
     }
 
     bool operator==(const DisplayViewport& other) const {
@@ -89,6 +95,7 @@
                 && deviceWidth == other.deviceWidth
                 && deviceHeight == other.deviceHeight
                 && uniqueId == other.uniqueId
+                && physicalPort == other.physicalPort
                 && type == other.type;
     }
 
@@ -114,16 +121,19 @@
         deviceWidth = width;
         deviceHeight = height;
         uniqueId.clear();
+        physicalPort = std::nullopt;
         type = ViewportType::VIEWPORT_INTERNAL;
     }
 
     std::string toString() const {
-        return StringPrintf("Viewport %s: displayId=%d, orientation=%d, "
+        return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, "
             "logicalFrame=[%d, %d, %d, %d], "
             "physicalFrame=[%d, %d, %d, %d], "
             "deviceSize=[%d, %d]",
-            viewportTypeToString(type),
-            displayId, orientation,
+            viewportTypeToString(type), displayId,
+            uniqueId.c_str(),
+            physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>",
+            orientation,
             logicalLeft, logicalTop,
             logicalRight, logicalBottom,
             physicalLeft, physicalTop,
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
index 1ef8986..610834d 100644
--- a/include/input/IInputFlinger.h
+++ b/include/input/IInputFlinger.h
@@ -36,6 +36,9 @@
     DECLARE_META_INTERFACE(InputFlinger)
 
     virtual void setInputWindows(const Vector<InputWindowInfo>& inputHandles) = 0;
+
+    virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
+    virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
 };
 
 
@@ -46,6 +49,8 @@
 public:
     enum {
         SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+        REGISTER_INPUT_CHANNEL_TRANSACTION,
+        UNREGISTER_INPUT_CHANNEL_TRANSACTION
     };
 
     virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/input/Input.h b/include/input/Input.h
index cc45aef..ee22bc6 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -244,7 +244,12 @@
     float getAxisValue(int32_t axis) const;
     status_t setAxisValue(int32_t axis, float value);
 
-    void scale(float scale);
+    void scale(float globalScale);
+
+    // Scale the pointer coordinates according to a global scale and a
+    // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR
+    // axes, however the window scaling will not.
+    void scale(float globalScale, float windowXScale, float windowYScale);
     void applyOffset(float xOffset, float yOffset);
 
     inline float getX() const {
@@ -595,7 +600,7 @@
 
     void offsetLocation(float xOffset, float yOffset);
 
-    void scale(float scaleFactor);
+    void scale(float globalScaleFactor);
 
     // Apply 3x3 perspective matrix transformation.
     // Matrix is in row-major form and compatible with SkMatrix.
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 9b365b9..71a8f20 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -19,6 +19,9 @@
 
 #include <string>
 
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+
 #include <input/Input.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
@@ -29,8 +32,12 @@
  * Describes the properties of an application that can receive input.
  */
 struct InputApplicationInfo {
+    sp<IBinder> token;
     std::string name;
     nsecs_t dispatchingTimeout;
+
+    status_t write(Parcel& output) const;
+    static InputApplicationInfo read(const Parcel& from);
 };
 
 
@@ -54,6 +61,10 @@
         return mInfo ? mInfo->dispatchingTimeout : defaultValue;
     }
 
+    inline sp<IBinder> getApplicationToken() const {
+        return mInfo ? mInfo->token : nullptr;
+    }
+
     /**
      * Requests that the state of this object be updated to reflect
      * the most current available information about the application.
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/include/input/InputWindow.h b/include/input/InputWindow.h
index 9e3d334..8dd95cf 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -116,17 +116,42 @@
         INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
         INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
     };
-
-    sp<InputChannel> inputChannel;
+    
+    /* These values are filled in by the WM and passed through SurfaceFlinger
+     * unless specified otherwise.
+     */
+    sp<IBinder> token;
     std::string name;
     int32_t layoutParamsFlags;
     int32_t layoutParamsType;
     nsecs_t dispatchingTimeout;
+
+    /* These values are filled in by SurfaceFlinger. */
     int32_t frameLeft;
     int32_t frameTop;
     int32_t frameRight;
     int32_t frameBottom;
-    float scaleFactor;
+
+    /*
+     * SurfaceFlinger consumes this value to shrink the computed frame. This is
+     * different from shrinking the touchable region in that it DOES shift the coordinate
+     * space where-as the touchable region does not and is more like "cropping". This
+     * is used for window shadows.
+     */
+    int32_t surfaceInset = 0;
+
+    // A global scaling factor for all windows. Unlike windowScaleX/Y this results
+    // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
+    float globalScaleFactor;
+
+    // Scaling factors applied to individual windows.
+    float windowXScale = 1.0f;
+    float windowYScale = 1.0f;
+
+    /*
+     * This is filled in by the WM relative to the frame and then translated
+     * to absolute coordinates by SurfaceFlinger once the frame is computed.
+     */
     Region touchableRegion;
     bool visible;
     bool canReceiveKeys;
@@ -138,6 +163,7 @@
     int32_t ownerUid;
     int32_t inputFeatures;
     int32_t displayId;
+    InputApplicationInfo applicationInfo;
 
     void addTouchableRegion(const Rect& region);
 
@@ -168,20 +194,23 @@
  */
 class InputWindowHandle : public RefBase {
 public:
-    const sp<InputApplicationHandle> inputApplicationHandle;
 
     inline const InputWindowInfo* getInfo() const {
         return &mInfo;
     }
 
-    sp<InputChannel> getInputChannel() const;
+    sp<IBinder> getToken() const;
+
+    sp<IBinder> getApplicationToken() {
+        return mInfo.applicationInfo.token;
+    }
 
     inline std::string getName() const {
-        return mInfo.inputChannel ? mInfo.name : "<invalid>";
+        return mInfo.token ? mInfo.name : "<invalid>";
     }
 
     inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
-        return mInfo.inputChannel? mInfo.dispatchingTimeout : defaultValue;
+        return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
     }
 
     /**
@@ -202,7 +231,7 @@
     void releaseChannel();
 
 protected:
-    explicit InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
+    explicit InputWindowHandle();
     virtual ~InputWindowHandle();
 
     InputWindowInfo mInfo;
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index f0c21f5..8df83f1 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -109,9 +109,7 @@
     "BC_DEAD_BINDER_DONE"
 };
 
-// The work source represents the UID of the process we should attribute the transaction to.
-// We use -1 to specify that the work source was not set using #setWorkSource.
-static const int kUnsetWorkSource = -1;
+static const int64_t kWorkSourcePropagatedBitIndex = 32;
 
 static const char* getReturnString(uint32_t cmd)
 {
@@ -389,12 +387,29 @@
 
 int64_t IPCThreadState::setCallingWorkSourceUid(uid_t uid)
 {
-    // Note: we currently only use half of the int64. We return an int64 for extensibility.
-    int64_t token = mWorkSource;
+    int64_t token = setCallingWorkSourceUidWithoutPropagation(uid);
+    mPropagateWorkSource = true;
+    return token;
+}
+
+int64_t IPCThreadState::setCallingWorkSourceUidWithoutPropagation(uid_t uid)
+{
+    const int64_t propagatedBit = ((int64_t)mPropagateWorkSource) << kWorkSourcePropagatedBitIndex;
+    int64_t token = propagatedBit | mWorkSource;
     mWorkSource = uid;
     return token;
 }
 
+void IPCThreadState::clearPropagateWorkSource()
+{
+    mPropagateWorkSource = false;
+}
+
+bool IPCThreadState::shouldPropagateWorkSource() const
+{
+    return mPropagateWorkSource;
+}
+
 uid_t IPCThreadState::getCallingWorkSourceUid() const
 {
     return mWorkSource;
@@ -408,7 +423,8 @@
 void IPCThreadState::restoreCallingWorkSource(int64_t token)
 {
     uid_t uid = (int)token;
-    setCallingWorkSourceUid(uid);
+    setCallingWorkSourceUidWithoutPropagation(uid);
+    mPropagateWorkSource = ((token >> kWorkSourcePropagatedBitIndex) & 1) == 1;
 }
 
 void IPCThreadState::setLastTransactionBinderFlags(int32_t flags)
@@ -765,6 +781,7 @@
 IPCThreadState::IPCThreadState()
     : mProcess(ProcessState::self()),
       mWorkSource(kUnsetWorkSource),
+      mPropagateWorkSource(false),
       mStrictModePolicy(0),
       mLastTransactionBinderFlags(0)
 {
@@ -1127,6 +1144,13 @@
             const uid_t origUid = mCallingUid;
             const int32_t origStrictModePolicy = mStrictModePolicy;
             const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
+            const int32_t origWorkSource = mWorkSource;
+            const bool origPropagateWorkSet = mPropagateWorkSource;
+            // Calling work source will be set by Parcel#enforceInterface. Parcel#enforceInterface
+            // is only guaranteed to be called for AIDL-generated stubs so we reset the work source
+            // here to never propagate it.
+            clearCallingWorkSource();
+            clearPropagateWorkSource();
 
             mCallingPid = tr.sender_pid;
             mCallingUid = tr.sender_euid;
@@ -1179,6 +1203,8 @@
             mCallingUid = origUid;
             mStrictModePolicy = origStrictModePolicy;
             mLastTransactionBinderFlags = origTransactionBinderFlags;
+            mWorkSource = origWorkSource;
+            mPropagateWorkSource = origPropagateWorkSet;
 
             IF_LOG_TRANSACTIONS() {
                 TextOutput::Bundle _b(alog);
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 643f428..a0a634a 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -599,9 +599,10 @@
 // Write RPC headers.  (previously just the interface token)
 status_t Parcel::writeInterfaceToken(const String16& interface)
 {
-    writeInt32(IPCThreadState::self()->getStrictModePolicy() |
-               STRICT_MODE_PENALTY_GATHER);
-    writeInt32(IPCThreadState::self()->getCallingWorkSourceUid());
+    const IPCThreadState* threadState = IPCThreadState::self();
+    writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
+    writeInt32(threadState->shouldPropagateWorkSource() ?
+            threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
     // currently the interface identification token is just its name as a string
     return writeString16(interface);
 }
@@ -631,7 +632,7 @@
     }
     // WorkSource.
     int32_t workSource = readInt32();
-    threadState->setCallingWorkSourceUid(workSource);
+    threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
     // Interface descriptor.
     const String16 str(readString16());
     if (str == interface) {
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index de126d5..26e8c0b 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -49,12 +49,16 @@
 
             // See Binder#setCallingWorkSourceUid in Binder.java.
             int64_t             setCallingWorkSourceUid(uid_t uid);
+            // Internal only. Use setCallingWorkSourceUid(uid) instead.
+            int64_t             setCallingWorkSourceUidWithoutPropagation(uid_t uid);
             // See Binder#getCallingWorkSourceUid in Binder.java.
             uid_t               getCallingWorkSourceUid() const;
             // See Binder#clearCallingWorkSource in Binder.java.
             int64_t             clearCallingWorkSource();
             // See Binder#restoreCallingWorkSource in Binder.java.
             void                restoreCallingWorkSource(int64_t token);
+            void                clearPropagateWorkSource();
+            bool                shouldPropagateWorkSource() const;
 
             void                setLastTransactionBinderFlags(int32_t flags);
             int32_t             getLastTransactionBinderFlags() const;
@@ -127,6 +131,13 @@
             // infer information about thread state.
             bool                isServingCall() const;
 
+            // The work source represents the UID of the process we should attribute the transaction
+            // to. We use -1 to specify that the work source was not set using #setWorkSource.
+            //
+            // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
+            // side.
+            static const int32_t kUnsetWorkSource = -1;
+
 private:
                                 IPCThreadState();
                                 ~IPCThreadState();
@@ -167,6 +178,8 @@
             // The UID of the process who is responsible for this transaction.
             // This is used for resource attribution.
             int32_t             mWorkSource;
+            // Whether the work source should be propagated.
+            bool                mPropagateWorkSource;
             int32_t             mStrictModePolicy;
             int32_t             mLastTransactionBinderFlags;
             IPCThreadStateBase  *mIPCThreadStateBase;
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/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
index e2c0cfa..c6fcaa4 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
@@ -163,7 +163,9 @@
     ScopedAResource& operator=(ScopedAResource&&) = delete;
 
     // move-constructing is okay
-    ScopedAResource(ScopedAResource&&) = default;
+    ScopedAResource(ScopedAResource&& other) : mT(std::move(other.mT)) {
+      other.mT = DEFAULT;
+    }
 
    private:
     T mT;
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 3594349..866af70 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -54,6 +54,31 @@
 void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29);
 
 /**
+ * Sets the position within the parcel.
+ *
+ * \param parcel The parcel of which to set the position.
+ * \param position Position of the parcel to set. This must be a value returned by
+ * AParcel_getDataPosition. Positions are constant for a given parcel between processes.
+ *
+ * \return STATUS_OK on success. If position is negative, then STATUS_BAD_VALUE will be returned.
+ */
+binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position)
+        __INTRODUCED_IN(29);
+
+/**
+ * Gets the current position within the parcel.
+ *
+ * \param parcel The parcel of which to get the position.
+ *
+ * \return The size of the parcel. This will always be greater than 0. The values returned by this
+ * function before and after calling various reads and writes are not defined. Only the delta
+ * between two positions between a specific sequence of calls is defined. For instance, if position
+ * is X, writeBool is called, and then position is Y, readBool can be called from position X will
+ * return the same value, and then position will be Y.
+ */
+int32_t AParcel_getDataPosition(const AParcel* parcel) __INTRODUCED_IN(29);
+
+/**
  * This is called to allocate a buffer for a C-style string (null-terminated). The returned buffer
  * should be at least length bytes. This includes space for a null terminator. For a string, length
  * will always be strictly less than or equal to the maximum size that can be held in a size_t and
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 41df90b..4328b6e 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -25,6 +25,7 @@
     AIBinder_Weak_new;
     AIBinder_Weak_promote;
     AParcel_delete;
+    AParcel_getDataPosition;
     AParcel_readBool;
     AParcel_readBoolArray;
     AParcel_readByte;
@@ -48,6 +49,7 @@
     AParcel_readUint32Array;
     AParcel_readUint64;
     AParcel_readUint64Array;
+    AParcel_setDataPosition;
     AParcel_writeBool;
     AParcel_writeBoolArray;
     AParcel_writeByte;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 3c32100..2d68559 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -212,6 +212,19 @@
     delete parcel;
 }
 
+binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position) {
+    if (position < 0) {
+        return STATUS_BAD_VALUE;
+    }
+
+    parcel->get()->setDataPosition(position);
+    return STATUS_OK;
+}
+
+int32_t AParcel_getDataPosition(const AParcel* parcel) {
+    return parcel->get()->dataPosition();
+}
+
 binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) {
     sp<IBinder> writeBinder = binder != nullptr ? binder->getBinder() : nullptr;
     return parcel->get()->writeStrongBinder(writeBinder);
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/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 22c1bad..cd37d49 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -180,6 +180,7 @@
     public:
         virtual void SetUp() {
             m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
+            IPCThreadState::self()->restoreCallingWorkSource(0); 
         }
         virtual void TearDown() {
         }
@@ -953,11 +954,28 @@
 {
     status_t ret;
     Parcel data, reply;
+    IPCThreadState::self()->clearCallingWorkSource();
     int64_t previousWorkSource = IPCThreadState::self()->setCallingWorkSourceUid(100);
     data.writeInterfaceToken(binderLibTestServiceName);
     ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
     EXPECT_EQ(100, reply.readInt32());
     EXPECT_EQ(-1, previousWorkSource);
+    EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceSetWithoutPropagation)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    IPCThreadState::self()->setCallingWorkSourceUidWithoutPropagation(100);
+    EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+    EXPECT_EQ(-1, reply.readInt32());
+    EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
     EXPECT_EQ(NO_ERROR, ret);
 }
 
@@ -967,7 +985,8 @@
     Parcel data, reply;
 
     IPCThreadState::self()->setCallingWorkSourceUid(100);
-    int64_t previousWorkSource = IPCThreadState::self()->clearCallingWorkSource();
+    int64_t token = IPCThreadState::self()->clearCallingWorkSource();
+    int32_t previousWorkSource = (int32_t)token;
     data.writeInterfaceToken(binderLibTestServiceName);
     ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
 
@@ -989,9 +1008,58 @@
     ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
 
     EXPECT_EQ(100, reply.readInt32());
+    EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
     EXPECT_EQ(NO_ERROR, ret);
 }
 
+TEST_F(BinderLibTest, PropagateFlagSet)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    IPCThreadState::self()->clearPropagateWorkSource();
+    IPCThreadState::self()->setCallingWorkSourceUid(100);
+    EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, PropagateFlagCleared)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    IPCThreadState::self()->setCallingWorkSourceUid(100);
+    IPCThreadState::self()->clearPropagateWorkSource();
+    EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, PropagateFlagRestored)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    int token = IPCThreadState::self()->setCallingWorkSourceUid(100);
+    IPCThreadState::self()->restoreCallingWorkSource(token);
+
+    EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls)
+{
+    IPCThreadState::self()->setCallingWorkSourceUid(100);
+
+    Parcel data, reply;
+    status_t ret;
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+
+    Parcel data2, reply2;
+    status_t ret2;
+    data2.writeInterfaceToken(binderLibTestServiceName);
+    ret2 = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data2, &reply2);
+    EXPECT_EQ(100, reply2.readInt32());
+    EXPECT_EQ(NO_ERROR, ret2);
+}
+
 class BinderLibTestService : public BBinder
 {
     public:
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 0d224d8..04884bb 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -48,6 +48,7 @@
         "android.hardware.drm@1.0::IDrmFactory",
         "android.hardware.graphics.allocator@2.0::IAllocator",
         "android.hardware.graphics.composer@2.1::IComposer",
+        "android.hardware.health@2.0::IHealth",
         "android.hardware.media.omx@1.0::IOmx",
         "android.hardware.media.omx@1.0::IOmxStore",
         "android.hardware.sensors@1.0::ISensors",
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index bab87ac..280c14a 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -22,8 +22,9 @@
     cflags: ["-Wall", "-Werror"],
 
     shared_libs: [
-        "liblog",
+        "libbase",
         "libcutils",
+        "liblog",
     ],
 
     export_include_dirs: ["include"],
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index afa32b6..c2a6764 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -18,32 +18,99 @@
 #define LOG_TAG "GraphicsEnv"
 #include <graphicsenv/GraphicsEnv.h>
 
+#include <dlfcn.h>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android/dlext.h>
+#include <cutils/properties.h>
+#include <log/log.h>
 #include <sys/prctl.h>
 
 #include <mutex>
 
-#include <android/dlext.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-
 // TODO(b/37049319) Get this from a header once one exists
 extern "C" {
-  android_namespace_t* android_get_exported_namespace(const char*);
-  android_namespace_t* android_create_namespace(const char* name,
-                                                const char* ld_library_path,
-                                                const char* default_library_path,
-                                                uint64_t type,
-                                                const char* permitted_when_isolated_path,
-                                                android_namespace_t* parent);
+android_namespace_t* android_get_exported_namespace(const char*);
+android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
+                                              const char* default_library_path, uint64_t type,
+                                              const char* permitted_when_isolated_path,
+                                              android_namespace_t* parent);
+bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to,
+                             const char* shared_libs_sonames);
 
-  enum {
-     ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
-     ANDROID_NAMESPACE_TYPE_SHARED = 2,
-  };
+enum {
+    ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+    ANDROID_NAMESPACE_TYPE_SHARED = 2,
+};
 }
 
 namespace android {
 
+enum NativeLibrary {
+    LLNDK = 0,
+    VNDKSP = 1,
+};
+
+static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt",
+                                                                   "/etc/vndksp.libraries.txt"};
+
+static std::string vndkVersionStr() {
+#ifdef __BIONIC__
+    std::string version = android::base::GetProperty("ro.vndk.version", "");
+    if (version != "" && version != "current") {
+        return "." + version;
+    }
+#endif
+    return "";
+}
+
+static void insertVndkVersionStr(std::string* fileName) {
+    LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr");
+    size_t insertPos = fileName->find_last_of(".");
+    if (insertPos == std::string::npos) {
+        insertPos = fileName->length();
+    }
+    fileName->insert(insertPos, vndkVersionStr());
+}
+
+static bool readConfig(const std::string& configFile, std::vector<std::string>* soNames) {
+    // Read list of public native libraries from the config file.
+    std::string fileContent;
+    if (!base::ReadFileToString(configFile, &fileContent)) {
+        return false;
+    }
+
+    std::vector<std::string> lines = base::Split(fileContent, "\n");
+
+    for (auto& line : lines) {
+        auto trimmedLine = base::Trim(line);
+        if (!trimmedLine.empty()) {
+            soNames->push_back(trimmedLine);
+        }
+    }
+
+    return true;
+}
+
+static const std::string getSystemNativeLibraries(NativeLibrary type) {
+    static const char* androidRootEnv = getenv("ANDROID_ROOT");
+    static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system";
+
+    std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type];
+
+    insertVndkVersionStr(&nativeLibrariesSystemConfig);
+
+    std::vector<std::string> soNames;
+    if (!readConfig(nativeLibrariesSystemConfig, &soNames)) {
+        ALOGE("Failed to retrieve library names from %s", nativeLibrariesSystemConfig.c_str());
+        return "";
+    }
+
+    return base::Join(soNames, ':');
+}
+
 /*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
     static GraphicsEnv env;
     return env;
@@ -59,8 +126,8 @@
 
 void GraphicsEnv::setDriverPath(const std::string path) {
     if (!mDriverPath.empty()) {
-        ALOGV("ignoring attempt to change driver path from '%s' to '%s'",
-                mDriverPath.c_str(), path.c_str());
+        ALOGV("ignoring attempt to change driver path from '%s' to '%s'", mDriverPath.c_str(),
+              path.c_str());
         return;
     }
     ALOGV("setting driver path to '%s'", path.c_str());
@@ -102,7 +169,7 @@
         mAppNamespace = appNamespace;
     } else {
         ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'",
-                layerPaths.c_str(), appNamespace);
+              layerPaths.c_str(), appNamespace);
     }
 }
 
@@ -154,20 +221,41 @@
 android_namespace_t* GraphicsEnv::getDriverNamespace() {
     static std::once_flag once;
     std::call_once(once, [this]() {
-        if (mDriverPath.empty())
-            return;
-        // If the sphal namespace isn't configured for a device, don't support updatable drivers.
-        // We need a parent namespace to inherit the default search path from.
-        auto sphalNamespace = android_get_exported_namespace("sphal");
-        if (!sphalNamespace) return;
+        if (mDriverPath.empty()) return;
+
+        auto vndkNamespace = android_get_exported_namespace("vndk");
+        if (!vndkNamespace) return;
+
         mDriverNamespace = android_create_namespace("gfx driver",
                                                     mDriverPath.c_str(), // ld_library_path
                                                     mDriverPath.c_str(), // default_library_path
-                                                    ANDROID_NAMESPACE_TYPE_SHARED |
-                                                            ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                                    ANDROID_NAMESPACE_TYPE_ISOLATED,
                                                     nullptr, // permitted_when_isolated_path
-                                                    sphalNamespace);
+                                                    nullptr);
+
+        const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK);
+        if (llndkLibraries.empty()) {
+            mDriverNamespace = nullptr;
+            return;
+        }
+        if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) {
+            ALOGE("Failed to link default namespace[%s]", dlerror());
+            mDriverNamespace = nullptr;
+            return;
+        }
+
+        const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP);
+        if (vndkspLibraries.empty()) {
+            mDriverNamespace = nullptr;
+            return;
+        }
+        if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) {
+            ALOGE("Failed to link vndk namespace[%s]", dlerror());
+            mDriverNamespace = nullptr;
+            return;
+        }
     });
+
     return mDriverNamespace;
 }
 
diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp
index b715e43..add3ef0 100644
--- a/libs/gui/HdrMetadata.cpp
+++ b/libs/gui/HdrMetadata.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <gui/HdrMetadata.h>
+#include <limits>
 
 namespace android {
 
@@ -26,6 +27,10 @@
     if (validTypes & CTA861_3) {
         size += sizeof(cta8613);
     }
+    if (validTypes & HDR10PLUS) {
+        size += sizeof(size_t);
+        size += hdr10plus.size();
+    }
     return size;
 }
 
@@ -41,6 +46,12 @@
     if (validTypes & CTA861_3) {
         FlattenableUtils::write(buffer, size, cta8613);
     }
+    if (validTypes & HDR10PLUS) {
+        size_t metadataSize = hdr10plus.size();
+        FlattenableUtils::write(buffer, size, metadataSize);
+        memcpy(buffer, hdr10plus.data(), metadataSize);
+        FlattenableUtils::advance(buffer, size, metadataSize);
+    }
 
     return NO_ERROR;
 }
@@ -62,6 +73,22 @@
         }
         FlattenableUtils::read(buffer, size, cta8613);
     }
+    if (validTypes & HDR10PLUS) {
+        if (size < sizeof(size_t)) {
+            return NO_MEMORY;
+        }
+
+        size_t metadataSize;
+        FlattenableUtils::read(buffer, size, metadataSize);
+
+        if (size < metadataSize) {
+            return NO_MEMORY;
+        }
+
+        hdr10plus.resize(metadataSize);
+        memcpy(hdr10plus.data(), buffer, metadataSize);
+        FlattenableUtils::advance(buffer, size, metadataSize);
+    }
 
     return NO_ERROR;
 }
@@ -91,6 +118,10 @@
         }
     }
 
+    if ((validTypes & HDR10PLUS) == HDR10PLUS) {
+        if (hdr10plus != rhs.hdr10plus) return false;
+    }
+
     return true;
 }
 
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 69e5379..80d435f 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -599,6 +599,43 @@
         }
         return err;
     }
+
+    virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+                                                           ui::PixelFormat* outFormat,
+                                                           ui::Dataspace* outDataspace,
+                                                           uint8_t* outComponentMask) const {
+        if (!outFormat || !outDataspace || !outComponentMask) return BAD_VALUE;
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeStrongBinder(display);
+
+        status_t error =
+                remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
+                                   data, &reply);
+        if (error != NO_ERROR) {
+            return error;
+        }
+
+        uint32_t value = 0;
+        error = reply.readUint32(&value);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        *outFormat = static_cast<ui::PixelFormat>(value);
+
+        error = reply.readUint32(&value);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        *outDataspace = static_cast<ui::Dataspace>(value);
+
+        error = reply.readUint32(&value);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        *outComponentMask = static_cast<uint8_t>(value);
+        return error;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -951,6 +988,22 @@
             }
             return result;
         }
+        case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+            sp<IBinder> display = data.readStrongBinder();
+            ui::PixelFormat format;
+            ui::Dataspace dataspace;
+            uint8_t component = 0;
+            auto result =
+                    getDisplayedContentSamplingAttributes(display, &format, &dataspace, &component);
+            if (result == NO_ERROR) {
+                reply->writeUint32(static_cast<uint32_t>(format));
+                reply->writeUint32(static_cast<uint32_t>(dataspace));
+                reply->writeUint32(static_cast<uint32_t>(component));
+            }
+            return result;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
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/Surface.cpp b/libs/gui/Surface.cpp
index b505c6f..00e23f0 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -965,6 +965,9 @@
     case NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA:
         res = dispatchSetBuffersCta8613Metadata(args);
         break;
+    case NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA:
+        res = dispatchSetBuffersHdr10PlusMetadata(args);
+        break;
     case NATIVE_WINDOW_SET_SURFACE_DAMAGE:
         res = dispatchSetSurfaceDamage(args);
         break;
@@ -1120,6 +1123,12 @@
     return setBuffersCta8613Metadata(metadata);
 }
 
+int Surface::dispatchSetBuffersHdr10PlusMetadata(va_list args) {
+    const size_t size = va_arg(args, size_t);
+    const uint8_t* metadata = va_arg(args, const uint8_t*);
+    return setBuffersHdr10PlusMetadata(size, metadata);
+}
+
 int Surface::dispatchSetSurfaceDamage(va_list args) {
     android_native_rect_t* rects = va_arg(args, android_native_rect_t*);
     size_t numRects = va_arg(args, size_t);
@@ -1568,6 +1577,19 @@
     return NO_ERROR;
 }
 
+int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) {
+    ALOGV("Surface::setBuffersBlobMetadata");
+    Mutex::Autolock lock(mMutex);
+    if (size > 0) {
+        mHdrMetadata.hdr10plus.assign(metadata, metadata + size);
+        mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS;
+    } else {
+        mHdrMetadata.validTypes &= ~HdrMetadata::HDR10PLUS;
+        mHdrMetadata.hdr10plus.clear();
+    }
+    return NO_ERROR;
+}
+
 Dataspace Surface::getBuffersDataSpace() {
     ALOGV("Surface::getBuffersDataSpace");
     Mutex::Autolock lock(mMutex);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 87c6f27..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,
@@ -1077,6 +1089,15 @@
             outCapabilities);
 }
 
+status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+                                                                      ui::PixelFormat* outFormat,
+                                                                      ui::Dataspace* outDataspace,
+                                                                      uint8_t* outComponentMask) {
+    return ComposerService::getComposerService()
+            ->getDisplayedContentSamplingAttributes(display, outFormat, outDataspace,
+                                                    outComponentMask);
+}
+
 // ----------------------------------------------------------------------------
 
 status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
diff --git a/libs/gui/include/gui/HdrMetadata.h b/libs/gui/include/gui/HdrMetadata.h
index 9800602..1e9c3e7 100644
--- a/libs/gui/include/gui/HdrMetadata.h
+++ b/libs/gui/include/gui/HdrMetadata.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <stdint.h>
+#include <vector>
 
 #include <system/graphics.h>
 #include <utils/Flattenable.h>
@@ -26,12 +27,15 @@
 struct HdrMetadata : public LightFlattenable<HdrMetadata> {
     enum Type : uint32_t {
         SMPTE2086 = 1 << 0,
-        CTA861_3  = 1 << 1,
+        CTA861_3 = 1 << 1,
+        HDR10PLUS = 1 << 2,
     };
+
     uint32_t validTypes{0};
 
     android_smpte2086_metadata smpte2086{};
     android_cta861_3_metadata cta8613{};
+    std::vector<uint8_t> hdr10plus{};
 
     // LightFlattenable
     bool isFixedSize() const { return false; }
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 761f31a..3b6c6e4 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -293,6 +293,13 @@
                                               ui::PixelFormat* defaultPixelFormat,
                                               ui::Dataspace* wideColorGamutDataspace,
                                               ui::PixelFormat* wideColorGamutPixelFormat) const = 0;
+    /*
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+                                                           ui::PixelFormat* outFormat,
+                                                           ui::Dataspace* outDataspace,
+                                                           uint8_t* outComponentMask) const = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -332,6 +339,7 @@
         CREATE_SCOPED_CONNECTION,
         GET_COMPOSITION_PREFERENCE,
         GET_COLOR_MANAGEMENT,
+        GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
     };
 
     virtual status_t onTransact(uint32_t code, const Parcel& data,
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/Surface.h b/libs/gui/include/gui/Surface.h
index 32ee595..248e105 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -218,6 +218,7 @@
     int dispatchSetBuffersDataSpace(va_list args);
     int dispatchSetBuffersSmpte2086Metadata(va_list args);
     int dispatchSetBuffersCta8613Metadata(va_list args);
+    int dispatchSetBuffersHdr10PlusMetadata(va_list args);
     int dispatchSetSurfaceDamage(va_list args);
     int dispatchSetSharedBufferMode(va_list args);
     int dispatchSetAutoRefresh(va_list args);
@@ -249,6 +250,7 @@
     virtual int setBuffersDataSpace(ui::Dataspace dataSpace);
     virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata);
     virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata);
+    virtual int setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata);
     virtual int setCrop(Rect const* rect);
     virtual int setUsage(uint64_t reqUsage);
     virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 10c27b1..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
@@ -381,6 +382,11 @@
 
     inline sp<ISurfaceComposerClient> getClient() { return mClient; }
 
+    static status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+                                                          ui::PixelFormat* outFormat,
+                                                          ui::Dataspace* outDataspace,
+                                                          uint8_t* outComponentMask);
+
 private:
     virtual void onFirstRef();
 
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 7ecadf8..6de641d 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -16,6 +16,8 @@
         "BufferItemConsumer_test.cpp",
         "BufferQueue_test.cpp",
         "CpuConsumer_test.cpp",
+        "EndToEndNativeInputTest.cpp",
+        "DisplayedContentSampling_test.cpp",
         "FillBuffer.cpp",
         "GLTest.cpp",
         "IGraphicBufferProducer_test.cpp",
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
new file mode 100644
index 0000000..f2c0e0c
--- /dev/null
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <binder/ProcessState.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <inttypes.h>
+
+namespace android {
+
+using Transaction = SurfaceComposerClient::Transaction;
+
+static constexpr uint32_t INVALID_MASK = 0x10;
+class DisplayedContentSamplingTest : public ::testing::Test {
+protected:
+    void SetUp() {
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(OK, mComposerClient->initCheck());
+        mDisplayToken = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+        ASSERT_TRUE(mDisplayToken);
+    }
+
+    bool shouldSkipTest(status_t status) {
+        if (status == PERMISSION_DENIED) {
+            SUCCEED() << "permissions denial, skipping test";
+            return true;
+        }
+        if (status == INVALID_OPERATION) {
+            SUCCEED() << "optional function not supported, skipping test";
+            return true;
+        }
+        return false;
+    }
+
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<IBinder> mDisplayToken;
+};
+
+TEST_F(DisplayedContentSamplingTest, GetDisplayedContentSamplingAttributesAreSane) {
+    ui::PixelFormat format;
+    ui::Dataspace dataspace;
+    uint8_t componentMask = 0;
+    status_t status =
+            mComposerClient->getDisplayedContentSamplingAttributes(mDisplayToken, &format,
+                                                                   &dataspace, &componentMask);
+    if (shouldSkipTest(status)) {
+        return;
+    }
+    EXPECT_EQ(OK, status);
+    EXPECT_LE(componentMask, INVALID_MASK);
+}
+} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
new file mode 100644
index 0000000..86e9c23
--- /dev/null
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -0,0 +1,271 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <poll.h>
+
+#include <memory>
+
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+#include <input/InputWindow.h>
+#include <input/IInputFlinger.h>
+#include <input/InputTransport.h>
+#include <input/Input.h>
+
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+
+namespace android {
+namespace test {
+
+sp<IInputFlinger> getInputFlinger() {
+   sp<IBinder> input(defaultServiceManager()->getService(
+            String16("inputflinger")));
+    if (input == nullptr) {
+        ALOGE("Failed to link to input service");
+    } else { ALOGE("Linked to input"); }
+    return interface_cast<IInputFlinger>(input);
+}
+
+// We use the top 10 layers as a way to haphazardly place ourselves above anything else.
+static const int LAYER_BASE = INT32_MAX - 10;
+
+class InputSurface {
+public:
+    InputSurface(const sp<SurfaceComposerClient>& scc, int width, int height) {
+        mSurfaceControl = scc->createSurface(String8("Test Surface"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                             ISurfaceComposerClient::eFXSurfaceColor);
+
+        InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
+        mServerChannel->setToken(new BBinder());
+
+        mInputFlinger = getInputFlinger();
+        mInputFlinger->registerInputChannel(mServerChannel);
+
+        populateInputInfo(width, height);
+
+        mInputConsumer = new InputConsumer(mClientChannel);
+    }
+
+    InputEvent* consumeEvent() {
+        waitForEventAvailable();
+
+        InputEvent *ev;
+        uint32_t seqId;
+        status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
+        if (consumed != OK) {
+            return nullptr;
+        }
+        mInputConsumer->sendFinishedSignal(seqId, true);
+        return ev;
+    }
+
+    void expectTap(int x, int y) {
+        InputEvent* ev = consumeEvent();
+        EXPECT_TRUE(ev != nullptr);
+        EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+        MotionEvent* mev = static_cast<MotionEvent*>(ev);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
+        EXPECT_EQ(x, mev->getX(0));
+        EXPECT_EQ(y, mev->getY(0));
+
+        ev = consumeEvent();
+        EXPECT_TRUE(ev != nullptr);
+        EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+        mev = static_cast<MotionEvent*>(ev);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+    }
+
+    ~InputSurface() {
+        mInputFlinger->unregisterInputChannel(mServerChannel);
+    }
+
+    void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
+                    const sp<SurfaceControl>&)> transactionBody) {
+        SurfaceComposerClient::Transaction t;
+        transactionBody(t, mSurfaceControl);
+        t.apply(true);
+    }
+
+    void showAt(int x, int y) {
+        SurfaceComposerClient::Transaction t;
+        t.show(mSurfaceControl);
+        t.setInputWindowInfo(mSurfaceControl, mInputInfo);
+        t.setLayer(mSurfaceControl, LAYER_BASE);
+        t.setPosition(mSurfaceControl, x, y);
+        t.setCrop_legacy(mSurfaceControl, Rect(0, 0, 100, 100));
+        t.setAlpha(mSurfaceControl, 1);
+        t.apply(true);
+    }
+
+private:
+    void waitForEventAvailable() {
+        struct pollfd fd;
+
+        fd.fd = mClientChannel->getFd();
+        fd.events = POLLIN;
+        poll(&fd, 1, 3000);
+    }
+
+    void populateInputInfo(int width, int height) {
+        mInputInfo.token = mServerChannel->getToken();
+        mInputInfo.name = "Test info";
+        mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
+        mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
+        mInputInfo.dispatchingTimeout = 100000;
+        mInputInfo.globalScaleFactor = 1.0;
+        mInputInfo.canReceiveKeys = true;
+        mInputInfo.hasFocus = true;
+        mInputInfo.hasWallpaper = false;
+        mInputInfo.paused = false;
+
+        mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
+
+        // TODO: Fill in from SF?
+        mInputInfo.ownerPid = 11111;
+        mInputInfo.ownerUid = 11111;
+        mInputInfo.inputFeatures = 0;
+        mInputInfo.displayId = 0;
+
+        InputApplicationInfo aInfo;
+        aInfo.token = new BBinder();
+        aInfo.name = "Test app info";
+        aInfo.dispatchingTimeout = 100000;
+
+        mInputInfo.applicationInfo = aInfo;
+    }
+public:
+    sp<SurfaceControl> mSurfaceControl;
+    sp<InputChannel> mServerChannel, mClientChannel;
+    sp<IInputFlinger> mInputFlinger;
+
+    InputWindowInfo mInputInfo;
+
+    PreallocatedInputEventFactory mInputEventFactory;
+    InputConsumer* mInputConsumer;
+};
+
+class InputSurfacesTest : public ::testing::Test {
+public:
+    InputSurfacesTest() {
+        ProcessState::self()->startThreadPool();
+    }
+
+    void SetUp() {
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+    }
+
+    void TearDown() {
+        mComposerClient->dispose();
+    }
+
+    std::unique_ptr<InputSurface> makeSurface(int width, int height) {
+        return std::make_unique<InputSurface>(mComposerClient, width, height);
+    }
+
+    sp<SurfaceComposerClient> mComposerClient;
+};
+
+void injectTap(int x, int y) {
+    char *buf1, *buf2;
+    asprintf(&buf1, "%d", x);
+    asprintf(&buf2, "%d", y);
+    if (fork() == 0) {
+        execlp("input", "input", "tap", buf1, buf2, NULL);
+    }
+}
+
+TEST_F(InputSurfacesTest, can_receive_input) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(100, 100);
+
+    injectTap(101, 101);
+
+    EXPECT_TRUE(surface->consumeEvent() != nullptr);
+}
+
+TEST_F(InputSurfacesTest, input_respects_positioning) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(100, 100);
+
+    std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+    surface2->showAt(200, 200);
+
+    injectTap(201, 201);
+    surface2->expectTap(1, 1);
+
+    injectTap(101, 101);
+    surface->expectTap(1, 1);
+
+    surface2->doTransaction([](auto &t, auto &sc) {
+         t.setPosition(sc, 100, 100);
+    });
+    surface->doTransaction([](auto &t, auto &sc) {
+         t.setPosition(sc, 200, 200);
+    });
+
+    injectTap(101, 101);
+    surface2->expectTap(1, 1);
+
+    injectTap(201, 201);
+    surface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_layering) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+
+    surface->showAt(10, 10);
+    surface2->showAt(10, 10);
+
+    surface->doTransaction([](auto &t, auto &sc) {
+         t.setLayer(sc, LAYER_BASE + 1);
+    });
+
+    injectTap(11, 11);
+    surface->expectTap(1, 1);
+
+    surface2->doTransaction([](auto &t, auto &sc) {
+         t.setLayer(sc, LAYER_BASE + 1);
+    });
+
+    injectTap(11, 11);
+    surface2->expectTap(1, 1);
+
+    surface2->doTransaction([](auto &t, auto &sc) {
+         t.hide(sc);
+    });
+
+    injectTap(11, 11);
+    surface->expectTap(1, 1);
+}
+
+}
+}
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index d0600da..3950bb6 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -366,10 +366,17 @@
         78.0,
         62.0,
     };
+
+    std::vector<uint8_t> hdr10plus;
+    hdr10plus.push_back(0xff);
+
     int error = native_window_set_buffers_smpte2086_metadata(window.get(), &smpte2086);
     ASSERT_EQ(error, NO_ERROR);
     error = native_window_set_buffers_cta861_3_metadata(window.get(), &cta861_3);
     ASSERT_EQ(error, NO_ERROR);
+    error = native_window_set_buffers_hdr10_plus_metadata(window.get(), hdr10plus.size(),
+                                                          hdr10plus.data());
+    ASSERT_EQ(error, NO_ERROR);
 }
 
 TEST_F(SurfaceTest, DynamicSetBufferCount) {
@@ -634,6 +641,12 @@
             ui::PixelFormat* /*outWideColorGamutPixelFormat*/) const override {
         return NO_ERROR;
     }
+    status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& /*display*/,
+                                                   ui::PixelFormat* /*outFormat*/,
+                                                   ui::Dataspace* /*outDataspace*/,
+                                                   uint8_t* /*outComponentMask*/) const override {
+        return NO_ERROR;
+    }
 
     virtual status_t getColorManagement(bool* /*outGetColorManagement*/) const { return NO_ERROR; }
 
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 47a2c0c..139570a 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -38,7 +38,22 @@
         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) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
+        channel->write(data);
+        remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
+    }
+
+    virtual void unregisterInputChannel(const sp<InputChannel>& channel) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
+        channel->write(data);
+        remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
     }
 };
 
@@ -61,6 +76,20 @@
         setInputWindows(handles);
         break;
     }
+    case REGISTER_INPUT_CHANNEL_TRANSACTION: {
+        CHECK_INTERFACE(IInputFlinger, data, reply);
+        sp<InputChannel> channel = new InputChannel();
+        channel->read(data);
+        registerInputChannel(channel);
+        break;
+    }
+    case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
+        CHECK_INTERFACE(IInputFlinger, data, reply);
+        sp<InputChannel> channel = new InputChannel();
+        channel->read(data);
+        unregisterInputChannel(channel);
+        break;
+    }
     default:
         return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 8a15e2f..a558970 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -131,15 +131,24 @@
     }
 }
 
-void PointerCoords::scale(float scaleFactor) {
+void PointerCoords::scale(float globalScaleFactor, float windowXScale, float windowYScale) {
     // No need to scale pressure or size since they are normalized.
     // No need to scale orientation since it is meaningless to do so.
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor);
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor);
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor);
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor);
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor);
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor);
+
+    // If there is a global scale factor, it is included in the windowX/YScale
+    // so we don't need to apply it twice to the X/Y axes.
+    // However we don't want to apply any windowXYScale not included in the global scale
+    // to the TOUCH_MAJOR/MINOR coordinates.
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, windowXScale);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, windowYScale);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, globalScaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, globalScaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, globalScaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor);
+}
+
+void PointerCoords::scale(float globalScaleFactor) {
+    scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
 }
 
 void PointerCoords::applyOffset(float xOffset, float yOffset) {
@@ -345,15 +354,15 @@
     mYOffset += yOffset;
 }
 
-void MotionEvent::scale(float scaleFactor) {
-    mXOffset *= scaleFactor;
-    mYOffset *= scaleFactor;
-    mXPrecision *= scaleFactor;
-    mYPrecision *= scaleFactor;
+void MotionEvent::scale(float globalScaleFactor) {
+    mXOffset *= globalScaleFactor;
+    mYOffset *= globalScaleFactor;
+    mXPrecision *= globalScaleFactor;
+    mYPrecision *= globalScaleFactor;
 
     size_t numSamples = mSamplePointerCoords.size();
     for (size_t i = 0; i < numSamples; i++) {
-        mSamplePointerCoords.editItemAt(i).scale(scaleFactor);
+        mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor);
     }
 }
 
diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp
index a0d1668..7936f50 100644
--- a/libs/input/InputApplication.cpp
+++ b/libs/input/InputApplication.cpp
@@ -39,4 +39,21 @@
     }
 }
 
+InputApplicationInfo InputApplicationInfo::read(const Parcel& from) {
+    InputApplicationInfo ret;
+    ret.token = from.readStrongBinder();
+    ret.name = from.readString8().c_str();
+    ret.dispatchingTimeout = from.readInt64();
+
+    return ret;
+}
+
+status_t InputApplicationInfo::write(Parcel& output) const {
+    output.writeStrongBinder(token);
+    output.writeString8(String8(name.c_str()));
+    output.writeInt64(dispatchingTimeout);
+    
+    return OK;
+}
+
 } // namespace android
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index f82437e..556a005 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -65,12 +65,12 @@
 }
 
 status_t InputWindowInfo::write(Parcel& output) const {
-    if (inputChannel == nullptr) {
+    if (token == nullptr) {
         output.writeInt32(0);
         return OK;
     }
     output.writeInt32(1);
-    status_t s = inputChannel->write(output);
+    status_t s = output.writeStrongBinder(token);
     if (s != OK) return s;
 
     output.writeString8(String8(name.c_str()));
@@ -81,7 +81,10 @@
     output.writeInt32(frameTop);
     output.writeInt32(frameRight);
     output.writeInt32(frameBottom);
-    output.writeFloat(scaleFactor);
+    output.writeInt32(surfaceInset);
+    output.writeFloat(globalScaleFactor);
+    output.writeFloat(windowXScale);
+    output.writeFloat(windowYScale);
     output.writeBool(visible);
     output.writeBool(canReceiveKeys);
     output.writeBool(hasFocus);
@@ -92,6 +95,7 @@
     output.writeInt32(ownerUid);
     output.writeInt32(inputFeatures);
     output.writeInt32(displayId);
+    applicationInfo.write(output);
     output.write(touchableRegion);
 
     return OK;
@@ -102,15 +106,14 @@
 
     if (from.readInt32() == 0) {
         return ret;
-
     }
-    sp<InputChannel> inputChannel = new InputChannel();
-    status_t s = inputChannel->read(from);
-    if (s != OK) {
+
+    sp<IBinder> token = from.readStrongBinder();
+    if (token == nullptr) {
         return ret;
     }
 
-    ret.inputChannel = inputChannel;
+    ret.token = token;
     ret.name = from.readString8().c_str();
     ret.layoutParamsFlags = from.readInt32();
     ret.layoutParamsType = from.readInt32();
@@ -119,7 +122,10 @@
     ret.frameTop = from.readInt32();
     ret.frameRight = from.readInt32();
     ret.frameBottom = from.readInt32();
-    ret.scaleFactor = from.readFloat();
+    ret.surfaceInset = from.readInt32();
+    ret.globalScaleFactor = from.readFloat();
+    ret.windowXScale = from.readFloat();
+    ret.windowYScale = from.readFloat();
     ret.visible = from.readBool();
     ret.canReceiveKeys = from.readBool();
     ret.hasFocus = from.readBool();
@@ -130,6 +136,7 @@
     ret.ownerUid = from.readInt32();
     ret.inputFeatures = from.readInt32();
     ret.displayId = from.readInt32();
+    ret.applicationInfo = InputApplicationInfo::read(from);
     from.read(ret.touchableRegion);
 
     return ret;
@@ -141,19 +148,18 @@
 
 // --- InputWindowHandle ---
 
-InputWindowHandle::InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
-    inputApplicationHandle(inputApplicationHandle) {
+InputWindowHandle::InputWindowHandle() {
 }
 
 InputWindowHandle::~InputWindowHandle() {
 }
 
 void InputWindowHandle::releaseChannel() {
-    mInfo.inputChannel.clear();
+    mInfo.token.clear();
 }
 
-sp<InputChannel> InputWindowHandle::getInputChannel() const {
-    return mInfo.inputChannel;
+sp<IBinder> InputWindowHandle::getToken() const {
+    return mInfo.token;
 }
 
 } // namespace android
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 39ad26e..5e5893f 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include <binder/Binder.h>
 #include <binder/Parcel.h>
 
 #include <input/InputWindow.h>
@@ -24,24 +25,20 @@
 namespace android {
 namespace test {
 
-TEST(InputWindowInfo, ParcellingWithoutChannel) {
+TEST(InputWindowInfo, ParcellingWithoutToken) {
     InputWindowInfo i;
-    i.inputChannel = nullptr;
+    i.token = nullptr;
 
     Parcel p;
     ASSERT_EQ(OK, i.write(p));
     p.setDataPosition(0);
     InputWindowInfo i2 = InputWindowInfo::read(p);
-    ASSERT_TRUE(i2.inputChannel == nullptr);
+    ASSERT_TRUE(i2.token == nullptr);
 }
 
 TEST(InputWindowInfo, Parcelling) {
-    sp<InputChannel> channel, junkChannel;
-    status_t result = InputChannel::openInputChannelPair("name", channel, junkChannel);
-    ASSERT_EQ(OK, result) << "openInputChannelPair should have returned valid channels";
-
     InputWindowInfo i;
-    i.inputChannel = channel;
+    i.token = new BBinder();
     i.name = "Foobar";
     i.layoutParamsFlags = 7;
     i.layoutParamsType = 39;
@@ -50,7 +47,10 @@
     i.frameTop = 34;
     i.frameRight = 16;
     i.frameBottom = 19;
-    i.scaleFactor = 0.3;
+    i.surfaceInset = 17;
+    i.globalScaleFactor = 0.3;
+    i.windowXScale = 0.4;
+    i.windowYScale = 0.5;
     i.visible = false;
     i.canReceiveKeys = false;
     i.hasFocus = false;
@@ -67,7 +67,7 @@
 
     p.setDataPosition(0);
     InputWindowInfo i2 = InputWindowInfo::read(p);
-    ASSERT_EQ(i.inputChannel->getName(), i2.inputChannel->getName());
+    ASSERT_EQ(i.token, i2.token);
     ASSERT_EQ(i.name, i2.name);
     ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
     ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
@@ -76,7 +76,10 @@
     ASSERT_EQ(i.frameTop, i2.frameTop);
     ASSERT_EQ(i.frameRight, i2.frameRight);
     ASSERT_EQ(i.frameBottom, i2.frameBottom);
-    ASSERT_EQ(i.scaleFactor, i2.scaleFactor);
+    ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
+    ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
+    ASSERT_EQ(i.windowXScale, i2.windowXScale);
+    ASSERT_EQ(i.windowYScale, i2.windowYScale);
     ASSERT_EQ(i.visible, i2.visible);
     ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys);
     ASSERT_EQ(i.hasFocus, i2.hasFocus);
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 7e26b0b..a19fe17 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -41,33 +41,11 @@
 // ----------------------------------------------------------------------------
 
 int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) {
-    if (!outBuffer || !desc)
-        return BAD_VALUE;
-
-    if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
-        ALOGE("Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format);
-        return BAD_VALUE;
-    }
+    if (!outBuffer || !desc) return BAD_VALUE;
+    if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) return BAD_VALUE;
 
     int format = AHardwareBuffer_convertToPixelFormat(desc->format);
-    if (desc->rfu0 != 0 || desc->rfu1 != 0) {
-        ALOGE("AHardwareBuffer_Desc::rfu fields must be 0");
-        return BAD_VALUE;
-    }
-
-    if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) {
-        ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format");
-        return BAD_VALUE;
-    }
-
-    if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) &&
-        (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) {
-        ALOGE("AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER "
-              "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER");
-        return BAD_VALUE;
-    }
-
-    uint64_t usage =  AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
+    uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
     sp<GraphicBuffer> gbuffer(new GraphicBuffer(
             desc->width, desc->height, format, desc->layers, usage,
             std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]"));
@@ -122,19 +100,26 @@
     if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
                   AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
         ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
-                " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+                "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
         return BAD_VALUE;
     }
 
     usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
-    GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+    if (gbuffer->getLayerCount() > 1) {
+        ALOGE("Buffer with multiple layers passed to AHardwareBuffer_lock; "
+                "only buffers with one layer are allowed");
+        return INVALID_OPERATION;
+    }
+
     Rect bounds;
     if (!rect) {
-        bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight()));
+        bounds.set(Rect(gbuffer->getWidth(), gbuffer->getHeight()));
     } else {
         bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
     }
-    return gBuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
+    return gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
 }
 
 int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
@@ -274,6 +259,25 @@
     return NO_ERROR;
 }
 
+int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) {
+    if (!desc) return 0;
+    if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0;
+
+    // Make a trial allocation.
+    // TODO(b/115660272): add implementation that uses a HAL query.
+    AHardwareBuffer_Desc trialDesc = *desc;
+    trialDesc.width = 4;
+    trialDesc.height = desc->format == AHARDWAREBUFFER_FORMAT_BLOB ? 1 : 4;
+    trialDesc.layers = desc->layers == 1 ? 1 : 2;
+    AHardwareBuffer* trialBuffer = nullptr;
+    int result = AHardwareBuffer_allocate(&trialDesc, &trialBuffer);
+    if (result == NO_ERROR) {
+        AHardwareBuffer_release(trialBuffer);
+        return 1;
+    }
+    return 0;
+}
+
 
 // ----------------------------------------------------------------------------
 // VNDK functions
@@ -322,14 +326,71 @@
 
 namespace android {
 
-// A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks.
-struct UsageMaskMapping {
-    uint64_t hardwareBufferMask;
-    uint64_t grallocMask;
-};
+bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log) {
+    if (desc->width == 0 || desc->height == 0 || desc->layers == 0) {
+        ALOGE_IF(log, "Width, height and layers must all be nonzero");
+        return false;
+    }
 
-static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) {
-    return (mask & bitsToCheck) == bitsToCheck && bitsToCheck;
+    if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
+        ALOGE_IF(log, "Invalid AHardwareBuffer pixel format %u (%#x))",
+                desc->format, desc->format);
+        return false;
+    }
+
+    if (desc->rfu0 != 0 || desc->rfu1 != 0) {
+        ALOGE_IF(log, "AHardwareBuffer_Desc::rfu fields must be 0");
+        return false;
+    }
+
+    if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB) {
+        if (desc->height != 1 || desc->layers != 1) {
+            ALOGE_IF(log, "Height and layers must be 1 for AHARDWAREBUFFER_FORMAT_BLOB");
+            return false;
+        }
+        const uint64_t blobInvalidGpuMask =
+            AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+            AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER |
+            AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE |
+            AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
+        if (desc->usage & blobInvalidGpuMask) {
+            ALOGE_IF(log, "Invalid GPU usage flag for AHARDWAREBUFFER_FORMAT_BLOB; "
+                    "only AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER is allowed");
+            return false;
+        }
+        if (desc->usage & AHARDWAREBUFFER_USAGE_VIDEO_ENCODE) {
+            ALOGE_IF(log, "AHARDWAREBUFFER_FORMAT_BLOB cannot be encoded as video");
+            return false;
+        }
+    } else {
+        if (desc->usage & AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA) {
+            ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA requires AHARDWAREBUFFER_FORMAT_BLOB");
+            return false;
+        }
+        if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
+            ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER requires AHARDWAREBUFFER_FORMAT_BLOB");
+            return false;
+        }
+    }
+
+    if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) &&
+        (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) {
+        ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER "
+              "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER");
+        return false;
+    }
+
+    if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) {
+        if (desc->width != desc->height) {
+            ALOGE_IF(log, "Cube maps must be square");
+            return false;
+        }
+        if (desc->layers % 6 != 0) {
+            ALOGE_IF(log, "Cube map layers must be a multiple of 6");
+            return false;
+        }
+    }
+    return true;
 }
 
 bool AHardwareBuffer_isValidPixelFormat(uint32_t format) {
@@ -445,7 +506,7 @@
             "gralloc and AHardwareBuffer flags don't match");
     static_assert(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE == (uint64_t)BufferUsage::GPU_TEXTURE,
             "gralloc and AHardwareBuffer flags don't match");
-    static_assert(AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT == (uint64_t)BufferUsage::GPU_RENDER_TARGET,
+    static_assert(AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER == (uint64_t)BufferUsage::GPU_RENDER_TARGET,
             "gralloc and AHardwareBuffer flags don't match");
     static_assert(AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT == (uint64_t)BufferUsage::PROTECTED,
             "gralloc and AHardwareBuffer flags don't match");
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/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index 71f5634..bf688f8 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -28,10 +28,15 @@
 #include <stdint.h>
 
 struct AHardwareBuffer;
+struct AHardwareBuffer_Desc;
 struct ANativeWindowBuffer;
 
 namespace android {
 
+// Validates whether the passed description does not have conflicting
+// parameters. Note: this does not verify any platform-specific contraints.
+bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log);
+
 // whether this AHardwareBuffer format is valid
 bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format);
 
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 23ac60b..03545a6 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -102,8 +102,10 @@
     AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM        = 0x2b,
 
     /**
-     * An opaque binary blob format that must have height 1, with width equal to
-     * the buffer size in bytes.
+     * Opaque binary blob format.
+     * Must have height 1 and one layer, with width equal to the buffer
+     * size in bytes. Corresponds to Vulkan buffers and OpenGL buffer
+     * objects. Can be bound to the latter using GL_EXT_external_buffer.
      */
     AHARDWAREBUFFER_FORMAT_BLOB                     = 0x21,
 
@@ -195,20 +197,25 @@
     /// The buffer will be written to by the GPU as a framebuffer attachment.
     AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER        = 1UL << 9,
     /**
-     * The buffer will be written to by the GPU as a framebuffer attachment.
-     * Note that the name of this flag is somewhat misleading: it does not imply
-     * that the buffer contains a color format. A buffer with depth or stencil
-     * format that will be used as a framebuffer attachment should also have
-     * this flag.
+     * The buffer will be written to by the GPU as a framebuffer
+     * attachment.
+     *
+     * Note that the name of this flag is somewhat misleading: it does
+     * not imply that the buffer contains a color format. A buffer with
+     * depth or stencil format that will be used as a framebuffer
+     * attachment should also have this flag. Use the equivalent flag
+     * AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to avoid this confusion.
      */
     AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT       = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
     /**
-     * The buffer is protected from direct CPU access or being read by non-secure
-     * hardware, such as video encoders. This flag is incompatible with CPU
-     * read and write flags. It is mainly used when handling DRM video.
-     * Refer to the EGL extension EGL_EXT_protected_content and GL extension
-     * EXT_protected_textures for more information on how these buffers are expected
-     * to behave.
+     * The buffer is protected from direct CPU access or being read by
+     * non-secure hardware, such as video encoders.
+     *
+     * This flag is incompatible with CPU read and write flags. It is
+     * mainly used when handling DRM video. Refer to the EGL extension
+     * EGL_EXT_protected_content and GL extension
+     * GL_EXT_protected_textures for more information on how these
+     * buffers are expected to behave.
      */
     AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT      = 1UL << 14,
     /// The buffer will be read by a hardware video encoder.
@@ -225,11 +232,17 @@
     AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER        = 1UL << 24,
     /**
      * The buffer will be used as a cube map texture.
-     * When this flag is present, the buffer must have a layer count that is
-     * a multiple of 6.
+     * When this flag is present, the buffer must have a layer count
+     * that is a multiple of 6. Note that buffers with this flag must be
+     * bound to OpenGL textures using the extension
+     * GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image.
      */
     AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP               = 1UL << 25,
-    /// The buffer contains a complete mipmap hierarchy.
+    /**
+     * The buffer contains a complete mipmap hierarchy.
+     * Note that buffers with this flag must be bound to OpenGL textures using
+     * the extension GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image.
+     */
     AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE        = 1UL << 26,
 
     AHARDWAREBUFFER_USAGE_VENDOR_0  = 1ULL << 28,
@@ -255,13 +268,21 @@
 };
 
 /**
- * Buffer description. Used for allocating new buffers and querying parameters
- * of existing ones.
+ * Buffer description. Used for allocating new buffers and querying
+ * parameters of existing ones.
  */
 typedef struct AHardwareBuffer_Desc {
     uint32_t    width;      ///< Width in pixels.
     uint32_t    height;     ///< Height in pixels.
-    uint32_t    layers;     ///< Number of images in an image array.
+    /**
+     * Number of images in an image array. AHardwareBuffers with one
+     * layer correspond to regular 2D textures. AHardwareBuffers with
+     * more than layer correspond to texture arrays. If the layer count
+     * is a multiple of 6 and the usage flag
+     * AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP is present, the buffer is
+     * a cube map or a cube map array.
+     */
+    uint32_t    layers;
     uint32_t    format;     ///< One of AHardwareBuffer_Format.
     uint64_t    usage;      ///< Combination of AHardwareBuffer_UsageFlags.
     uint32_t    stride;     ///< Row stride in pixels, ignored for AHardwareBuffer_allocate()
@@ -290,8 +311,10 @@
 int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
         AHardwareBuffer** outBuffer) __INTRODUCED_IN(26);
 /**
- * Acquire a reference on the given AHardwareBuffer object.  This prevents the
- * object from being deleted until the last reference is removed.
+ * Acquire a reference on the given AHardwareBuffer object.
+ * 
+ * This prevents the object from being deleted until the last reference
+ * is removed.
  */
 void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26);
 
@@ -309,50 +332,73 @@
         AHardwareBuffer_Desc* outDesc) __INTRODUCED_IN(26);
 
 /**
- * Lock the AHardwareBuffer for reading or writing, depending on the usage flags
- * passed.  This call may block if the hardware needs to finish rendering or if
- * CPU caches need to be synchronized, or possibly for other implementation-
- * specific reasons.  If fence is not negative, then it specifies a fence file
- * descriptor that will be signaled when the buffer is locked, otherwise the
- * caller will block until the buffer is available.
+ * Lock the AHardwareBuffer for direct CPU access.
  *
- * If \a rect is not NULL, the caller promises to modify only data in the area
- * specified by rect. If rect is NULL, the caller may modify the contents of the
- * entire buffer.
+ * This function can lock the buffer for either reading or writing.
+ * It may block if the hardware needs to finish rendering, if CPU caches
+ * need to be synchronized, or possibly for other implementation-
+ * specific reasons.
  *
- * The content of the buffer outside of the specified rect is NOT modified
- * by this call.
+ * The passed AHardwareBuffer must have one layer, otherwise the call
+ * will fail.
  *
- * The \a usage parameter may only specify AHARDWAREBUFFER_USAGE_CPU_*. If set,
- * then outVirtualAddress is filled with the address of the buffer in virtual
+ * If \a fence is not negative, it specifies a fence file descriptor on
+ * which to wait before locking the buffer. If it's negative, the caller
+ * is responsible for ensuring that writes to the buffer have completed
+ * before calling this function.  Using this parameter is more efficient
+ * than waiting on the fence and then calling this function.
+ *
+ * The \a usage parameter may only specify AHARDWAREBUFFER_USAGE_CPU_*.
+ * If set, then outVirtualAddress is filled with the address of the
+ * buffer in virtual memory. The flags must also be compatible with
+ * usage flags specified at buffer creation: if a read flag is passed,
+ * the buffer must have been created with
+ * AHARDWAREBUFFER_USAGE_CPU_READ_RARELY or
+ * AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN. If a write flag is passed, it
+ * must have been created with AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY or
+ * AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN.
+ *
+ * If \a rect is not NULL, the caller promises to modify only data in
+ * the area specified by rect. If rect is NULL, the caller may modify
+ * the contents of the entire buffer. The content of the buffer outside
+ * of the specified rect is NOT modified by this call.
+ *
+ * It is legal for several different threads to lock a buffer for read
+ * access; none of the threads are blocked.
+ *
+ * Locking a buffer simultaneously for write or read/write is undefined,
+ * but will neither terminate the process nor block the caller.
+ * AHardwareBuffer_lock may return an error or leave the buffer's
+ * content in an indeterminate state.
+ *
+ * If the buffer has AHARDWAREBUFFER_FORMAT_BLOB, it is legal lock it
+ * for reading and writing in multiple threads and/or processes
+ * simultaneously, and the contents of the buffer behave like shared
  * memory.
  *
- * THREADING CONSIDERATIONS:
- *
- * It is legal for several different threads to lock a buffer for read access;
- * none of the threads are blocked.
- *
- * Locking a buffer simultaneously for write or read/write is undefined, but
- * will neither terminate the process nor block the caller; AHardwareBuffer_lock
- * may return an error or leave the buffer's content into an indeterminate
- * state.
- *
- * \return 0 on success, -EINVAL if \a buffer is NULL or if the usage
- * flags are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or an error
- * number of the lock fails for any reason.
+ * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags
+ * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer
+ * has more than one layer. Error number if the lock fails for any other
+ * reason.
  */
 int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
         int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26);
 
 /**
- * Unlock the AHardwareBuffer; must be called after all changes to the buffer
- * are completed by the caller. If fence is not NULL then it will be set to a
- * file descriptor that is signaled when all pending work on the buffer is
- * completed. The caller is responsible for closing the fence when it is no
- * longer needed.
+ * Unlock the AHardwareBuffer from direct CPU access.
  *
- * \return 0 on success, -EINVAL if \a buffer is NULL, or an error
- * number if the unlock fails for any reason.
+ * Must be called after all changes to the buffer are completed by the
+ * caller.  If \a fence is NULL, the function will block until all work
+ * is completed.  Otherwise, \a fence will be set either to a valid file
+ * descriptor or to -1.  The file descriptor will become signaled once
+ * the unlocking is complete and buffer contents are updated.
+ * The caller is responsible for closing the file descriptor once it's
+ * no longer needed.  The value -1 indicates that unlocking has already
+ * completed before the function returned and no further operations are
+ * necessary.
+ *
+ * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if
+ * the unlock fails for any reason.
  */
 int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) __INTRODUCED_IN(26);
 
@@ -365,7 +411,7 @@
 int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) __INTRODUCED_IN(26);
 
 /**
- * Receive the AHardwareBuffer from an AF_UNIX socket.
+ * Receive an AHardwareBuffer from an AF_UNIX socket.
  *
  * \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error
  * number if the operation fails for any reason.
@@ -374,6 +420,29 @@
 
 #endif // __ANDROID_API__ >= 26
 
+#if __ANDROID_API__ >= 29
+
+/**
+ * Test whether the given format and usage flag combination is
+ * allocatable.
+ *
+ * If this function returns true, it means that a buffer with the given
+ * description can be allocated on this implementation, unless resource
+ * exhaustion occurs. If this function returns false, it means that the
+ * allocation of the given description will never succeed.
+ *
+ * The return value of this function may depend on all fields in the
+ * description, except stride, which is always ignored. For example,
+ * some implementations have implementation-defined limits on texture
+ * size and layer count.
+ *
+ * \return 1 if the format and usage flag combination is allocatable,
+ *     0 otherwise.
+ */
+int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
 __END_DECLS
 
 #endif // ANDROID_HARDWARE_BUFFER_H
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 197f73f..61590e0 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -202,7 +202,7 @@
  * ANativeWindow.
  */
 enum {
-// clang-format off
+    // clang-format off
     NATIVE_WINDOW_SET_USAGE                     =  0,   /* deprecated */
     NATIVE_WINDOW_CONNECT                       =  1,   /* deprecated */
     NATIVE_WINDOW_DISCONNECT                    =  2,   /* deprecated */
@@ -237,7 +237,8 @@
     NATIVE_WINDOW_GET_CONSUMER_USAGE64          = 31,
     NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
     NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33,
-// clang-format on
+    NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34,
+    // clang-format on
 };
 
 /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -748,6 +749,27 @@
 }
 
 /*
+ * native_window_set_buffers_hdr10_plus_metadata(..., metadata)
+ * All buffers queued after this call will be associated with the
+ * HDR10+ dynamic metadata specified.
+ *
+ * metadata specifies additional dynamic information about the
+ * contents of the buffer that may affect how it is displayed.  When
+ * it is nullptr, it means no such information is available.  No
+ * HDR10+ dynamic emtadata is associated with the buffers by default.
+ *
+ * Parameter "size" refers to the length of the metadata blob pointed to
+ * by parameter "data".  The metadata blob will adhere to the HDR10+ SEI
+ * message standard.
+ */
+static inline int native_window_set_buffers_hdr10_plus_metadata(struct ANativeWindow* window,
+                                                           const size_t size,
+                                                           const uint8_t* metadata) {
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA, size,
+                           metadata);
+}
+
+/*
  * native_window_set_buffers_transform(..., int transform)
  * All buffers queued after this call will be displayed transformed according
  * to the transform parameter specified.
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 753954d..a796e97 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -5,6 +5,7 @@
     AHardwareBuffer_createFromHandle; # vndk
     AHardwareBuffer_describe;
     AHardwareBuffer_getNativeHandle; # vndk
+    AHardwareBuffer_isSupported; # introduced=29
     AHardwareBuffer_lock;
     AHardwareBuffer_recvHandleFromUnixSocket;
     AHardwareBuffer_release;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 7efc8bd..beaf9ee 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -50,7 +50,6 @@
         "gl/GLExtensions.cpp",
         "gl/GLFramebuffer.cpp",
         "gl/GLImage.cpp",
-        "gl/GLSurface.cpp",
         "gl/Program.cpp",
         "gl/ProgramCache.cpp",
     ],
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 e244a83..7adda83 100644
--- a/libs/renderengine/gl/GLES20RenderEngine.cpp
+++ b/libs/renderengine/gl/GLES20RenderEngine.cpp
@@ -41,7 +41,6 @@
 #include "GLExtensions.h"
 #include "GLFramebuffer.h"
 #include "GLImage.h"
-#include "GLSurface.h"
 #include "Program.h"
 #include "ProgramCache.h"
 
@@ -241,60 +240,29 @@
         config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
     }
 
-    EGLint renderableType = 0;
-    if (config == EGL_NO_CONFIG) {
-        renderableType = EGL_OPENGL_ES2_BIT;
-    } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
-        LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
-    }
-    EGLint contextClientVersion = 0;
-    if (renderableType & EGL_OPENGL_ES2_BIT) {
-        contextClientVersion = 2;
-    } else if (renderableType & EGL_OPENGL_ES_BIT) {
-        contextClientVersion = 1;
-    } else {
-        LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
-    }
-
-    std::vector<EGLint> contextAttributes;
-    contextAttributes.reserve(6);
-    contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
-    contextAttributes.push_back(contextClientVersion);
     bool useContextPriority = extensions.hasContextPriority() &&
             (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT);
-    if (useContextPriority) {
-        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
-        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
-    }
-    contextAttributes.push_back(EGL_NONE);
-
-    EGLContext ctxt = eglCreateContext(display, config, nullptr, contextAttributes.data());
+    EGLContext ctxt = createEglContext(display, config, EGL_NO_CONTEXT, useContextPriority);
 
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
 
-    // now figure out what version of GL did we actually get
-    // NOTE: a dummy surface is not needed if KHR_create_context is supported
-    // TODO(alecmouri): don't create this surface if EGL_KHR_surfaceless_context
-    // is supported.
-
-    EGLConfig dummyConfig = config;
-    if (dummyConfig == EGL_NO_CONFIG) {
-        dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+    EGLSurface dummy = EGL_NO_SURFACE;
+    if (!extensions.hasSurfacelessContext()) {
+        dummy = createDummyEglPbufferSurface(display, config, hwcFormat);
+        LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
     }
-    EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
-    EGLSurface dummy = eglCreatePbufferSurface(display, dummyConfig, attribs);
-    LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
+
     EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
     LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
 
     extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
                                  glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
 
+    // now figure out what version of GL did we actually get
     GlesVersion version = parseGlesVersion(extensions.getVersion());
 
     // initialize the renderer while GL is current
-
     std::unique_ptr<GLES20RenderEngine> engine;
     switch (version) {
         case GLES_VERSION_1_0:
@@ -423,10 +391,6 @@
     return std::make_unique<GLFramebuffer>(*this);
 }
 
-std::unique_ptr<Surface> GLES20RenderEngine::createSurface() {
-    return std::make_unique<GLSurface>(*this);
-}
-
 std::unique_ptr<Image> GLES20RenderEngine::createImage() {
     return std::make_unique<GLImage>(*this);
 }
@@ -439,31 +403,6 @@
     return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
 }
 
-bool GLES20RenderEngine::setCurrentSurface(const Surface& surface) {
-    // Surface is an abstract interface. GLES20RenderEngine only ever
-    // creates GLSurface's, so it is safe to just cast to the actual
-    // type.
-    bool success = true;
-    const GLSurface& glSurface = static_cast<const GLSurface&>(surface);
-    EGLSurface eglSurface = glSurface.getEGLSurface();
-    if (eglSurface != eglGetCurrentSurface(EGL_DRAW)) {
-        success = eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext) == EGL_TRUE;
-        if (success && glSurface.getAsync()) {
-            eglSwapInterval(mEGLDisplay, 0);
-        }
-        if (success) {
-            mSurfaceHeight = glSurface.getHeight();
-        }
-    }
-
-    return success;
-}
-
-void GLES20RenderEngine::resetCurrentSurface() {
-    eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    mSurfaceHeight = 0;
-}
-
 base::unique_fd GLES20RenderEngine::flush() {
     if (!GLExtensions::getInstance().hasNativeFenceSync()) {
         return base::unique_fd();
@@ -577,7 +516,7 @@
 
 void GLES20RenderEngine::setScissor(const Rect& region) {
     // Invert y-coordinate to map to GL-space.
-    int32_t canvasHeight = mRenderToFbo ? mFboHeight : mSurfaceHeight;
+    int32_t canvasHeight = mFboHeight;
     int32_t glBottom = canvasHeight - region.bottom;
 
     glScissor(region.left, glBottom, region.getWidth(), region.getHeight());
@@ -620,7 +559,6 @@
     glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
 
-    mRenderToFbo = true;
     mFboHeight = glFramebuffer->getBufferHeight();
 
     uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
@@ -632,7 +570,6 @@
 }
 
 void GLES20RenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
-    mRenderToFbo = false;
     mFboHeight = 0;
 
     // back to main framebuffer
@@ -661,9 +598,7 @@
     int32_t r = sourceCrop.right;
     int32_t b = sourceCrop.bottom;
     int32_t t = sourceCrop.top;
-    if (mRenderToFbo) {
-        std::swap(t, b);
-    }
+    std::swap(t, b);
     mat4 m = mat4::ortho(l, r, b, t, 0, 1);
 
     // Apply custom rotation to the projection.
@@ -691,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 {
@@ -768,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()) {
@@ -779,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
@@ -896,6 +843,10 @@
     if (mesh.getTexCoordsSize()) {
         glDisableVertexAttribArray(Program::texCoords);
     }
+
+    if (mState.cornerRadius > 0.0f) {
+        glDisableVertexAttribArray(Program::cropCoords);
+    }
 }
 
 size_t GLES20RenderEngine::getMaxTextureSize() const {
@@ -942,6 +893,53 @@
     return GLES_VERSION_1_0;
 }
 
+EGLContext GLES20RenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
+                                                EGLContext shareContext, bool useContextPriority) {
+    EGLint renderableType = 0;
+    if (config == EGL_NO_CONFIG) {
+        renderableType = EGL_OPENGL_ES2_BIT;
+    } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+        LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+    }
+    EGLint contextClientVersion = 0;
+    if (renderableType & EGL_OPENGL_ES2_BIT) {
+        contextClientVersion = 2;
+    } else if (renderableType & EGL_OPENGL_ES_BIT) {
+        contextClientVersion = 1;
+    } else {
+        LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+    }
+
+    std::vector<EGLint> contextAttributes;
+    contextAttributes.reserve(5);
+    contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+    contextAttributes.push_back(contextClientVersion);
+    if (useContextPriority) {
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+    }
+    contextAttributes.push_back(EGL_NONE);
+
+    return eglCreateContext(display, config, shareContext, contextAttributes.data());
+}
+
+EGLSurface GLES20RenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
+                                                            int hwcFormat) {
+    EGLConfig dummyConfig = config;
+    if (dummyConfig == EGL_NO_CONFIG) {
+        dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+    }
+    std::vector<EGLint> attributes;
+    attributes.reserve(5);
+    attributes.push_back(EGL_WIDTH);
+    attributes.push_back(1);
+    attributes.push_back(EGL_HEIGHT);
+    attributes.push_back(1);
+    attributes.push_back(EGL_NONE);
+
+    return eglCreatePbufferSurface(display, dummyConfig, attributes.data());
+}
+
 bool GLES20RenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
     const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
     const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
diff --git a/libs/renderengine/gl/GLES20RenderEngine.h b/libs/renderengine/gl/GLES20RenderEngine.h
index aebb319..a9f8cad 100644
--- a/libs/renderengine/gl/GLES20RenderEngine.h
+++ b/libs/renderengine/gl/GLES20RenderEngine.h
@@ -40,7 +40,6 @@
 namespace gl {
 
 class GLImage;
-class GLSurface;
 
 class GLES20RenderEngine : public impl::RenderEngine {
 public:
@@ -52,13 +51,10 @@
     ~GLES20RenderEngine() override;
 
     std::unique_ptr<Framebuffer> createFramebuffer() override;
-    std::unique_ptr<Surface> createSurface() override;
     std::unique_ptr<Image> createImage() override;
 
     void primeCache() const override;
     bool isCurrent() const override;
-    bool setCurrentSurface(const Surface& surface) override;
-    void resetCurrentSurface() override;
     base::unique_fd flush() override;
     bool finish() override;
     bool waitFence(base::unique_fd fenceFd) override;
@@ -87,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;
@@ -116,6 +113,10 @@
     };
 
     static GlesVersion parseGlesVersion(const char* str);
+    static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
+                                       EGLContext shareContext, bool useContextPriority);
+    static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
+                                                   int hwcFormat);
 
     // A data space is considered HDR data space if it has BT2020 color space
     // with PQ or HLG transfer function.
@@ -147,8 +148,6 @@
     mat4 mBt2020ToSrgb;
     mat4 mBt2020ToDisplayP3;
 
-    bool mRenderToFbo = false;
-    int32_t mSurfaceHeight = 0;
     int32_t mFboHeight = 0;
 
     // Current dataspace of layer being rendered
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp
index 784693b..ce83dd5 100644
--- a/libs/renderengine/gl/GLExtensions.cpp
+++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -112,6 +112,9 @@
     if (extensionSet.hasExtension("EGL_IMG_context_priority")) {
         mHasContextPriority = true;
     }
+    if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) {
+        mHasSurfacelessContext = true;
+    }
 }
 
 char const* GLExtensions::getEGLVersion() const {
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h
index 382c23a..2a654d5 100644
--- a/libs/renderengine/gl/GLExtensions.h
+++ b/libs/renderengine/gl/GLExtensions.h
@@ -32,29 +32,6 @@
 namespace gl {
 
 class GLExtensions : public Singleton<GLExtensions> {
-    friend class Singleton<GLExtensions>;
-
-    bool mHasNoConfigContext = false;
-    bool mHasNativeFenceSync = false;
-    bool mHasFenceSync = false;
-    bool mHasWaitSync = false;
-    bool mHasProtectedContent = false;
-    bool mHasContextPriority = false;
-
-    String8 mVendor;
-    String8 mRenderer;
-    String8 mVersion;
-    String8 mExtensions;
-
-    String8 mEGLVersion;
-    String8 mEGLExtensions;
-
-    GLExtensions(const GLExtensions&);
-    GLExtensions& operator=(const GLExtensions&);
-
-protected:
-    GLExtensions() = default;
-
 public:
     bool hasNoConfigContext() const { return mHasNoConfigContext; }
     bool hasNativeFenceSync() const { return mHasNativeFenceSync; }
@@ -62,6 +39,7 @@
     bool hasWaitSync() const { return mHasWaitSync; }
     bool hasProtectedContent() const { return mHasProtectedContent; }
     bool hasContextPriority() const { return mHasContextPriority; }
+    bool hasSurfacelessContext() const { return mHasSurfacelessContext; }
 
     void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
                            GLubyte const* extensions);
@@ -73,6 +51,31 @@
     void initWithEGLStrings(char const* eglVersion, char const* eglExtensions);
     char const* getEGLVersion() const;
     char const* getEGLExtensions() const;
+
+protected:
+    GLExtensions() = default;
+
+private:
+    friend class Singleton<GLExtensions>;
+
+    bool mHasNoConfigContext = false;
+    bool mHasNativeFenceSync = false;
+    bool mHasFenceSync = false;
+    bool mHasWaitSync = false;
+    bool mHasProtectedContent = false;
+    bool mHasContextPriority = false;
+    bool mHasSurfacelessContext = false;
+
+    String8 mVendor;
+    String8 mRenderer;
+    String8 mVersion;
+    String8 mExtensions;
+
+    String8 mEGLVersion;
+    String8 mEGLExtensions;
+
+    GLExtensions(const GLExtensions&);
+    GLExtensions& operator=(const GLExtensions&);
 };
 
 } // namespace gl
diff --git a/libs/renderengine/gl/GLSurface.cpp b/libs/renderengine/gl/GLSurface.cpp
deleted file mode 100644
index 2d694e9..0000000
--- a/libs/renderengine/gl/GLSurface.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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 "GLSurface.h"
-
-#include <android/native_window.h>
-#include <log/log.h>
-#include <ui/PixelFormat.h>
-#include "GLES20RenderEngine.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLSurface::GLSurface(const GLES20RenderEngine& engine)
-      : mEGLDisplay(engine.getEGLDisplay()), mEGLConfig(engine.getEGLConfig()) {
-    // RE does not assume any config when EGL_KHR_no_config_context is supported
-    if (mEGLConfig == EGL_NO_CONFIG_KHR) {
-        mEGLConfig =
-                GLES20RenderEngine::chooseEglConfig(mEGLDisplay, PIXEL_FORMAT_RGBA_8888, false);
-    }
-}
-
-GLSurface::~GLSurface() {
-    setNativeWindow(nullptr);
-}
-
-void GLSurface::setNativeWindow(ANativeWindow* window) {
-    if (mEGLSurface != EGL_NO_SURFACE) {
-        eglDestroySurface(mEGLDisplay, mEGLSurface);
-        mEGLSurface = EGL_NO_SURFACE;
-        mSurfaceWidth = 0;
-        mSurfaceHeight = 0;
-    }
-
-    mWindow = window;
-    if (mWindow) {
-        mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mWindow, nullptr);
-        mSurfaceWidth = ANativeWindow_getWidth(window);
-        mSurfaceHeight = ANativeWindow_getHeight(window);
-    }
-}
-
-void GLSurface::swapBuffers() const {
-    if (!eglSwapBuffers(mEGLDisplay, mEGLSurface)) {
-        EGLint error = eglGetError();
-
-        const char format[] = "eglSwapBuffers(%p, %p) failed with 0x%08x";
-        if (mCritical || error == EGL_CONTEXT_LOST) {
-            LOG_ALWAYS_FATAL(format, mEGLDisplay, mEGLSurface, error);
-        } else {
-            ALOGE(format, mEGLDisplay, mEGLSurface, error);
-        }
-    }
-}
-
-EGLint GLSurface::queryConfig(EGLint attrib) const {
-    EGLint value;
-    if (!eglGetConfigAttrib(mEGLDisplay, mEGLConfig, attrib, &value)) {
-        value = 0;
-    }
-
-    return value;
-}
-
-int32_t GLSurface::queryRedSize() const {
-    return queryConfig(EGL_RED_SIZE);
-}
-
-int32_t GLSurface::queryGreenSize() const {
-    return queryConfig(EGL_GREEN_SIZE);
-}
-
-int32_t GLSurface::queryBlueSize() const {
-    return queryConfig(EGL_BLUE_SIZE);
-}
-
-int32_t GLSurface::queryAlphaSize() const {
-    return queryConfig(EGL_ALPHA_SIZE);
-}
-
-int32_t GLSurface::getWidth() const {
-    return mSurfaceWidth;
-}
-
-int32_t GLSurface::getHeight() const {
-    return mSurfaceHeight;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLSurface.h b/libs/renderengine/gl/GLSurface.h
deleted file mode 100644
index 092d371..0000000
--- a/libs/renderengine/gl/GLSurface.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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 <cstdint>
-
-#include <EGL/egl.h>
-#include <android-base/macros.h>
-#include <renderengine/Surface.h>
-
-struct ANativeWindow;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLES20RenderEngine;
-
-class GLSurface final : public renderengine::Surface {
-public:
-    GLSurface(const GLES20RenderEngine& engine);
-    ~GLSurface() override;
-
-    // renderengine::Surface implementation
-    void setCritical(bool enable) override { mCritical = enable; }
-    void setAsync(bool enable) override { mAsync = enable; }
-
-    void setNativeWindow(ANativeWindow* window) override;
-    void swapBuffers() const override;
-
-    int32_t queryRedSize() const override;
-    int32_t queryGreenSize() const override;
-    int32_t queryBlueSize() const override;
-    int32_t queryAlphaSize() const override;
-
-    bool getAsync() const { return mAsync; }
-    EGLSurface getEGLSurface() const { return mEGLSurface; }
-
-    int32_t getWidth() const override;
-    int32_t getHeight() const override;
-
-private:
-    EGLint queryConfig(EGLint attrib) const;
-
-    EGLDisplay mEGLDisplay;
-    EGLConfig mEGLConfig;
-
-    bool mCritical = false;
-    bool mAsync = false;
-
-    int32_t mSurfaceWidth = 0;
-    int32_t mSurfaceHeight = 0;
-
-    ANativeWindow* mWindow = nullptr;
-    EGLSurface mEGLSurface = EGL_NO_SURFACE;
-
-    DISALLOW_COPY_AND_ASSIGN(GLSurface);
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
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 becb3c3..bb7f4df 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -48,7 +48,6 @@
 class BindNativeBufferAsFramebuffer;
 class Image;
 class Mesh;
-class Surface;
 class Texture;
 
 namespace impl {
@@ -72,7 +71,6 @@
     // used to support legacy behavior.
 
     virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0;
-    virtual std::unique_ptr<Surface> createSurface() = 0;
     virtual std::unique_ptr<Image> createImage() = 0;
 
     virtual void primeCache() const = 0;
@@ -84,8 +82,6 @@
     virtual bool useWaitSync() const = 0;
 
     virtual bool isCurrent() const = 0;
-    virtual bool setCurrentSurface(const Surface& surface) = 0;
-    virtual void resetCurrentSurface() = 0;
 
     // helpers
     // flush submits RenderEngine command stream for execution and returns a
@@ -118,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/Surface.h b/libs/renderengine/include/renderengine/Surface.h
deleted file mode 100644
index ba7331d..0000000
--- a/libs/renderengine/include/renderengine/Surface.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-struct ANativeWindow;
-
-namespace android {
-namespace renderengine {
-
-class Surface {
-public:
-    virtual ~Surface() = default;
-
-    virtual void setCritical(bool enable) = 0;
-    virtual void setAsync(bool enable) = 0;
-
-    virtual void setNativeWindow(ANativeWindow* window) = 0;
-    virtual void swapBuffers() const = 0;
-
-    virtual int32_t queryRedSize() const = 0;
-    virtual int32_t queryGreenSize() const = 0;
-    virtual int32_t queryBlueSize() const = 0;
-    virtual int32_t queryAlphaSize() const = 0;
-
-    virtual int32_t getWidth() const = 0;
-    virtual int32_t getHeight() const = 0;
-};
-
-} // namespace renderengine
-} // namespace android
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/Android.bp b/libs/ui/Android.bp
index 0a0c8ca..f770975 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -109,6 +109,7 @@
     // bufferhub is not used when building libgui for vendors
     target: {
         vendor: {
+            cflags: ["-DLIBUI_IN_VNDK"],
             exclude_srcs: [
                 "BufferHubBuffer.cpp",
                 "BufferHubMetadata.cpp",
@@ -116,6 +117,7 @@
             exclude_header_libs: [
                 "libbufferhub_headers",
                 "libdvr_headers",
+                "libnativewindow_headers",
             ],
             exclude_shared_libs: [
                 "libpdx_default_transport",
@@ -128,6 +130,7 @@
         "libbufferhub_headers",
         "libdvr_headers",
         "libnativebase_headers",
+        "libnativewindow_headers",
         "libhardware_headers",
         "libui_headers",
         "libpdx_headers",
@@ -155,6 +158,7 @@
     vendor_available: true,
     target: {
         vendor: {
+            cflags: ["-DLIBUI_IN_VNDK"],
             override_export_include_dirs: ["include_vndk"],
         },
     },
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index 8cc1a4e..e747ee1 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -36,10 +36,12 @@
 #include <private/dvr/bufferhub_rpc.h>
 #pragma clang diagnostic pop
 
-#include <ui/BufferHubBuffer.h>
-
 #include <poll.h>
 
+#include <android-base/unique_fd.h>
+#include <ui/BufferHubBuffer.h>
+
+using android::base::unique_fd;
 using android::dvr::BufferTraits;
 using android::dvr::DetachedBufferRPC;
 using android::dvr::NativeHandleWrapper;
@@ -132,7 +134,9 @@
     const int bufferId = bufferTraits.id();
 
     // Import the metadata.
-    mMetadata = BufferHubMetadata::Import(bufferTraits.take_metadata_handle());
+    LocalHandle metadataHandle = bufferTraits.take_metadata_handle();
+    unique_fd metadataFd(metadataHandle.Release());
+    mMetadata = BufferHubMetadata::Import(std::move(metadataFd));
 
     if (!mMetadata.IsValid()) {
         ALOGE("BufferHubBuffer::ImportGraphicBuffer: invalid metadata.");
@@ -156,6 +160,16 @@
     // GraphicBuffer instance can be created in future.
     mBufferHandle = bufferTraits.take_buffer_handle();
 
+    // Populate buffer desc based on buffer traits.
+    mBufferDesc.width = bufferTraits.width();
+    mBufferDesc.height = bufferTraits.height();
+    mBufferDesc.layers = bufferTraits.layer_count();
+    mBufferDesc.format = bufferTraits.format();
+    mBufferDesc.usage = bufferTraits.usage();
+    mBufferDesc.stride = bufferTraits.stride();
+    mBufferDesc.rfu0 = 0U;
+    mBufferDesc.rfu1 = 0U;
+
     // If all imports succeed, replace the previous buffer and id.
     mId = bufferId;
     mClientStateMask = bufferTraits.client_state_mask();
diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp
index 1e08ed1..18d9a2c 100644
--- a/libs/ui/BufferHubMetadata.cpp
+++ b/libs/ui/BufferHubMetadata.cpp
@@ -48,47 +48,47 @@
         return {};
     }
 
-    // Hand over the ownership of the fd to a pdx::LocalHandle immediately after the successful
-    // return of ashmem_create_region. The ashmemHandle is going to own the fd and to prevent fd
+    // Hand over the ownership of the fd to a unique_fd immediately after the successful
+    // return of ashmem_create_region. The ashmemFd is going to own the fd and to prevent fd
     // leaks during error handling.
-    pdx::LocalHandle ashmemHandle{fd};
+    unique_fd ashmemFd{fd};
 
-    if (ashmem_set_prot_region(ashmemHandle.Get(), kAshmemProt) != 0) {
+    if (ashmem_set_prot_region(ashmemFd.get(), kAshmemProt) != 0) {
         ALOGE("BufferHubMetadata::Create: failed to set protect region.");
         return {};
     }
 
-    return BufferHubMetadata::Import(std::move(ashmemHandle));
+    return BufferHubMetadata::Import(std::move(ashmemFd));
 }
 
 /* static */
-BufferHubMetadata BufferHubMetadata::Import(pdx::LocalHandle ashmemHandle) {
-    if (!ashmem_valid(ashmemHandle.Get())) {
+BufferHubMetadata BufferHubMetadata::Import(unique_fd ashmemFd) {
+    if (!ashmem_valid(ashmemFd.get())) {
         ALOGE("BufferHubMetadata::Import: invalid ashmem fd.");
         return {};
     }
 
-    size_t metadataSize = static_cast<size_t>(ashmem_get_size_region(ashmemHandle.Get()));
+    size_t metadataSize = static_cast<size_t>(ashmem_get_size_region(ashmemFd.get()));
     size_t userMetadataSize = metadataSize - kMetadataHeaderSize;
 
     // Note that here the buffer state is mapped from shared memory as an atomic object. The
     // std::atomic's constructor will not be called so that the original value stored in the memory
     // region can be preserved.
     auto metadataHeader = static_cast<MetadataHeader*>(mmap(nullptr, metadataSize, kAshmemProt,
-                                                            MAP_SHARED, ashmemHandle.Get(),
+                                                            MAP_SHARED, ashmemFd.get(),
                                                             /*offset=*/0));
     if (metadataHeader == nullptr) {
         ALOGE("BufferHubMetadata::Import: failed to map region.");
         return {};
     }
 
-    return BufferHubMetadata(userMetadataSize, std::move(ashmemHandle), metadataHeader);
+    return BufferHubMetadata(userMetadataSize, std::move(ashmemFd), metadataHeader);
 }
 
-BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, pdx::LocalHandle ashmemHandle,
+BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
                                      MetadataHeader* metadataHeader)
       : mUserMetadataSize(userMetadataSize),
-        mAshmemHandle(std::move(ashmemHandle)),
+        mAshmemFd(std::move(ashmemFd)),
         mMetadataHeader(metadataHeader) {}
 
 BufferHubMetadata::~BufferHubMetadata() {
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 29deb01..e606e26 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -22,6 +22,10 @@
 
 #include <grallocusage/GrallocUsageConversion.h>
 
+#ifndef LIBUI_IN_VNDK
+#include <ui/BufferHubBuffer.h>
+#endif // LIBUI_IN_VNDK
+
 #include <ui/Gralloc2.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
@@ -89,6 +93,21 @@
                                 inUsage, inStride);
 }
 
+#ifndef LIBUI_IN_VNDK
+GraphicBuffer::GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer) : GraphicBuffer() {
+    if (buffer == nullptr) {
+        mInitCheck = BAD_VALUE;
+        return;
+    }
+
+    mInitCheck = initWithHandle(buffer->DuplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE,
+                                buffer->desc().width, buffer->desc().height,
+                                static_cast<PixelFormat>(buffer->desc().format),
+                                buffer->desc().layers, buffer->desc().usage, buffer->desc().stride);
+    mBufferHubBuffer = std::move(buffer);
+}
+#endif // LIBUI_IN_VNDK
+
 GraphicBuffer::~GraphicBuffer()
 {
     if (handle) {
@@ -483,6 +502,12 @@
     return NO_ERROR;
 }
 
+#ifndef LIBUI_IN_VNDK
+bool GraphicBuffer::isBufferHubBuffer() const {
+    return mBufferHubBuffer != nullptr;
+}
+#endif // LIBUI_IN_VNDK
+
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index fe4ae6c..618c7d6 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -325,6 +325,20 @@
     return *this;
 }
 
+Region& Region::scaleSelf(float sx, float sy) {
+    size_t count = mStorage.size();
+    Rect* rects = mStorage.editArray();
+    while (count) {
+        rects->left = static_cast<int32_t>(rects->left * sx + 0.5f);
+        rects->right = static_cast<int32_t>(rects->right * sx + 0.5f);
+        rects->top = static_cast<int32_t>(rects->top * sy + 0.5f);
+        rects->bottom = static_cast<int32_t>(rects->bottom * sy + 0.5f);
+        rects++;
+        count--;
+    }
+    return *this;
+}
+
 // ----------------------------------------------------------------------------
 
 const Region Region::merge(const Rect& rhs) const {
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 8e949ec..25128ef 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -95,6 +95,14 @@
     return mMatrix[2][1];
 }
 
+float Transform::sx() const {
+    return mMatrix[0][0];
+}
+
+float Transform::sy() const {
+    return mMatrix[1][1];
+}
+
 void Transform::reset() {
     mType = IDENTITY;
     for(size_t i = 0; i < 3; i++) {
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index daf6192..6850b43 100644
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -38,6 +38,7 @@
 #include <private/dvr/native_handle_wrapper.h>
 #pragma clang diagnostic pop
 
+#include <android/hardware_buffer.h>
 #include <ui/BufferHubMetadata.h>
 
 namespace android {
@@ -62,9 +63,9 @@
     // Allocates a standalone BufferHubBuffer not associated with any producer consumer set.
     static std::unique_ptr<BufferHubBuffer> Create(uint32_t width, uint32_t height,
                                                    uint32_t layerCount, uint32_t format,
-                                                   uint64_t usage, size_t mUserMetadataSize) {
+                                                   uint64_t usage, size_t userMetadataSize) {
         return std::unique_ptr<BufferHubBuffer>(
-                new BufferHubBuffer(width, height, layerCount, format, usage, mUserMetadataSize));
+                new BufferHubBuffer(width, height, layerCount, format, usage, userMetadataSize));
     }
 
     // Imports the given channel handle to a BufferHubBuffer, taking ownership.
@@ -79,6 +80,9 @@
     // bufferhubd share the same buffer id.
     int id() const { return mId; }
 
+    // Returns the buffer description, which is guaranteed to be faithful values from bufferhubd.
+    const AHardwareBuffer_Desc& desc() const { return mBufferDesc; }
+
     const native_handle_t* DuplicateHandle() { return mBufferHandle.DuplicateHandle(); }
 
     // Returns the current value of MetadataHeader::buffer_state.
@@ -118,7 +122,7 @@
 
 private:
     BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
-                    uint64_t usage, size_t mUserMetadataSize);
+                    uint64_t usage, size_t userMetadataSize);
 
     BufferHubBuffer(pdx::LocalChannelHandle mChannelHandle);
 
@@ -128,6 +132,9 @@
     int mId = -1;
     uint64_t mClientStateMask = 0;
 
+    // Stores ground truth of the buffer.
+    AHardwareBuffer_Desc mBufferDesc;
+
     // Wrapps the gralloc buffer handle of this buffer.
     dvr::NativeHandleWrapper<pdx::LocalHandle> mBufferHandle;
 
diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h
index 94a9000..4261971 100644
--- a/libs/ui/include/ui/BufferHubMetadata.h
+++ b/libs/ui/include/ui/BufferHubMetadata.h
@@ -33,12 +33,17 @@
 #pragma clang diagnostic ignored "-Wundefined-func-template"
 #pragma clang diagnostic ignored "-Wunused-template"
 #pragma clang diagnostic ignored "-Wweak-vtables"
-#include <pdx/file_handle.h>
 #include <private/dvr/buffer_hub_defs.h>
 #pragma clang diagnostic pop
 
+#include <android-base/unique_fd.h>
+
 namespace android {
 
+namespace {
+using base::unique_fd;
+} // namespace
+
 class BufferHubMetadata {
 public:
     // Creates a new BufferHubMetadata backed by an ashmem region.
@@ -50,11 +55,8 @@
 
     // Imports an existing BufferHubMetadata from an ashmem FD.
     //
-    // TODO(b/112338294): Refactor BufferHub to use Binder as its internal IPC backend instead of
-    // UDS.
-    //
-    // @param ashmemHandle Ashmem file handle representing an ashmem region.
-    static BufferHubMetadata Import(pdx::LocalHandle ashmemHandle);
+    // @param ashmemFd Ashmem file descriptor representing an ashmem region.
+    static BufferHubMetadata Import(unique_fd ashmemFd);
 
     BufferHubMetadata() = default;
 
@@ -67,7 +69,7 @@
             mUserMetadataSize = other.mUserMetadataSize;
             other.mUserMetadataSize = 0;
 
-            mAshmemHandle = std::move(other.mAshmemHandle);
+            mAshmemFd = std::move(other.mAshmemFd);
 
             // The old raw mMetadataHeader pointer must be cleared, otherwise the destructor will
             // automatically mummap() the shared memory.
@@ -79,25 +81,25 @@
 
     // Returns true if the metadata is valid, i.e. the metadata has a valid ashmem fd and the ashmem
     // has been mapped into virtual address space.
-    bool IsValid() const { return mAshmemHandle.IsValid() && mMetadataHeader != nullptr; }
+    bool IsValid() const { return mAshmemFd.get() != -1 && mMetadataHeader != nullptr; }
 
     size_t user_metadata_size() const { return mUserMetadataSize; }
     size_t metadata_size() const {
         return mUserMetadataSize + dvr::BufferHubDefs::kMetadataHeaderSize;
     }
 
-    const pdx::LocalHandle& ashmem_handle() const { return mAshmemHandle; }
+    const unique_fd& ashmem_fd() const { return mAshmemFd; }
     dvr::BufferHubDefs::MetadataHeader* metadata_header() { return mMetadataHeader; }
 
 private:
-    BufferHubMetadata(size_t userMetadataSize, pdx::LocalHandle ashmemHandle,
+    BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
                       dvr::BufferHubDefs::MetadataHeader* metadataHeader);
 
     BufferHubMetadata(const BufferHubMetadata&) = delete;
     void operator=(const BufferHubMetadata&) = delete;
 
     size_t mUserMetadataSize = 0;
-    pdx::LocalHandle mAshmemHandle;
+    unique_fd mAshmemFd;
     dvr::BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr;
 };
 
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index fe6229a..81f6cd9 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -34,6 +34,10 @@
 
 namespace android {
 
+#ifndef LIBUI_IN_VNDK
+class BufferHubBuffer;
+#endif // LIBUI_IN_VNDK
+
 class GraphicBufferMapper;
 
 // ===========================================================================
@@ -133,6 +137,11 @@
     GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
             uint32_t inUsage, std::string requestorName = "<Unknown>");
 
+#ifndef LIBUI_IN_VNDK
+    // Create a GraphicBuffer from an existing BufferHubBuffer.
+    GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer);
+#endif // LIBUI_IN_VNDK
+
     // return status
     status_t initCheck() const;
 
@@ -188,6 +197,11 @@
     status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
     status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
 
+#ifndef LIBUI_IN_VNDK
+    // Returns whether this GraphicBuffer is backed by BufferHubBuffer.
+    bool isBufferHubBuffer() const;
+#endif // LIBUI_IN_VNDK
+
 private:
     ~GraphicBuffer();
 
@@ -237,6 +251,11 @@
     // match the BufferQueue's internal generation number (set through
     // IGBP::setGenerationNumber), attempts to attach the buffer will fail.
     uint32_t mGenerationNumber;
+
+#ifndef LIBUI_IN_VNDK
+    // Stores a BufferHubBuffer that handles buffer signaling, identification.
+    std::unique_ptr<BufferHubBuffer> mBufferHubBuffer;
+#endif // LIBUI_IN_VNDK
 };
 
 }; // namespace android
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 68b60fc..0a09960 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -89,11 +89,13 @@
 
             // these translate rhs first
             Region&     translateSelf(int dx, int dy);
+            Region&     scaleSelf(float sx, float sy);
             Region&     orSelf(const Region& rhs, int dx, int dy);
             Region&     xorSelf(const Region& rhs, int dx, int dy);
             Region&     andSelf(const Region& rhs, int dx, int dy);
             Region&     subtractSelf(const Region& rhs, int dx, int dy);
 
+
             // these translate rhs first
     const   Region      translate(int dx, int dy) const WARN_UNUSED;
     const   Region      merge(const Region& rhs, int dx, int dy) const WARN_UNUSED;
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index 42dca75..900a5c4 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -65,6 +65,8 @@
     const vec3& operator [] (size_t i) const;  // returns column i
     float   tx() const;
     float   ty() const;
+    float   sx() const;
+    float   sy() const;
 
     // modify the transform
     void        reset();
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 4c9c176..00f30a6 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,22 @@
 }
 
 cc_test {
+    name: "GraphicBuffer_test",
+    header_libs: [
+        "libbufferhub_headers",
+        "libdvr_headers",
+        "libnativewindow_headers",
+    ],
+    shared_libs: [
+        "libpdx_default_transport",
+        "libui",
+        "libutils",
+    ],
+    srcs: ["GraphicBuffer_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
     name: "BufferHubBuffer_test",
     header_libs: [
         "libbufferhub_headers",
@@ -37,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 6af8033..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());
@@ -156,20 +157,77 @@
     memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
 
     sp<IBufferClient> client;
+    BufferHubStatus ret;
     IBufferHub::allocateBuffer_cb alloc_cb = [&](const auto& outClient, const auto& status) {
-        ASSERT_EQ(status, BufferHubStatus::NO_ERROR);
-        ASSERT_NE(nullptr, outClient.get());
         client = outClient;
+        ret = status;
     };
     ASSERT_TRUE(bufferhub->allocateBuffer(desc, kUserMetadataSize, alloc_cb).isOk());
+    EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
+    ASSERT_NE(nullptr, client.get());
 
-    IBufferClient::duplicate_cb dup_cb = [](const auto& token, const auto& status) {
-        ASSERT_EQ(status, BufferHubStatus::NO_ERROR);
-        ASSERT_NE(token.getNativeHandle(), nullptr);
-        EXPECT_EQ(token->numInts, 1);
-        EXPECT_EQ(token->numFds, 0);
+    hidl_handle token;
+    IBufferClient::duplicate_cb dup_cb = [&](const auto& outToken, const auto& status) {
+        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 4209392..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 {
@@ -43,59 +43,51 @@
   EXPECT_TRUE(m1.IsValid());
   EXPECT_NE(m1.metadata_header(), nullptr);
 
-  pdx::LocalHandle h2 = m1.ashmem_handle().Duplicate();
-  EXPECT_TRUE(h2.IsValid());
+  unique_fd h2 = unique_fd(dup(m1.ashmem_fd().get()));
+  EXPECT_NE(h2.get(), -1);
 
   BufferHubMetadata m2 = BufferHubMetadata::Import(std::move(h2));
-  EXPECT_FALSE(h2.IsValid());
+  EXPECT_EQ(h2.get(), -1);
   EXPECT_TRUE(m1.IsValid());
   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) {
   BufferHubMetadata m1 = BufferHubMetadata::Create(sizeof(int));
   EXPECT_TRUE(m1.IsValid());
   EXPECT_NE(m1.metadata_header(), nullptr);
-  EXPECT_TRUE(m1.ashmem_handle().IsValid());
+  EXPECT_NE(m1.ashmem_fd().get(), -1);
   EXPECT_EQ(m1.user_metadata_size(), sizeof(int));
 
   BufferHubMetadata m2 = std::move(m1);
 
-  // After the move, the metadata header (a raw pointer) should be reset in the
-  // older buffer.
+  // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
   EXPECT_EQ(m1.metadata_header(), nullptr);
   EXPECT_NE(m2.metadata_header(), nullptr);
 
-  EXPECT_FALSE(m1.ashmem_handle().IsValid());
-  EXPECT_TRUE(m2.ashmem_handle().IsValid());
+  EXPECT_EQ(m1.ashmem_fd().get(), -1);
+  EXPECT_NE(m2.ashmem_fd().get(), -1);
 
   EXPECT_EQ(m1.user_metadata_size(), 0U);
   EXPECT_EQ(m2.user_metadata_size(), sizeof(int));
 
   BufferHubMetadata m3{std::move(m2)};
 
-  // After the move, the metadata header (a raw pointer) should be reset in the
-  // older buffer.
+  // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
   EXPECT_EQ(m2.metadata_header(), nullptr);
   EXPECT_NE(m3.metadata_header(), nullptr);
 
-  EXPECT_FALSE(m2.ashmem_handle().IsValid());
-  EXPECT_TRUE(m3.ashmem_handle().IsValid());
+  EXPECT_EQ(m2.ashmem_fd().get(), -1);
+  EXPECT_NE(m3.ashmem_fd().get(), -1);
 
   EXPECT_EQ(m2.user_metadata_size(), 0U);
   EXPECT_EQ(m3.user_metadata_size(), sizeof(int));
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
new file mode 100644
index 0000000..95ca2c1
--- /dev/null
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "GraphicBufferTest"
+
+#include <ui/BufferHubBuffer.h>
+#include <ui/GraphicBuffer.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace {
+
+constexpr uint32_t kTestWidth = 1024;
+constexpr uint32_t kTestHeight = 1;
+constexpr uint32_t kTestFormat = HAL_PIXEL_FORMAT_BLOB;
+constexpr uint32_t kTestLayerCount = 1;
+constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN;
+
+} // namespace
+
+class GraphicBufferTest : public testing::Test {};
+
+TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) {
+    std::unique_ptr<BufferHubBuffer> b1 =
+            BufferHubBuffer::Create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+                                    kTestUsage, /*userMetadataSize=*/0);
+    EXPECT_TRUE(b1->IsValid());
+
+    sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1)));
+    EXPECT_TRUE(gb->isBufferHubBuffer());
+
+    EXPECT_EQ(gb->getWidth(), kTestWidth);
+    EXPECT_EQ(gb->getHeight(), kTestHeight);
+    EXPECT_EQ(static_cast<uint32_t>(gb->getPixelFormat()), kTestFormat);
+    EXPECT_EQ(gb->getUsage(), kTestUsage);
+    EXPECT_EQ(gb->getLayerCount(), kTestLayerCount);
+}
+
+} // namespace android
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..9bcfaa1 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,20 @@
   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 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,18 +234,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 in gained state should fail.
   EXPECT_EQ(-EBUSY, c->Acquire(&fence));
-  EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
-  EXPECT_EQ(-EALREADY, p->Gain(&fence));
 }
 
 TEST_F(LibBufferHubTest, TestAsyncStateTransitions) {
@@ -269,24 +255,20 @@
 
   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 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 +277,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 +292,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,23 +301,32 @@
   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());
+}
+
+TEST_F(LibBufferHubTest, TestGainTwiceByTheSameProducer) {
+  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->GainAsync());
 }
 
 TEST_F(LibBufferHubTest, TestGainPostedBuffer) {
   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 +338,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 +353,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 +418,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 +480,7 @@
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c1.get() != nullptr);
 
+  EXPECT_EQ(0, p->GainAsync());
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
 
@@ -503,7 +505,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 +519,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 +548,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 +577,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 +603,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 +619,7 @@
   std::unique_ptr<ConsumerBuffer> c =
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   LocalHandle fence;
 
@@ -627,6 +634,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 +656,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 +720,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 +826,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 +929,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 +980,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..c177970 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,52 @@
 
 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());
-    return -EALREADY;
+  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 0;
   }
-  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::AnyClientGained(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::AnyClientGained(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 +221,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 +288,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/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index e27f233..6edadcd 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -41,13 +41,27 @@
 extern "C" {
   android_namespace_t* android_get_exported_namespace(const char*);
 
-  // TODO(ianelliott@): Get this from an ANGLE header:
+  // TODO(ianelliott@): Get the following from an ANGLE header:
+  // Version-1 API:
   typedef bool (*fpANGLEGetUtilityAPI)(unsigned int* versionToUse);
-
-  // TODO(ianelliott@): Get this from an ANGLE header:
   typedef bool (*fpAndroidUseANGLEForApplication)(int fd, long offset, long length,
                                                   const char* appName, const char* deviceMfr,
                                                   const char* deviceModel);
+  // Version-2 API:
+  typedef bool (*fpANGLEGetFeatureSupportUtilAPIVersion)(unsigned int* versionToUse);
+  typedef bool (*fpANGLEAndroidParseRulesString)(const char *rulesString,
+                                                 void** rulesHandle, int* rulesVersion);
+  typedef bool (*fpANGLEGetSystemInfo)(void** handle);
+  typedef bool (*fpANGLEAddDeviceInfoToSystemInfo)(const char* deviceMfr,
+                                                   const char* deviceModel,
+                                                   void* handle);
+  typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rules_handle,
+                                                    int rules_version,
+                                                    void* system_info_handle,
+                                                    const char *appName);
+  typedef bool (*fpANGLEFreeRulesHandle)(void* handle);
+  typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle);
+
 }
 
 // ----------------------------------------------------------------------------
@@ -523,19 +537,21 @@
     property_get("ro.product.manufacturer", manufacturer, "UNSET");
     property_get("ro.product.model", model, "UNSET");
 
+    // TODO: Replace this with the new function name once the version-1 API is removed:
     fpANGLEGetUtilityAPI ANGLEGetUtilityAPI =
             (fpANGLEGetUtilityAPI)dlsym(so, "ANGLEGetUtilityAPI");
 
     if (ANGLEGetUtilityAPI) {
 
         // Negotiate the interface version by requesting most recent known to the platform
-        unsigned int versionToUse = 1;
+        unsigned int versionToUse = 2;
+        // TODO: Replace this with the new function name once the version-1 API is removed:
         if ((ANGLEGetUtilityAPI)(&versionToUse)) {
 
             // Add and remove versions below as needed
             switch(versionToUse) {
             case 1: {
-                ALOGV("Using version 1 of ANGLE opt-in/out logic interface");
+                ALOGV("Using version 1 of ANGLE feature-support library");
                 fpAndroidUseANGLEForApplication AndroidUseANGLEForApplication =
                         (fpAndroidUseANGLEForApplication)dlsym(so, "AndroidUseANGLEForApplication");
 
@@ -548,6 +564,65 @@
                 }
             }
             break;
+            case 2: {
+                ALOGV("Using version 2 of ANGLE feature-support library");
+                void* rules_handle = nullptr;
+                int rules_version = 0;
+                void* system_info_handle = nullptr;
+
+                // Get the symbols for the feature-support-utility library:
+#define GET_SYMBOL(symbol)                                               \
+    fp##symbol symbol = (fp##symbol)dlsym(so, #symbol);                  \
+    if (!symbol) {                                                       \
+        ALOGW("Cannot find "#symbol" in ANGLE feature-support library"); \
+        break;                                                           \
+    }
+                GET_SYMBOL(ANGLEAndroidParseRulesString);
+                GET_SYMBOL(ANGLEGetSystemInfo);
+                GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo);
+                GET_SYMBOL(ANGLEShouldBeUsedForApplication);
+                GET_SYMBOL(ANGLEFreeRulesHandle);
+                GET_SYMBOL(ANGLEFreeSystemInfoHandle);
+
+                // Read the contents of the file into a string:
+                off_t fileSize       = rules_length;
+                off_t startOfContent = rules_offset;
+                lseek(rules_fd, startOfContent, SEEK_SET);
+                char *buffer                   = new char[fileSize + 1];
+                ssize_t numBytesRead           = read(rules_fd, buffer, fileSize);
+                if (numBytesRead < 0) {
+                    ALOGW("Cannot read rules file");
+                    break;
+                }
+                if (numBytesRead == 0) {
+                    ALOGW("Empty rules file");
+                    break;
+                }
+                buffer[numBytesRead]           = '\0';
+                std::string rule_file_contents = std::string(buffer);
+                delete[] buffer;
+
+                // Parse the rules, obtain the SystemInfo, and evaluate the
+                // application against the rules:
+                if (!(ANGLEAndroidParseRulesString)(rule_file_contents.c_str(),
+                                                    &rules_handle, &rules_version)) {
+                    ALOGW("ANGLE feature-support library cannot parse rules file");
+                    break;
+                }
+                if (!(ANGLEGetSystemInfo)(&system_info_handle)) {
+                    ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
+                    break;
+                }
+                if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, system_info_handle)) {
+                    ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
+                    break;
+                }
+                use_angle = (ANGLEShouldBeUsedForApplication)(rules_handle, rules_version,
+                                                              system_info_handle, app_name_str.c_str());
+                (ANGLEFreeRulesHandle)(rules_handle);
+                (ANGLEFreeSystemInfoHandle)(system_info_handle);
+            }
+            break;
             default:
                 ALOGW("Cannot find supported version of ANGLE feature-support library, found version %u", versionToUse);
             }
diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp
index 28a7501..b747dbc 100644
--- a/services/bufferhub/Android.bp
+++ b/services/bufferhub/Android.bp
@@ -17,6 +17,7 @@
 cc_library_shared {
     name: "libbufferhubservice",
     cflags: [
+        "-DLOG_TAG=\"libbufferhubservice\"",
         "-Wall",
         "-Werror",
         "-Wextra",
@@ -71,6 +72,7 @@
         "libutils",
     ],
     cflags: [
+        "-DLOG_TAG=\"bufferhub\"",
         "-Wall",
         "-Werror",
         "-Wextra",
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index fc5ad1d..6f97f0d 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -51,14 +51,48 @@
     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();
 }
 
-hidl_handle BufferHubService::registerToken(const BufferClient* client) {
+hidl_handle BufferHubService::registerToken(const wp<BufferClient>& client) {
     uint32_t token;
     std::lock_guard<std::mutex> lock(mTokenMapMutex);
     do {
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 5441750..6535659 100644
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -42,22 +42,22 @@
     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.
-    hidl_handle registerToken(const BufferClient* client);
+    hidl_handle registerToken(const wp<BufferClient>& client);
 
 private:
     // List of active BufferClient for bookkeeping.
     std::mutex mClientListMutex;
-    std::vector<sp<BufferClient>> mClientList GUARDED_BY(mClientListMutex);
+    std::vector<wp<BufferClient>> mClientList GUARDED_BY(mClientListMutex);
 
     // TODO(b/118180214): use a more secure implementation
     std::mt19937 mTokenEngine;
     // The mapping from token to the client creates it.
     std::mutex mTokenMapMutex;
-    std::map<uint32_t, const BufferClient*> mTokenMap GUARDED_BY(mTokenMapMutex);
+    std::map<uint32_t, const wp<BufferClient>> mTokenMap GUARDED_BY(mTokenMapMutex);
 };
 
 } // namespace implementation
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 0c9e04b..1dca1b6 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -489,7 +489,7 @@
         if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
                 && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                 && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
-                && mInputTargetWaitApplicationHandle != nullptr) {
+                && mInputTargetWaitApplicationToken != nullptr) {
             int32_t displayId = motionEntry->displayId;
             int32_t x = int32_t(motionEntry->pointerCoords[0].
                     getAxisValue(AMOTION_EVENT_AXIS_X));
@@ -497,8 +497,8 @@
                     getAxisValue(AMOTION_EVENT_AXIS_Y));
             sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
             if (touchedWindowHandle != nullptr
-                    && touchedWindowHandle->inputApplicationHandle
-                            != mInputTargetWaitApplicationHandle) {
+                    && touchedWindowHandle->getApplicationToken()
+                            != mInputTargetWaitApplicationToken) {
                 // User touched a different application than the one we are waiting on.
                 // Flag the event, and start pruning the input queue.
                 mNextUnblockedEvent = motionEntry;
@@ -819,7 +819,8 @@
             sp<InputWindowHandle> focusedWindowHandle =
                     getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
             if (focusedWindowHandle != nullptr) {
-                commandEntry->inputChannel = focusedWindowHandle->getInputChannel();
+                commandEntry->inputChannel =
+                    getInputChannelLocked(focusedWindowHandle->getToken());
             }
             commandEntry->keyEntry = entry;
             entry->refCount += 1;
@@ -1010,7 +1011,7 @@
             mInputTargetWaitStartTime = currentTime;
             mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
             mInputTargetWaitTimeoutExpired = false;
-            mInputTargetWaitApplicationHandle.clear();
+            mInputTargetWaitApplicationToken.clear();
         }
     } else {
         if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
@@ -1033,13 +1034,13 @@
             mInputTargetWaitStartTime = currentTime;
             mInputTargetWaitTimeoutTime = currentTime + timeout;
             mInputTargetWaitTimeoutExpired = false;
-            mInputTargetWaitApplicationHandle.clear();
+            mInputTargetWaitApplicationToken.clear();
 
             if (windowHandle != nullptr) {
-                mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
+                mInputTargetWaitApplicationToken = windowHandle->getApplicationToken();
             }
-            if (mInputTargetWaitApplicationHandle == nullptr && applicationHandle != nullptr) {
-                mInputTargetWaitApplicationHandle = applicationHandle;
+            if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) {
+                mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken();
             }
         }
     }
@@ -1117,7 +1118,7 @@
 
     // Reset input target wait timeout.
     mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
-    mInputTargetWaitApplicationHandle.clear();
+    mInputTargetWaitApplicationToken.clear();
 }
 
 /**
@@ -1666,11 +1667,13 @@
 
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
     InputTarget& target = inputTargets.editTop();
-    target.inputChannel = windowInfo->inputChannel;
+    target.inputChannel = getInputChannelLocked(windowHandle->getToken());
     target.flags = targetFlags;
     target.xOffset = - windowInfo->frameLeft;
     target.yOffset = - windowInfo->frameTop;
-    target.scaleFactor = windowInfo->scaleFactor;
+    target.globalScaleFactor = windowInfo->globalScaleFactor;
+    target.windowXScale = windowInfo->windowXScale;
+    target.windowYScale = windowInfo->windowYScale;
     target.pointerIds = pointerIds;
 }
 
@@ -1691,7 +1694,7 @@
             target.xOffset = 0;
             target.yOffset = 0;
             target.pointerIds.clear();
-            target.scaleFactor = 1.0f;
+            target.globalScaleFactor = 1.0f;
         }
     } else {
         // If there is no monitor channel registered or all monitor channel unregistered,
@@ -1773,7 +1776,8 @@
     }
 
     // If the window's connection is not registered then keep waiting.
-    ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
+    ssize_t connectionIndex = getConnectionIndexLocked(
+            getInputChannelLocked(windowHandle->getToken()));
     if (connectionIndex < 0) {
         return StringPrintf("Waiting because the %s window's input channel is not "
                 "registered with the input dispatcher.  The window may be in the process "
@@ -1910,11 +1914,13 @@
         const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
-            "xOffset=%f, yOffset=%f, scaleFactor=%f, "
-            "pointerIds=0x%x",
+            "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
+            "windowScaleFactor=(%f, %f), pointerIds=0x%x",
             connection->getInputChannelName().c_str(), inputTarget->flags,
             inputTarget->xOffset, inputTarget->yOffset,
-            inputTarget->scaleFactor, inputTarget->pointerIds.value);
+            inputTarget->globalScaleFactor,
+            inputTarget->windowXScale, inputTarget->windowYScale,
+            inputTarget->pointerIds.value);
 #endif
 
     // Skip this event if the connection status is not normal.
@@ -1991,7 +1997,8 @@
     // Enqueue a new dispatch entry onto the outbound queue for this connection.
     DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
             inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
-            inputTarget->scaleFactor);
+            inputTarget->globalScaleFactor, inputTarget->windowXScale,
+            inputTarget->windowYScale);
 
     // Apply target flags and update the connection's input state.
     switch (eventEntry->type) {
@@ -2107,13 +2114,15 @@
             float xOffset, yOffset;
             if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                     && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
-                float scaleFactor = dispatchEntry->scaleFactor;
-                xOffset = dispatchEntry->xOffset * scaleFactor;
-                yOffset = dispatchEntry->yOffset * scaleFactor;
-                if (scaleFactor != 1.0f) {
+                float globalScaleFactor = dispatchEntry->globalScaleFactor;
+                float wxs = dispatchEntry->windowXScale;
+                float wys = dispatchEntry->windowYScale;
+                xOffset = dispatchEntry->xOffset * wxs;
+                yOffset = dispatchEntry->yOffset * wys;
+                if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
                     for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
                         scaledCoords[i] = motionEntry->pointerCoords[i];
-                        scaledCoords[i].scale(scaleFactor);
+                        scaledCoords[i].scale(globalScaleFactor, wxs, wys);
                     }
                     usingCoords = scaledCoords;
                 }
@@ -2371,11 +2380,13 @@
                 const InputWindowInfo* windowInfo = windowHandle->getInfo();
                 target.xOffset = -windowInfo->frameLeft;
                 target.yOffset = -windowInfo->frameTop;
-                target.scaleFactor = windowInfo->scaleFactor;
+                target.globalScaleFactor = windowInfo->globalScaleFactor;
+                target.windowXScale = windowInfo->windowXScale;
+                target.windowYScale = windowInfo->windowYScale;
             } else {
                 target.xOffset = 0;
                 target.yOffset = 0;
-                target.scaleFactor = 1.0f;
+                target.globalScaleFactor = 1.0f;
             }
             target.inputChannel = connection->inputChannel;
             target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
@@ -3004,7 +3015,7 @@
         size_t numWindows = windowHandles.size();
         for (size_t i = 0; i < numWindows; i++) {
             const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
-            if (windowHandle->getInputChannel() == inputChannel) {
+            if (windowHandle->getToken() == inputChannel->getToken()) {
                 return windowHandle;
             }
         }
@@ -3018,8 +3029,8 @@
         const Vector<sp<InputWindowHandle>> windowHandles = it.second;
         size_t numWindows = windowHandles.size();
         for (size_t i = 0; i < numWindows; i++) {
-            if (windowHandles.itemAt(i)->getInputChannel()->getToken()
-                    == windowHandle->getInputChannel()->getToken()) {
+            if (windowHandles.itemAt(i)->getToken()
+                    == windowHandle->getToken()) {
                 if (windowHandle->getInfo()->displayId != it.first) {
                     ALOGE("Found window %s in display %" PRId32
                             ", but it should belong to display %" PRId32,
@@ -3033,6 +3044,14 @@
     return false;
 }
 
+sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token) const {
+    size_t count = mInputChannelsByToken.count(token);
+    if (count == 0) {
+        return nullptr;
+    }
+    return mInputChannelsByToken.at(token);
+}
+
 /**
  * Called from InputManagerService, update window handle list by displayId that can receive input.
  * A window handle contains information about InputChannel, Touch Region, Types, Focused,...
@@ -3061,7 +3080,9 @@
             size_t numWindows = inputWindowHandles.size();
             for (size_t i = 0; i < numWindows; i++) {
                 const sp<InputWindowHandle>& windowHandle = inputWindowHandles.itemAt(i);
-                if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == nullptr) {
+                if (!windowHandle->updateInfo() || getInputChannelLocked(windowHandle->getToken()) == nullptr) {
+                    ALOGE("Window handle %s has no registered input channel",
+                            windowHandle->getName().c_str());
                     continue;
                 }
 
@@ -3072,7 +3093,7 @@
                     continue;
                 }
 
-                if (windowHandle->getInfo()->hasFocus) {
+                if (windowHandle->getInfo()->hasFocus && windowHandle->getInfo()->visible) {
                     newFocusedWindowHandle = windowHandle;
                 }
                 if (windowHandle == mLastHoverWindowHandle) {
@@ -3097,7 +3118,8 @@
                 ALOGD("Focus left window: %s in display %" PRId32,
                         oldFocusedWindowHandle->getName().c_str(), displayId);
 #endif
-                sp<InputChannel> focusedInputChannel = oldFocusedWindowHandle->getInputChannel();
+                sp<InputChannel> focusedInputChannel = getInputChannelLocked(
+                        oldFocusedWindowHandle->getToken());
                 if (focusedInputChannel != nullptr) {
                     CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                             "focus left window");
@@ -3113,6 +3135,11 @@
 #endif
                 mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
             }
+
+            if (mFocusedDisplayId == displayId) {
+                onFocusChangedLocked(newFocusedWindowHandle);
+            }
+
         }
 
         ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
@@ -3126,7 +3153,7 @@
                             touchedWindow.windowHandle->getName().c_str(), displayId);
 #endif
                     sp<InputChannel> touchedInputChannel =
-                            touchedWindow.windowHandle->getInputChannel();
+                            getInputChannelLocked(touchedWindow.windowHandle->getToken());
                     if (touchedInputChannel != nullptr) {
                         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                                 "touched window was removed");
@@ -3214,7 +3241,8 @@
             sp<InputWindowHandle> oldFocusedWindowHandle =
                     getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
             if (oldFocusedWindowHandle != nullptr) {
-                sp<InputChannel> inputChannel = oldFocusedWindowHandle->getInputChannel();
+                sp<InputChannel> inputChannel =
+                    getInputChannelLocked(oldFocusedWindowHandle->getToken());
                 if (inputChannel != nullptr) {
                     CancelationOptions options(
                             CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS,
@@ -3227,6 +3255,8 @@
             // Sanity check
             sp<InputWindowHandle> newFocusedWindowHandle =
                     getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+            onFocusChangedLocked(newFocusedWindowHandle);
+
             if (newFocusedWindowHandle == nullptr) {
                 ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
                 if (!mFocusedWindowHandlesByDisplay.empty()) {
@@ -3488,7 +3518,7 @@
                     dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
                             "paused=%s, hasFocus=%s, hasWallpaper=%s, "
                             "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
-                            "frame=[%d,%d][%d,%d], scale=%f, "
+                            "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), "
                             "touchableRegion=",
                             i, windowInfo->name.c_str(), windowInfo->displayId,
                             toString(windowInfo->paused),
@@ -3500,7 +3530,8 @@
                             windowInfo->layer,
                             windowInfo->frameLeft, windowInfo->frameTop,
                             windowInfo->frameRight, windowInfo->frameBottom,
-                            windowInfo->scaleFactor);
+                            windowInfo->globalScaleFactor,
+                            windowInfo->windowXScale, windowInfo->windowYScale);
                     dumpRegion(dump, windowInfo->touchableRegion);
                     dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
                     dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
@@ -3667,6 +3698,7 @@
 
         int fd = inputChannel->getFd();
         mConnectionsByFd.add(fd, connection);
+        mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
 
         // Store monitor channel by displayId.
         if (monitor) {
@@ -3715,6 +3747,8 @@
     sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
     mConnectionsByFd.removeItemsAt(connectionIndex);
 
+    mInputChannelsByToken.erase(inputChannel->getToken());
+
     if (connection->monitor) {
         removeMonitorChannelLocked(inputChannel);
     }
@@ -3782,6 +3816,13 @@
     commandEntry->connection = connection;
 }
 
+void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& newFocus) {
+    sp<IBinder> token = newFocus != nullptr ? newFocus->getToken() : nullptr;
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doNotifyFocusChangedLockedInterruptible);
+    commandEntry->token = token;
+}
+
 void InputDispatcher::onANRLocked(
         nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
         const sp<InputWindowHandle>& windowHandle,
@@ -3812,7 +3853,8 @@
     CommandEntry* commandEntry = postCommandLocked(
             & InputDispatcher::doNotifyANRLockedInterruptible);
     commandEntry->inputApplicationHandle = applicationHandle;
-    commandEntry->inputChannel = windowHandle != nullptr ? windowHandle->getInputChannel() : nullptr;
+    commandEntry->inputChannel = windowHandle != nullptr ?
+            getInputChannelLocked(windowHandle->getToken()) : nullptr;
     commandEntry->reason = reason;
 }
 
@@ -3838,6 +3880,14 @@
     }
 }
 
+void InputDispatcher::doNotifyFocusChangedLockedInterruptible(
+        CommandEntry* commandEntry) {
+    sp<IBinder> token = commandEntry->token;
+    mLock.unlock();
+    mPolicy->notifyFocusChanged(token);
+    mLock.lock();
+}
+
 void InputDispatcher::doNotifyANRLockedInterruptible(
         CommandEntry* commandEntry) {
     mLock.unlock();
@@ -4343,10 +4393,12 @@
 volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic;
 
 InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,
-        int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) :
+        int32_t targetFlags, float xOffset, float yOffset, float globalScaleFactor,
+        float windowXScale, float windowYScale) :
         seq(nextSeq()),
         eventEntry(eventEntry), targetFlags(targetFlags),
-        xOffset(xOffset), yOffset(yOffset), scaleFactor(scaleFactor),
+        xOffset(xOffset), yOffset(yOffset), globalScaleFactor(globalScaleFactor),
+        windowXScale(windowXScale), windowYScale(windowYScale),
         deliveryTime(0), resolvedAction(0), resolvedFlags(0) {
     eventEntry->refCount += 1;
 }
@@ -4847,7 +4899,7 @@
 
 void InputDispatcher::TouchState::removeWindowByToken(const sp<IBinder>& token) {
     for (size_t i = 0; i < windows.size(); i++) {
-        if (windows.itemAt(i).windowHandle->getInputChannel()->getToken() == token) {
+        if (windows.itemAt(i).windowHandle->getToken() == token) {
             windows.removeAt(i);
             return;
         }
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 5016082..73bcc25 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -29,6 +29,7 @@
 #include <utils/Looper.h>
 #include <utils/BitSet.h>
 #include <cutils/atomic.h>
+#include <unordered_map>
 
 #include <stddef.h>
 #include <unistd.h>
@@ -159,7 +160,9 @@
 
     // Scaling factor to apply to MotionEvent as it is delivered.
     // (ignored for KeyEvents)
-    float scaleFactor;
+    float globalScaleFactor;
+    float windowXScale = 1.0f;
+    float windowYScale = 1.0f;
 
     // The subset of pointer ids to include in motion events dispatched to this input target
     // if FLAG_SPLIT is set.
@@ -213,6 +216,7 @@
 
     /* Notifies the system that an input channel is unrecoverably broken. */
     virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
+    virtual void notifyFocusChanged(const sp<IBinder>& token) = 0;
 
     /* Gets the input dispatcher configuration. */
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
@@ -561,7 +565,9 @@
         int32_t targetFlags;
         float xOffset;
         float yOffset;
-        float scaleFactor;
+        float globalScaleFactor;
+        float windowXScale = 1.0f;
+        float windowYScale = 1.0f;
         nsecs_t deliveryTime; // time when the event was actually delivered
 
         // Set to the resolved action and flags when the event is enqueued.
@@ -569,7 +575,8 @@
         int32_t resolvedFlags;
 
         DispatchEntry(EventEntry* eventEntry,
-                int32_t targetFlags, float xOffset, float yOffset, float scaleFactor);
+                int32_t targetFlags, float xOffset, float yOffset,
+                float globalScaleFactor, float windowXScale, float windowYScale);
         ~DispatchEntry();
 
         inline bool hasForegroundTarget() const {
@@ -622,6 +629,7 @@
         uint32_t seq;
         bool handled;
         sp<InputChannel> inputChannel;
+        sp<IBinder> token;
     };
 
     // Generic queue implementation.
@@ -916,6 +924,13 @@
     // All registered connections mapped by channel file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByFd;
 
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& b) const {
+            return std::hash<IBinder *>{}(b.get());
+        }
+    };
+    std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken;
+
     ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
 
     // Input channels that will receive a copy of all input events sent to the provided display.
@@ -979,6 +994,7 @@
     // Get window handles by display, return an empty vector if not found.
     Vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const;
     sp<InputWindowHandle> getWindowHandleLocked(const sp<InputChannel>& inputChannel) const;
+    sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const;
     bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const;
 
     // Focus tracking for keys, trackball, etc.
@@ -1051,7 +1067,7 @@
     nsecs_t mInputTargetWaitStartTime;
     nsecs_t mInputTargetWaitTimeoutTime;
     bool mInputTargetWaitTimeoutExpired;
-    sp<InputApplicationHandle> mInputTargetWaitApplicationHandle;
+    sp<IBinder> mInputTargetWaitApplicationToken;
 
     // Contains the last window which received a hover event.
     sp<InputWindowHandle> mLastHoverWindowHandle;
@@ -1143,6 +1159,7 @@
             nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
     void onDispatchCycleBrokenLocked(
             nsecs_t currentTime, const sp<Connection>& connection);
+    void onFocusChangedLocked(const sp<InputWindowHandle>& newFocus);
     void onANRLocked(
             nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
             const sp<InputWindowHandle>& windowHandle,
@@ -1151,6 +1168,7 @@
     // Outbound policy interactions.
     void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
     void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
+    void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry);
     void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
     void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
     void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry);
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 388423c..15d8070 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -21,9 +21,13 @@
 #include "InputManager.h"
 #include "InputReaderFactory.h"
 
+#include <binder/IPCThreadState.h>
+
 #include <log/log.h>
 #include <unordered_map>
 
+#include <private/android_filesystem_config.h>
+
 namespace android {
 
 InputManager::InputManager(
@@ -83,20 +87,9 @@
     return mDispatcher;
 }
 
-class BinderApplicationHandle : public InputApplicationHandle {
-public:
-    BinderApplicationHandle() = default;
-
-    bool updateInfo() override {
-        return true;
-    }
-};
-
 class BinderWindowHandle : public InputWindowHandle {
 public:
-    BinderWindowHandle(const InputWindowInfo& info) :
-        InputWindowHandle(new BinderApplicationHandle()) {
-
+    BinderWindowHandle(const InputWindowInfo& info) {
         mInfo = info;
     }
 
@@ -118,4 +111,20 @@
     }
 }
 
+// Used by tests only.
+void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int uid = ipc->getCallingUid();
+    if (uid != AID_SHELL && uid != AID_ROOT) {
+        ALOGE("Invalid attempt to register input channel over IPC"
+                "from non shell/root entity (PID: %d)", ipc->getCallingPid());
+        return;
+    }
+    mDispatcher->registerInputChannel(channel, false);
+}
+
+void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
+    mDispatcher->unregisterInputChannel(channel);
+}
+
 } // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 1173fa1..8f7551e 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -35,6 +35,7 @@
 #include <utils/RefBase.h>
 
 namespace android {
+class InputChannel;
 
 /*
  * The input manager is the core of the system event processing.
@@ -91,6 +92,9 @@
 
     virtual void setInputWindows(const Vector<InputWindowInfo>& handles);
 
+    virtual void registerInputChannel(const sp<InputChannel>& channel);
+    virtual void unregisterInputChannel(const sp<InputChannel>& channel);
+
 private:
     sp<InputReaderInterface> mReader;
     sp<InputReaderThread> mReaderThread;
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 9dd14dc..9e748d8 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -991,6 +991,12 @@
             deviceInfo.getDisplayName().c_str());
     dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
     dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
+    dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
+    if (mAssociatedDisplayPort) {
+        dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
+    } else {
+        dump += "<none>\n";
+    }
     dump += StringPrintf(INDENT2 "HasMic:     %s\n", toString(mHasMic));
     dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
     dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
@@ -1060,6 +1066,20 @@
             setEnabled(enabled, when);
         }
 
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+             // In most situations, no port will be specified.
+            mAssociatedDisplayPort = std::nullopt;
+            // Find the display port that corresponds to the current input port.
+            const std::string& inputPort = mIdentifier.location;
+            if (!inputPort.empty()) {
+                const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
+                const auto& displayPort = ports.find(inputPort);
+                if (displayPort != ports.end()) {
+                    mAssociatedDisplayPort = std::make_optional(displayPort->second);
+                }
+            }
+        }
+
         size_t numMappers = mMappers.size();
         for (size_t i = 0; i < numMappers; i++) {
             InputMapper* mapper = mMappers[i];
@@ -2203,7 +2223,7 @@
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         if (mParameters.orientationAware) {
-            mViewport = config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+            mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
         }
     }
 }
@@ -2613,7 +2633,7 @@
         mOrientation = DISPLAY_ORIENTATION_0;
         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
             std::optional<DisplayViewport> internalViewport =
-                    config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+                    config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
             if (internalViewport) {
                 mOrientation = internalViewport->orientation;
             }
@@ -2929,7 +2949,7 @@
     }
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         std::optional<DisplayViewport> internalViewport =
-                config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+                config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
         if (internalViewport) {
             mOrientation = internalViewport->orientation;
         } else {
@@ -3310,6 +3330,9 @@
             mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
         }
     }
+    if (getDevice()->getAssociatedDisplayPort()) {
+        mParameters.hasAssociatedDisplay = true;
+    }
 
     // Initial downs on external touch devices should wake the device.
     // Normally we don't do this for internal touch screens to prevent them from waking
@@ -3384,6 +3407,49 @@
     return mExternalStylusConnected;
 }
 
+/**
+ * Determine which DisplayViewport to use.
+ * 1. If display port is specified, return the matching viewport. If matching viewport not
+ * found, then return.
+ * 2. If a device has associated display, get the matching viewport by either unique id or by
+ * the display type (internal or external).
+ * 3. Otherwise, use a non-display viewport.
+ */
+std::optional<DisplayViewport> TouchInputMapper::findViewport() {
+    if (mParameters.hasAssociatedDisplay) {
+        const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+        if (displayPort) {
+            // Find the viewport that contains the same port
+            std::optional<DisplayViewport> v = mConfig.getDisplayViewportByPort(*displayPort);
+            if (!v) {
+                ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
+                        "but the corresponding viewport is not found.",
+                        getDeviceName().c_str(), *displayPort);
+            }
+            return v;
+        }
+
+        if (!mParameters.uniqueDisplayId.empty()) {
+            return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
+        }
+
+        ViewportType viewportTypeToUse;
+        if (mParameters.associatedDisplayIsExternal) {
+            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+        } else {
+            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+        }
+        return mConfig.getDisplayViewportByType(viewportTypeToUse);
+    }
+
+    DisplayViewport newViewport;
+    // Raw width and height in the natural orientation.
+    int32_t rawWidth = mRawPointerAxes.getRawWidth();
+    int32_t rawHeight = mRawPointerAxes.getRawHeight();
+    newViewport.setNonDisplayViewport(rawWidth, rawHeight);
+    return std::make_optional(newViewport);
+}
+
 void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
     int32_t oldDeviceMode = mDeviceMode;
 
@@ -3417,50 +3483,30 @@
 
     // Ensure we have valid X and Y axes.
     if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
-        ALOGW(INDENT "Touch device '%s' did not report support for X or Y axis!  "
+        ALOGW("Touch device '%s' did not report support for X or Y axis!  "
                 "The device will be inoperable.", getDeviceName().c_str());
         mDeviceMode = DEVICE_MODE_DISABLED;
         return;
     }
 
+    // Get associated display dimensions.
+    std::optional<DisplayViewport> newViewport = findViewport();
+    if (!newViewport) {
+        ALOGI("Touch device '%s' could not query the properties of its associated "
+                "display.  The device will be inoperable until the display size "
+                "becomes available.",
+                getDeviceName().c_str());
+        mDeviceMode = DEVICE_MODE_DISABLED;
+        return;
+    }
+
     // Raw width and height in the natural orientation.
     int32_t rawWidth = mRawPointerAxes.getRawWidth();
     int32_t rawHeight = mRawPointerAxes.getRawHeight();
 
-    // Get associated display dimensions.
-    DisplayViewport newViewport;
-    if (mParameters.hasAssociatedDisplay) {
-        std::string uniqueDisplayId;
-        ViewportType viewportTypeToUse;
-
-        if (mParameters.associatedDisplayIsExternal) {
-            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
-        } else if (!mParameters.uniqueDisplayId.empty()) {
-            // If the IDC file specified a unique display Id, then it expects to be linked to a
-            // virtual display with the same unique ID.
-            uniqueDisplayId = mParameters.uniqueDisplayId;
-            viewportTypeToUse = ViewportType::VIEWPORT_VIRTUAL;
-        } else {
-            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
-        }
-
-        std::optional<DisplayViewport> viewportToUse =
-                mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId);
-        if (!viewportToUse) {
-            ALOGI(INDENT "Touch device '%s' could not query the properties of its associated "
-                    "display.  The device will be inoperable until the display size "
-                    "becomes available.",
-                    getDeviceName().c_str());
-            mDeviceMode = DEVICE_MODE_DISABLED;
-            return;
-        }
-        newViewport = *viewportToUse;
-    } else {
-        newViewport.setNonDisplayViewport(rawWidth, rawHeight);
-    }
-    bool viewportChanged = mViewport != newViewport;
+    bool viewportChanged = mViewport != *newViewport;
     if (viewportChanged) {
-        mViewport = newViewport;
+        mViewport = *newViewport;
 
         if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
             // Convert rotated viewport to natural surface coordinates.
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 1786fe8..13f1bed 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -252,6 +252,9 @@
 
     inline bool isExternal() { return mIsExternal; }
     inline void setExternal(bool external) { mIsExternal = external; }
+    inline std::optional<uint8_t> getAssociatedDisplayPort() const {
+        return mAssociatedDisplayPort;
+    }
 
     inline void setMic(bool hasMic) { mHasMic = hasMic; }
     inline bool hasMic() const { return mHasMic; }
@@ -324,6 +327,7 @@
 
     uint32_t mSources;
     bool mIsExternal;
+    std::optional<uint8_t> mAssociatedDisplayPort;
     bool mHasMic;
     bool mDropUntilNextSync;
 
@@ -1501,6 +1505,8 @@
     VelocityControl mWheelXVelocityControl;
     VelocityControl mWheelYVelocityControl;
 
+    std::optional<DisplayViewport> findViewport();
+
     void resetExternalStylus();
     void clearStylusDataPendingFlags();
 
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 17a116e..f48a645 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -49,14 +49,51 @@
 
 // --- InputReaderConfiguration ---
 
-std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewport(
-        ViewportType viewportType, const std::string& uniqueDisplayId) const {
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByUniqueId(
+        const std::string& uniqueDisplayId) const {
+    if (uniqueDisplayId.empty()) {
+        ALOGE("Empty string provided to %s", __func__);
+        return std::nullopt;
+    }
+    size_t count = 0;
+    std::optional<DisplayViewport> result = std::nullopt;
     for (const DisplayViewport& currentViewport : mDisplays) {
-        if (currentViewport.type == viewportType) {
-            if (uniqueDisplayId.empty() ||
-                    (!uniqueDisplayId.empty() && uniqueDisplayId == currentViewport.uniqueId)) {
-                return std::make_optional(currentViewport);
-            }
+        if (uniqueDisplayId == currentViewport.uniqueId) {
+            result = std::make_optional(currentViewport);
+            count++;
+        }
+    }
+    if (count > 1) {
+        ALOGE("Found %zu viewports with uniqueId %s, but expected 1 at most",
+            count, uniqueDisplayId.c_str());
+    }
+    return result;
+}
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByType(ViewportType type)
+        const {
+    size_t count = 0;
+    std::optional<DisplayViewport> result = std::nullopt;
+    for (const DisplayViewport& currentViewport : mDisplays) {
+        // Return the first match
+        if (currentViewport.type == type && !result) {
+            result = std::make_optional(currentViewport);
+            count++;
+        }
+    }
+    if (count > 1) {
+        ALOGE("Found %zu viewports with type %s, but expected 1 at most",
+                count, viewportTypeToString(type));
+    }
+    return result;
+}
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByPort(
+        uint8_t displayPort) const {
+    for (const DisplayViewport& currentViewport : mDisplays) {
+        const std::optional<uint8_t>& physicalPort = currentViewport.physicalPort;
+        if (physicalPort && (*physicalPort == displayPort)) {
+            return std::make_optional(currentViewport);
         }
     }
     return std::nullopt;
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 0e48f24..cbe0190 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -62,6 +62,4 @@
     static_libs: [
         "libarect",
     ],
-
-    init_rc: ["inputflinger.rc"],
 }
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 15ca7b3..82ff089 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -40,6 +40,8 @@
 
     virtual status_t dump(int fd, const Vector<String16>& args);
     void setInputWindows(const Vector<InputWindowInfo>&) {}
+    void registerInputChannel(const sp<InputChannel>&) {}
+    void unregisterInputChannel(const sp<InputChannel>&) {}
 
 private:
     virtual ~InputFlinger();
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index fff8480..fe1c50b 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -32,6 +32,7 @@
 #include <optional>
 #include <stddef.h>
 #include <unistd.h>
+#include <unordered_map>
 #include <vector>
 
 // Maximum supported size of a vibration pattern.
@@ -164,6 +165,10 @@
     // Devices with these names will be ignored.
     std::vector<std::string> excludedDeviceNames;
 
+    // The associations between input ports and display ports.
+    // Used to determine which DisplayViewport should be tied to which InputDevice.
+    std::unordered_map<std::string, uint8_t> portAssociations;
+
     // Velocity control parameters for mouse pointer movements.
     VelocityControlParameters pointerVelocityControlParameters;
 
@@ -262,8 +267,10 @@
             pointerGestureZoomSpeedRatio(0.3f),
             showTouches(false) { }
 
-    std::optional<DisplayViewport> getDisplayViewport(ViewportType viewportType,
-            const std::string& uniqueDisplayId) const;
+    std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
+    std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
+            const;
+    std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t physicalPort) const;
     void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
 
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e860db5..26f01b7 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -63,6 +63,9 @@
     virtual void notifyInputChannelBroken(const sp<IBinder>&) {
     }
 
+    virtual void notifyFocusChanged(const sp<IBinder>&) {
+    }
+
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
         *outConfig = mConfig;
     }
@@ -338,7 +341,6 @@
             const std::string name, int32_t displayId) :
                 mDispatcher(dispatcher), mName(name), mDisplayId(displayId) {
             InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
-
             mConsumer = new InputConsumer(mClientChannel);
         }
 
@@ -352,6 +354,7 @@
 
         sp<InputDispatcher> mDispatcher;
         sp<InputChannel> mServerChannel, mClientChannel;
+        sp<IBinder> mToken;
         InputConsumer *mConsumer;
         PreallocatedInputEventFactory mEventFactory;
 
@@ -366,15 +369,17 @@
 
     FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
         const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
-            InputWindowHandle(inputApplicationHandle),
             FakeInputReceiver(dispatcher, name, displayId),
             mFocused(false) {
             mServerChannel->setToken(new BBinder());
             mDispatcher->registerInputChannel(mServerChannel, displayId);
+ 
+            inputApplicationHandle->updateInfo();
+            mInfo.applicationInfo = *inputApplicationHandle->getInfo();
     }
 
     virtual bool updateInfo() {
-        mInfo.inputChannel = mServerChannel;
+        mInfo.token = mServerChannel->getToken();
         mInfo.name = mName;
         mInfo.layoutParamsFlags = 0;
         mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
@@ -383,7 +388,7 @@
         mInfo.frameTop = 0;
         mInfo.frameRight = WIDTH;
         mInfo.frameBottom = HEIGHT;
-        mInfo.scaleFactor = 1.0;
+        mInfo.globalScaleFactor = 1.0;
         mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
         mInfo.visible = true;
         mInfo.canReceiveKeys = true;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 929424b..04b87d5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -28,12 +28,14 @@
 
 // Arbitrary display properties.
 static const int32_t DISPLAY_ID = 0;
+static const int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
 static const int32_t DISPLAY_WIDTH = 480;
 static const int32_t DISPLAY_HEIGHT = 800;
 static const int32_t VIRTUAL_DISPLAY_ID = 1;
 static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
 static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
 static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
+static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
 
 // Error tolerance for floating point assertions.
 static const float EPSILON = 0.001f;
@@ -147,15 +149,22 @@
         mConfig.setDisplayViewports(mViewports);
     }
 
-    std::optional<DisplayViewport> getDisplayViewport(ViewportType viewportType,
-            const std::string& uniqueId) {
-        return mConfig.getDisplayViewport(viewportType, uniqueId);
+    std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const {
+        return mConfig.getDisplayViewportByUniqueId(uniqueId);
+    }
+    std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const {
+        return mConfig.getDisplayViewportByType(type);
+    }
+
+    std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const {
+        return mConfig.getDisplayViewportByPort(displayPort);
     }
 
     void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
-            const std::string& uniqueId, ViewportType viewportType) {
+            const std::string& uniqueId, std::optional<uint8_t> physicalPort,
+            ViewportType viewportType) {
         const DisplayViewport viewport = createDisplayViewport(displayId, width, height,
-                orientation, uniqueId, viewportType);
+                orientation, uniqueId, physicalPort, viewportType);
         mViewports.push_back(viewport);
         mConfig.setDisplayViewports(mViewports);
     }
@@ -164,6 +173,10 @@
         mConfig.excludedDeviceNames.push_back(deviceName);
     }
 
+    void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) {
+        mConfig.portAssociations.insert({inputPort, displayPort});
+    }
+
     void addDisabledDevice(int32_t deviceId) {
         ssize_t index = mConfig.disabledDevices.indexOf(deviceId);
         bool currentlyEnabled = index < 0;
@@ -207,7 +220,8 @@
 
 private:
     DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
-            int32_t orientation, const std::string& uniqueId, ViewportType type) {
+            int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
+            ViewportType type) {
         bool isRotated = (orientation == DISPLAY_ORIENTATION_90
                 || orientation == DISPLAY_ORIENTATION_270);
         DisplayViewport v;
@@ -224,6 +238,7 @@
         v.deviceWidth = isRotated ? height : width;
         v.deviceHeight = isRotated ? width : height;
         v.uniqueId = uniqueId;
+        v.physicalPort = physicalPort;
         v.type = type;
         return v;
     }
@@ -1105,26 +1120,28 @@
 
     // We didn't add any viewports yet, so there shouldn't be any.
     std::optional<DisplayViewport> internalViewport =
-            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
     ASSERT_FALSE(internalViewport);
 
     // Add an internal viewport, then clear it
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, uniqueId, ViewportType::VIEWPORT_INTERNAL);
+            DISPLAY_ORIENTATION_0, uniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
 
     // Check matching by uniqueId
-    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+    internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
     ASSERT_TRUE(internalViewport);
+    ASSERT_EQ(ViewportType::VIEWPORT_INTERNAL, internalViewport->type);
 
     // Check matching by viewport type
-    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+    internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
     ASSERT_TRUE(internalViewport);
+    ASSERT_EQ(uniqueId, internalViewport->uniqueId);
 
     mFakePolicy->clearViewports();
     // Make sure nothing is found after clear
-    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+    internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
     ASSERT_FALSE(internalViewport);
-    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+    internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
     ASSERT_FALSE(internalViewport);
 }
 
@@ -1138,40 +1155,42 @@
 
     // Add an internal viewport
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, internalUniqueId, ViewportType::VIEWPORT_INTERNAL);
+            DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
     // Add an external viewport
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, externalUniqueId, ViewportType::VIEWPORT_EXTERNAL);
+            DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, ViewportType::VIEWPORT_EXTERNAL);
     // Add an virtual viewport
     mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, virtualUniqueId1, ViewportType::VIEWPORT_VIRTUAL);
+            DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
     // Add another virtual viewport
     mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, virtualUniqueId2, ViewportType::VIEWPORT_VIRTUAL);
+            DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
 
     // Check matching by type for internal
     std::optional<DisplayViewport> internalViewport =
-            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
     ASSERT_TRUE(internalViewport);
     ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
 
     // Check matching by type for external
     std::optional<DisplayViewport> externalViewport =
-            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, "");
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_EXTERNAL);
     ASSERT_TRUE(externalViewport);
     ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
 
     // Check matching by uniqueId for virtual viewport #1
     std::optional<DisplayViewport> virtualViewport1 =
-            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_VIRTUAL, virtualUniqueId1);
+            mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1);
     ASSERT_TRUE(virtualViewport1);
+    ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport1->type);
     ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
     ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
 
     // Check matching by uniqueId for virtual viewport #2
     std::optional<DisplayViewport> virtualViewport2 =
-            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_VIRTUAL, virtualUniqueId2);
+            mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2);
     ASSERT_TRUE(virtualViewport2);
+    ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport2->type);
     ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
     ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
 }
@@ -1194,18 +1213,20 @@
         mFakePolicy->clearViewports();
         // Add a viewport
         mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, uniqueId1, type);
+            DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT, type);
         // Add another viewport
         mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, uniqueId2, type);
+            DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT, type);
 
         // Check that correct display viewport was returned by comparing the display IDs.
-        std::optional<DisplayViewport> viewport1 = mFakePolicy->getDisplayViewport(type, uniqueId1);
+        std::optional<DisplayViewport> viewport1 =
+                mFakePolicy->getDisplayViewportByUniqueId(uniqueId1);
         ASSERT_TRUE(viewport1);
         ASSERT_EQ(displayId1, viewport1->displayId);
         ASSERT_EQ(type, viewport1->type);
 
-        std::optional<DisplayViewport> viewport2 = mFakePolicy->getDisplayViewport(type, uniqueId2);
+        std::optional<DisplayViewport> viewport2 =
+                mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
         ASSERT_TRUE(viewport2);
         ASSERT_EQ(displayId2, viewport2->displayId);
         ASSERT_EQ(type, viewport2->type);
@@ -1216,11 +1237,50 @@
         // is just implementation detail.
         // However, we can check that it still returns *a* viewport, we just cannot assert
         // which one specifically is returned.
-        std::optional<DisplayViewport> someViewport = mFakePolicy->getDisplayViewport(type, "");
+        std::optional<DisplayViewport> someViewport = mFakePolicy->getDisplayViewportByType(type);
         ASSERT_TRUE(someViewport);
     }
 }
 
+/**
+ * Check getDisplayViewportByPort
+ */
+TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
+    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+    const std::string uniqueId1 = "uniqueId1";
+    const std::string uniqueId2 = "uniqueId2";
+    constexpr int32_t displayId1 = 1;
+    constexpr int32_t displayId2 = 2;
+    const uint8_t hdmi1 = 0;
+    const uint8_t hdmi2 = 1;
+    const uint8_t hdmi3 = 2;
+
+    mFakePolicy->clearViewports();
+    // Add a viewport that's associated with some display port that's not of interest.
+    mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId1, hdmi3, type);
+    // Add another viewport, connected to HDMI1 port
+    mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId2, hdmi1, type);
+
+    // Check that correct display viewport was returned by comparing the display ports.
+    std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
+    ASSERT_TRUE(hdmi1Viewport);
+    ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
+    ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+
+    // Check that we can still get the same viewport using the uniqueId
+    hdmi1Viewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
+    ASSERT_TRUE(hdmi1Viewport);
+    ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
+    ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+    ASSERT_EQ(type, hdmi1Viewport->type);
+
+    // Check that we cannot find a port with "HDMI2", because we never added one
+    std::optional<DisplayViewport> hdmi2Viewport = mFakePolicy->getDisplayViewportByPort(hdmi2);
+    ASSERT_FALSE(hdmi2Viewport);
+}
+
 // --- InputReaderTest ---
 
 class InputReaderTest : public testing::Test {
@@ -1696,6 +1756,7 @@
 class InputMapperTest : public testing::Test {
 protected:
     static const char* DEVICE_NAME;
+    static const char* DEVICE_LOCATION;
     static const int32_t DEVICE_ID;
     static const int32_t DEVICE_GENERATION;
     static const int32_t DEVICE_CONTROLLER_NUMBER;
@@ -1714,10 +1775,11 @@
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
+        identifier.location = DEVICE_LOCATION;
         mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
                 DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
 
-        mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
+        mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
     }
 
     virtual void TearDown() {
@@ -1729,7 +1791,7 @@
     }
 
     void addConfigurationProperty(const char* key, const char* value) {
-        mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8(key), String8(value));
+        mFakeEventHub->addConfigurationProperty(mDevice->getId(), String8(key), String8(value));
     }
 
     void configureDevice(uint32_t changes) {
@@ -1743,9 +1805,10 @@
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
-            int32_t orientation, const std::string& uniqueId, ViewportType viewportType) {
+            int32_t orientation, const std::string& uniqueId,
+            std::optional<uint8_t> physicalPort, ViewportType viewportType) {
         mFakePolicy->addDisplayViewport(
-                displayId, width, height, orientation, uniqueId, viewportType);
+                displayId, width, height, orientation, uniqueId, physicalPort, viewportType);
         configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     }
 
@@ -1801,6 +1864,7 @@
 };
 
 const char* InputMapperTest::DEVICE_NAME = "device";
+const char* InputMapperTest::DEVICE_LOCATION = "USB1";
 const int32_t InputMapperTest::DEVICE_ID = 1;
 const int32_t InputMapperTest::DEVICE_GENERATION = 2;
 const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
@@ -1867,7 +1931,7 @@
  */
 void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            orientation, UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+            orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
 }
 
 void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
@@ -2170,7 +2234,7 @@
     // ^--- already checked by the previous test
 
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
-            UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+            UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2180,7 +2244,7 @@
     constexpr int32_t newDisplayId = 2;
     clearViewports();
     setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
-            UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+            UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2305,11 +2369,18 @@
         InputMapperTest::SetUp();
 
         mFakePointerController = new FakePointerController();
-        mFakePolicy->setPointerController(DEVICE_ID, mFakePointerController);
+        mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
     }
 
     void testMotionRotation(CursorInputMapper* mapper,
             int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
+
+    void prepareDisplay(int32_t orientation) {
+        const std::string uniqueId = "local:0";
+        const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                orientation, uniqueId, NO_PORT, viewportType);
+    }
 };
 
 const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
@@ -2599,12 +2670,7 @@
     addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
-    const std::string uniqueId = "local:0";
-    const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
-
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_90, uniqueId, viewportType);
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2621,11 +2687,7 @@
     addConfigurationProperty("cursor.orientationAware", "1");
     addMapperAndConfigure(mapper);
 
-    const std::string uniqueId = "local:0";
-    const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
-
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, uniqueId, viewportType);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2635,8 +2697,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90, uniqueId, viewportType);
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0, -1));
@@ -2646,8 +2707,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1,  1));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180, uniqueId, viewportType);
+    prepareDisplay(DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0, -1,  0));
@@ -2657,8 +2717,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270, uniqueId, viewportType);
+    prepareDisplay(DISPLAY_ORIENTATION_270);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0,  1));
@@ -3104,6 +3163,7 @@
     static const VirtualKeyDefinition VIRTUAL_KEYS[2];
 
     const std::string UNIQUE_ID = "local:0";
+    const std::string SECONDARY_UNIQUE_ID = "local:1";
 
     enum Axes {
         POSITION = 1 << 0,
@@ -3119,7 +3179,8 @@
         TOOL_TYPE = 1 << 10,
     };
 
-    void prepareDisplay(int32_t orientation);
+    void prepareDisplay(int32_t orientation, std::optional<uint8_t> port = NO_PORT);
+    void prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port = NO_PORT);
     void prepareVirtualDisplay(int32_t orientation);
     void prepareVirtualKeys();
     void prepareLocationCalibration();
@@ -3172,15 +3233,20 @@
         { KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
 };
 
-void TouchInputMapperTest::prepareDisplay(int32_t orientation) {
+void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
-            UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+            UNIQUE_ID, port, ViewportType::VIEWPORT_INTERNAL);
+}
+
+void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
+    setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, SECONDARY_UNIQUE_ID, port, type);
 }
 
 void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
     setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
         VIRTUAL_DISPLAY_HEIGHT, orientation,
-        VIRTUAL_DISPLAY_UNIQUE_ID, ViewportType::VIEWPORT_VIRTUAL);
+        VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
 }
 
 void TouchInputMapperTest::prepareVirtualKeys() {
@@ -6138,5 +6204,46 @@
     ASSERT_EQ(0U, args.deviceTimestamp);
 }
 
+/**
+ * Set the input device port <--> display port associations, and check that the
+ * events are routed to the display that matches the display port.
+ * This can be checked by looking at the displayId of the resulting NotifyMotionArgs.
+ */
+TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    const std::string usb2 = "USB2";
+    const uint8_t hdmi1 = 0;
+    const uint8_t hdmi2 = 1;
+    const std::string secondaryUniqueId = "uniqueId2";
+    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+    mFakePolicy->addInputPortAssociation(usb2, hdmi2);
+
+    // We are intentionally not adding the viewport for display 1 yet. Since the port association
+    // for this input device is specified, and the matching viewport is not present,
+    // the input device should be disabled (at the mapper level).
+
+    // Add viewport for display 2 on hdmi2
+    prepareSecondaryDisplay(type, hdmi2);
+    // Send a touch event
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // Add viewport for display 1 on hdmi1
+    prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+    // Send a touch event again
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(DISPLAY_ID, args.displayId);
+}
 
 } // namespace android
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/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 0fb4ac6..8dc80cc 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -315,32 +315,7 @@
     if (mCacheSize != 0) {
         // There are some events in the cache which need to be sent first. Copy this buffer to
         // the end of cache.
-        if (mCacheSize + count <= mMaxCacheSize) {
-            memcpy(&mEventCache[mCacheSize], scratch, count * sizeof(sensors_event_t));
-            mCacheSize += count;
-        } else {
-            // Check if any new sensors have registered on this connection which may have increased
-            // the max cache size that is desired.
-            if (mCacheSize + count < computeMaxCacheSizeLocked()) {
-                reAllocateCacheLocked(scratch, count);
-                return status_t(NO_ERROR);
-            }
-            // Some events need to be dropped.
-            int remaningCacheSize = mMaxCacheSize - mCacheSize;
-            if (remaningCacheSize != 0) {
-                memcpy(&mEventCache[mCacheSize], scratch,
-                                                remaningCacheSize * sizeof(sensors_event_t));
-            }
-            int numEventsDropped = count - remaningCacheSize;
-            countFlushCompleteEventsLocked(mEventCache, numEventsDropped);
-            // Drop the first "numEventsDropped" in the cache.
-            memmove(mEventCache, &mEventCache[numEventsDropped],
-                    (mCacheSize - numEventsDropped) * sizeof(sensors_event_t));
-
-            // Copy the remainingEvents in scratch buffer to the end of cache.
-            memcpy(&mEventCache[mCacheSize - numEventsDropped], scratch + remaningCacheSize,
-                                            numEventsDropped * sizeof(sensors_event_t));
-        }
+        appendEventsToCacheLocked(scratch, count);
         return status_t(NO_ERROR);
     }
 
@@ -376,8 +351,8 @@
             mEventCache = new sensors_event_t[mMaxCacheSize];
             mCacheSize = 0;
         }
-        memcpy(&mEventCache[mCacheSize], scratch, count * sizeof(sensors_event_t));
-        mCacheSize += count;
+        // Save the events so that they can be written later
+        appendEventsToCacheLocked(scratch, count);
 
         // Add this file descriptor to the looper to get a callback when this fd is available for
         // writing.
@@ -417,6 +392,51 @@
     mMaxCacheSize = new_cache_size;
 }
 
+void SensorService::SensorEventConnection::appendEventsToCacheLocked(sensors_event_t const* events,
+                                                                     int count) {
+    if (count <= 0) {
+        return;
+    } else if (mCacheSize + count <= mMaxCacheSize) {
+        // The events fit within the current cache: add them
+        memcpy(&mEventCache[mCacheSize], events, count * sizeof(sensors_event_t));
+        mCacheSize += count;
+    } else if (mCacheSize + count <= computeMaxCacheSizeLocked()) {
+        // The events fit within a resized cache: resize the cache and add the events
+        reAllocateCacheLocked(events, count);
+    } else {
+        // The events do not fit within the cache: drop the oldest events.
+        ALOGW("Dropping events from cache (%d / %d) to save %d newer events", mCacheSize,
+                mMaxCacheSize, count);
+
+        int freeSpace = mMaxCacheSize - mCacheSize;
+
+        // Drop up to the currently cached number of events to make room for new events
+        int cachedEventsToDrop = std::min(mCacheSize, count - freeSpace);
+
+        // New events need to be dropped if there are more new events than the size of the cache
+        int newEventsToDrop = std::max(0, count - mMaxCacheSize);
+
+        // Determine the number of new events to copy into the cache
+        int eventsToCopy = std::min(mMaxCacheSize, count);
+
+        // Check for any flush complete events in the events that will be dropped
+        countFlushCompleteEventsLocked(mEventCache, cachedEventsToDrop);
+        countFlushCompleteEventsLocked(events, newEventsToDrop);
+
+        // Only shift the events if they will not all be overwritten
+        if (eventsToCopy != mMaxCacheSize) {
+            memmove(mEventCache, &mEventCache[cachedEventsToDrop],
+                    (mCacheSize - cachedEventsToDrop) * sizeof(sensors_event_t));
+        }
+        mCacheSize -= cachedEventsToDrop;
+
+        // Copy the events into the cache
+        memcpy(&mEventCache[mCacheSize], &events[newEventsToDrop],
+                eventsToCopy * sizeof(sensors_event_t));
+        mCacheSize += eventsToCopy;
+    }
+}
+
 void SensorService::SensorEventConnection::sendPendingFlushEventsLocked() {
     ASensorEvent flushCompleteEvent;
     memset(&flushCompleteEvent, 0, sizeof(flushCompleteEvent));
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 40c21ff..eefd81a 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -108,6 +108,10 @@
     // size, reallocate memory and copy over events from the older cache.
     void reAllocateCacheLocked(sensors_event_t const* scratch, int count);
 
+    // Add the events to the cache. If the cache would be exceeded, drop events at the beginning of
+    // the cache.
+    void appendEventsToCacheLocked(sensors_event_t const* events, int count);
+
     // LooperCallback method. If there is data to read on this fd, it is an ack from the app that it
     // has read events from a wake up sensor, decrement mWakeLockRefCount.  If this fd is available
     // for writing send the data from the cache.
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 088c256..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 {
@@ -54,6 +53,7 @@
         "libsync",
         "libtimestats_proto",
         "libui",
+        "libinput",
         "libutils",
     ],
     static_libs: [
@@ -142,6 +142,7 @@
         "Scheduler/LayerHistory.cpp",
         "Scheduler/MessageQueue.cpp",
         "Scheduler/Scheduler.cpp",
+        "Scheduler/SchedulerUtils.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceInterceptor.cpp",
@@ -183,6 +184,7 @@
         "libdisplayservicehidl",
         "libhidlbase",
         "libhidltransport",
+        "libinput",
         "liblayers_proto",
         "liblog",
         "libsync",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index ade62bf..9b1c0db 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -24,6 +24,8 @@
 #include "DisplayDevice.h"
 #include "LayerRejecter.h"
 
+#include "TimeStats/TimeStats.h"
+
 #include <renderengine/RenderEngine.h>
 
 #include <gui/BufferItem.h>
@@ -68,10 +70,10 @@
         ALOGE("Found stale hardware composer layers when destroying "
               "surface flinger layer %s",
               mName.string());
-        destroyAllHwcLayers();
+        destroyAllHwcLayersPlusChildren();
     }
 
-    mTimeStats.onDestroy(getSequence());
+    mFlinger->mTimeStats->onDestroy(getSequence());
 }
 
 void BufferLayer::useSurfaceDamage() {
@@ -334,7 +336,7 @@
     mFrameTracker.setDesiredPresentTime(desiredPresentTime);
 
     const int32_t layerID = getSequence();
-    mTimeStats.setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
+    mFlinger->mTimeStats->setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
 
     std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime();
     if (frameReadyFence->isValid()) {
@@ -346,13 +348,13 @@
     }
 
     if (presentFence->isValid()) {
-        mTimeStats.setPresentFence(layerID, mCurrentFrameNumber, presentFence);
+        mFlinger->mTimeStats->setPresentFence(layerID, mCurrentFrameNumber, presentFence);
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
     } else if (displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
         // The HWC doesn't support present fences, so use the refresh
         // timestamp instead.
         const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
-        mTimeStats.setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
+        mFlinger->mTimeStats->setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
         mFrameTracker.setActualPresentTime(actualPresentTime);
     }
 
@@ -501,7 +503,7 @@
 
     // FIXME: postedRegion should be dirty & bounds
     // transform the dirty region to window-manager space
-    return getTransform().transform(Region(Rect(getActiveWidth(s), getActiveHeight(s))));
+    return getTransform().transform(Region(getBufferSize(s)));
 }
 
 // transaction
@@ -624,11 +626,13 @@
 
     ui::Transform t = getTransform();
     Rect win = bounds;
+    const int bufferWidth = getBufferSize(s).getWidth();
+    const int bufferHeight = getBufferSize(s).getHeight();
 
-    float left = float(win.left) / float(getActiveWidth(s));
-    float top = float(win.top) / float(getActiveHeight(s));
-    float right = float(win.right) / float(getActiveWidth(s));
-    float bottom = float(win.bottom) / float(getActiveHeight(s));
+    const float left = float(win.left) / float(bufferWidth);
+    const float top = float(win.top) / float(bufferHeight);
+    const float right = float(win.right) / float(bufferWidth);
+    const float bottom = float(win.bottom) / float(bufferHeight);
 
     // TODO: we probably want to generate the texture coords with the mesh
     // here we assume that we only have 4 vertices
@@ -639,15 +643,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/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 78ab23a..b784d11 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -17,6 +17,8 @@
 #include "BufferQueueLayer.h"
 #include "LayerRejecter.h"
 
+#include "TimeStats/TimeStats.h"
+
 #include <system/window.h>
 
 namespace android {
@@ -246,7 +248,7 @@
         // and return early
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
-            mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+            mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
             mQueueItems.removeAt(0);
             mQueuedFrames--;
         }
@@ -260,7 +262,7 @@
             Mutex::Autolock lock(mQueueItemLock);
             mQueueItems.clear();
             mQueuedFrames = 0;
-            mTimeStats.clearLayerRecord(layerID);
+            mFlinger->mTimeStats->onDestroy(layerID);
         }
 
         // Once we have hit this state, the shadow queue may no longer
@@ -281,13 +283,14 @@
         // Remove any stale buffers that have been dropped during
         // updateTexImage
         while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
-            mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+            mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
             mQueueItems.removeAt(0);
             mQueuedFrames--;
         }
 
-        mTimeStats.setAcquireFence(layerID, currentFrameNumber, mQueueItems[0].mFenceTime);
-        mTimeStats.setLatchTime(layerID, currentFrameNumber, latchTime);
+        mFlinger->mTimeStats->setAcquireFence(layerID, currentFrameNumber,
+                                              mQueueItems[0].mFenceTime);
+        mFlinger->mTimeStats->setLatchTime(layerID, currentFrameNumber, latchTime);
 
         mQueueItems.removeAt(0);
     }
@@ -352,9 +355,10 @@
 void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
     // Add this buffer from our internal queue tracker
     { // Autolock scope
-        // Report the timestamp to the Scheduler.
+        // Report the requested present time to the Scheduler.
         if (mFlinger->mUseScheduler) {
-            mFlinger->mScheduler->addNewFrameTimestamp(item.mTimestamp, item.mIsAutoTimestamp);
+            mFlinger->mScheduler->addFramePresentTimeForLayer(item.mTimestamp,
+                                                              item.mIsAutoTimestamp, mName.c_str());
         }
 
         Mutex::Autolock lock(mQueueItemLock);
@@ -382,7 +386,7 @@
 
     mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
                                              item.mGraphicBuffer->getHeight(), item.mFrameNumber);
-    
+
     // If this layer is orphaned, then we run a fake vsync pulse so that
     // dequeueBuffer doesn't block indefinitely.
     if (isRemovedFromCurrentState()) {
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 425f5c7..1f71a44 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -21,6 +21,8 @@
 
 #include "BufferStateLayer.h"
 
+#include "TimeStats/TimeStats.h"
+
 #include <private/gui/SyncFeatures.h>
 #include <renderengine/Image.h>
 
@@ -336,6 +338,9 @@
 }
 
 PixelFormat BufferStateLayer::getPixelFormat() const {
+    if (!mActiveBuffer) {
+        return PIXEL_FORMAT_NONE;
+    }
     return mActiveBuffer->format;
 }
 
@@ -454,7 +459,7 @@
         ALOGE("[%s] rejecting buffer: "
               "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
               mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h);
-        mTimeStats.removeTimeRecord(layerID, getFrameNumber());
+        mFlinger->mTimeStats->removeTimeRecord(layerID, getFrameNumber());
         return BAD_VALUE;
     }
 
@@ -466,7 +471,7 @@
     if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) {
         // TODO(alecmouri): Fail somewhere upstream if the fence is invalid.
         if (!releaseFence->isValid()) {
-            mTimeStats.clearLayerRecord(layerID);
+            mFlinger->mTimeStats->onDestroy(layerID);
             return UNKNOWN_ERROR;
         }
 
@@ -476,7 +481,7 @@
         auto currentStatus = s.acquireFence->getStatus();
         if (currentStatus == Fence::Status::Invalid) {
             ALOGE("Existing fence has invalid state");
-            mTimeStats.clearLayerRecord(layerID);
+            mFlinger->mTimeStats->onDestroy(layerID);
             return BAD_VALUE;
         }
 
@@ -484,7 +489,7 @@
         if (incomingStatus == Fence::Status::Invalid) {
             ALOGE("New fence has invalid state");
             mDrawingState.acquireFence = releaseFence;
-            mTimeStats.clearLayerRecord(layerID);
+            mFlinger->mTimeStats->onDestroy(layerID);
             return BAD_VALUE;
         }
 
@@ -500,7 +505,7 @@
                 // synchronization is broken, the best we can do is hope fences
                 // signal in order so the new fence will act like a union
                 mDrawingState.acquireFence = releaseFence;
-                mTimeStats.clearLayerRecord(layerID);
+                mFlinger->mTimeStats->onDestroy(layerID);
                 return BAD_VALUE;
             }
             mDrawingState.acquireFence = mergedFence;
@@ -523,15 +528,15 @@
         // a GL-composited layer) not at all.
         status_t err = bindTextureImage();
         if (err != NO_ERROR) {
-            mTimeStats.clearLayerRecord(layerID);
+            mFlinger->mTimeStats->onDestroy(layerID);
             return BAD_VALUE;
         }
     }
 
     // TODO(marissaw): properly support mTimeStats
-    mTimeStats.setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime);
-    mTimeStats.setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
-    mTimeStats.setLatchTime(layerID, getFrameNumber(), latchTime);
+    mFlinger->mTimeStats->setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime);
+    mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
+    mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime);
 
     return NO_ERROR;
 }
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/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 55ce434..1215bd9 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -225,7 +225,6 @@
         mNativeWindow(args.nativeWindow),
         mGraphicBuffer(nullptr),
         mDisplaySurface(args.displaySurface),
-        mSurface{std::move(args.renderSurface)},
         mDisplayInstallOrientation(args.displayInstallOrientation),
         mPageFlipCount(0),
         mIsVirtual(args.isVirtual),
@@ -247,7 +246,6 @@
 
     ALOGE_IF(!mNativeWindow, "No native window was set for display");
     ALOGE_IF(!mDisplaySurface, "No display surface was set for display");
-    ALOGE_IF(!mSurface, "No render surface was set for display");
 
     std::vector<Hdr> types = args.hdrCapabilities.getSupportedHdrTypes();
     for (Hdr hdrType : types) {
@@ -443,12 +441,6 @@
     mDisplaySurface->onFrameCommitted();
 }
 
-bool DisplayDevice::makeCurrent() const {
-    bool success = mFlinger->getRenderEngine().setCurrentSurface(*mSurface);
-    setViewportAndProjection();
-    return success;
-}
-
 void DisplayDevice::setViewportAndProjection() const {
     size_t w = mDisplayWidth;
     size_t h = mDisplayHeight;
@@ -611,12 +603,8 @@
 void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
     dirtyRegion.set(getBounds());
 
-    mSurface->setNativeWindow(nullptr);
-
     mDisplaySurface->resizeBuffers(newWidth, newHeight);
 
-    ANativeWindow* const window = mNativeWindow.get();
-    mSurface->setNativeWindow(window);
     mDisplayWidth = newWidth;
     mDisplayHeight = newHeight;
 }
@@ -732,12 +720,11 @@
     ANativeWindow* const window = mNativeWindow.get();
     result.appendFormat("+ %s\n", getDebugName().c_str());
     result.appendFormat("  layerStack=%u, (%4dx%4d), ANativeWindow=%p "
-                        "(%d:%d:%d:%d), orient=%2d (type=%08x), "
-                        "flips=%u, isSecure=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n",
+                        "format=%d, orient=%2d (type=%08x), flips=%u, isSecure=%d, "
+                        "powerMode=%d, activeConfig=%d, numLayers=%zu\n",
                         mLayerStack, mDisplayWidth, mDisplayHeight, window,
-                        mSurface->queryRedSize(), mSurface->queryGreenSize(),
-                        mSurface->queryBlueSize(), mSurface->queryAlphaSize(), mOrientation,
-                        tr.getType(), getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig,
+                        ANativeWindow_getFormat(window), mOrientation, tr.getType(),
+                        getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig,
                         mVisibleLayersSortedByZ.size());
     result.appendFormat("   v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d],"
                         "transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n",
@@ -748,9 +735,9 @@
     auto const surface = static_cast<Surface*>(window);
     ui::Dataspace dataspace = surface->getBuffersDataSpace();
     result.appendFormat("   wideColorGamut=%d, hdr10=%d, colorMode=%s, dataspace: %s (%d)\n",
-                        mHasWideColorGamut, mHasHdr10,
-                        decodeColorMode(mActiveColorMode).c_str(),
-                        dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(), dataspace);
+                        mHasWideColorGamut, mHasHdr10, decodeColorMode(mActiveColorMode).c_str(),
+                        dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(),
+                        dataspace);
 
     String8 surfaceDump;
     mDisplaySurface->dumpAsString(surfaceDump);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 560a958..eb2c5c3 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -24,12 +24,13 @@
 #include <string>
 #include <unordered_map>
 
+#include <android/native_window.h>
 #include <binder/IBinder.h>
 #include <gui/LayerState.h>
 #include <hardware/hwcomposer_defs.h>
 #include <math/mat4.h>
 #include <renderengine/RenderEngine.h>
-#include <renderengine/Surface.h>
+#include <system/window.h>
 #include <ui/GraphicTypes.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
@@ -42,8 +43,6 @@
 #include "DisplayHardware/DisplayIdentification.h"
 #include "RenderArea.h"
 
-struct ANativeWindow;
-
 namespace android {
 
 class DisplaySurface;
@@ -163,7 +162,6 @@
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
 
-    bool makeCurrent() const;
     // Acquires a new buffer for GPU composition.
     void readyNewBuffer();
     // Marks the current buffer has finished, so that it can be presented and
@@ -220,7 +218,6 @@
     // that drawing to the buffer is now complete.
     base::unique_fd mBufferReady;
 
-    std::unique_ptr<renderengine::Surface> mSurface;
     int             mDisplayWidth;
     int             mDisplayHeight;
     const int       mDisplayInstallOrientation;
@@ -340,7 +337,6 @@
     bool isSecure{false};
     sp<ANativeWindow> nativeWindow;
     sp<DisplaySurface> displaySurface;
-    std::unique_ptr<renderengine::Surface> renderSurface;
     int displayInstallOrientation{DisplayState::eOrientationDefault};
     bool hasWideColorGamut{false};
     HdrCapabilities hdrCapabilities;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index f0bccaa..5b641a2 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1023,6 +1023,50 @@
     return Error::NONE;
 }
 
+Error Composer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+                                                      Dataspace* outDataspace,
+                                                      uint8_t* outComponentMask) {
+    if (!outFormat || !outDataspace || !outComponentMask) {
+        return Error::BAD_PARAMETER;
+    }
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError;
+    mClient_2_3->getDisplayedContentSamplingAttributes(display,
+                                                       [&](const auto tmpError,
+                                                           const auto& tmpFormat,
+                                                           const auto& tmpDataspace,
+                                                           const auto& tmpComponentMask) {
+                                                           error = tmpError;
+                                                           if (error == Error::NONE) {
+                                                               *outFormat = tmpFormat;
+                                                               *outDataspace = tmpDataspace;
+                                                               *outComponentMask =
+                                                                       static_cast<uint8_t>(
+                                                                               tmpComponentMask);
+                                                           }
+                                                       });
+    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 4188352..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;
 
@@ -193,6 +189,11 @@
                                                std::vector<uint8_t>* outData) = 0;
     virtual Error setLayerColorTransform(Display display, Layer layer,
                                          const float* matrix) = 0;
+    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 {
@@ -392,6 +393,11 @@
     Error getDisplayIdentificationData(Display display, uint8_t* outPort,
                                        std::vector<uint8_t>* outData) override;
     Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+    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 393041d..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;
 }
 
@@ -538,6 +547,14 @@
     return Error::None;
 }
 
+Error Display::getDisplayedContentSamplingAttributes(PixelFormat* outFormat,
+                                                     Dataspace* outDataspace,
+                                                     uint8_t* outComponentMask) const {
+    auto intError = mComposer.getDisplayedContentSamplingAttributes(mId, outFormat, outDataspace,
+                                                                    outComponentMask);
+    return static_cast<Error>(intError);
+}
+
 Error Display::getReleaseFences(
         std::unordered_map<Layer*, sp<Fence>>* outFences) const
 {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index d274631..8c0f50c 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -237,6 +237,9 @@
     [[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const;
     [[clang::warn_unused_result]] Error getHdrCapabilities(
             android::HdrCapabilities* outCapabilities) const;
+    [[clang::warn_unused_result]] Error getDisplayedContentSamplingAttributes(
+            android::ui::PixelFormat* outFormat, android::ui::Dataspace* outDataspace,
+            uint8_t* outComponentMask) const;
     [[clang::warn_unused_result]] Error getReleaseFences(
             std::unordered_map<Layer*,
                     android::sp<android::Fence>>* outFences) const;
@@ -269,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);
@@ -295,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 0f25b52..a752a7d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -20,31 +20,12 @@
 #define LOG_TAG "HWComposer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
 #include <utils/Errors.h>
-#include <utils/misc.h>
-#include <utils/NativeHandle.h>
-#include <utils/String8.h>
-#include <utils/Thread.h>
 #include <utils/Trace.h>
-#include <utils/Vector.h>
 
 #include <ui/DebugUtils.h>
 #include <ui/GraphicBuffer.h>
 
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
-#include <android/configuration.h>
-
-#include <cutils/properties.h>
 #include <log/log.h>
 
 #include "HWComposer.h"
@@ -85,11 +66,7 @@
 
 namespace android {
 
-#define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION
-
-// ---------------------------------------------------------------------------
-
-HWComposer::HWComposer(std::unique_ptr<android::Hwc2::Composer> composer)
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
       : mHwcDevice(std::make_unique<HWC2::Device>(std::move(composer))) {}
 
 HWComposer::~HWComposer() {
@@ -118,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) {
@@ -184,30 +170,31 @@
 
     RETURN_IF_INVALID_DISPLAY(*displayId, false);
 
-    const auto& displayData = mDisplayData[*displayId];
+    auto& displayData = mDisplayData[*displayId];
     if (displayData.isVirtual) {
         LOG_DISPLAY_ERROR(*displayId, "Invalid operation on virtual display");
         return false;
     }
 
     {
-        Mutex::Autolock _l(mLock);
+        std::lock_guard lock(displayData.lastHwVsyncLock);
 
         // There have been reports of HWCs that signal several vsync events
         // with the same timestamp when turning the display off and on. This
         // is a bug in the HWC implementation, but filter the extra events
         // out here so they don't cause havoc downstream.
-        if (timestamp == mLastHwVSync[*displayId]) {
+        if (timestamp == displayData.lastHwVsync) {
             ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")",
                   to_string(*displayId).c_str(), timestamp);
             return false;
         }
 
-        mLastHwVSync[*displayId] = timestamp;
+        displayData.lastHwVsync = timestamp;
     }
 
     const auto tag = "HW_VSYNC_" + to_string(*displayId);
-    ATRACE_INT(tag.c_str(), ++mVSyncCounts[*displayId] & 1);
+    ATRACE_INT(tag.c_str(), displayData.vsyncTraceToggle);
+    displayData.vsyncTraceToggle = !displayData.vsyncTraceToggle;
 
     return true;
 }
@@ -270,13 +257,14 @@
 
 nsecs_t HWComposer::getRefreshTimestamp(DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, 0);
+    const auto& displayData = mDisplayData.at(displayId);
     // this returns the last refresh timestamp.
     // if the last one is not available, we estimate it based on
     // the refresh period and whatever closest timestamp we have.
-    Mutex::Autolock _l(mLock);
+    std::lock_guard lock(displayData.lastHwVsyncLock);
     nsecs_t now = systemTime(CLOCK_MONOTONIC);
     auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
-    return now - ((now - mLastHwVSync[displayId]) % vsyncPeriod);
+    return now - ((now - displayData.lastHwVsync) % vsyncPeriod);
 }
 
 bool HWComposer::isConnected(DisplayId displayId) const {
@@ -375,17 +363,19 @@
     // into the HWC with the lock held, and we want to make sure
     // that even if HWC blocks (which it shouldn't), it won't
     // affect other threads.
-    Mutex::Autolock _l(mVsyncLock);
-    if (enabled != displayData.vsyncEnabled) {
-        ATRACE_CALL();
-        auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
-        RETURN_IF_HWC_ERROR(error, displayId);
-
-        displayData.vsyncEnabled = enabled;
-
-        const auto tag = "HW_VSYNC_ON_" + to_string(displayId);
-        ATRACE_INT(tag.c_str(), enabled == HWC2::Vsync::Enable ? 1 : 0);
+    std::lock_guard lock(displayData.vsyncEnabledLock);
+    if (enabled == displayData.vsyncEnabled) {
+        return;
     }
+
+    ATRACE_CALL();
+    auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
+    RETURN_IF_HWC_ERROR(error, displayId);
+
+    displayData.vsyncEnabled = enabled;
+
+    const auto tag = "HW_VSYNC_ON_" + to_string(displayId);
+    ATRACE_INT(tag.c_str(), enabled == HWC2::Vsync::Enable ? 1 : 0);
 }
 
 status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot,
@@ -405,8 +395,6 @@
 
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
-    Mutex::Autolock _l(mDisplayLock);
-
     auto& displayData = mDisplayData[displayId];
     auto& hwcDisplay = displayData.hwcDisplay;
     if (!hwcDisplay->isConnected()) {
@@ -428,7 +416,7 @@
     // back to validate when there is any client layer.
     displayData.validateWasSkipped = false;
     if (!displayData.hasClientComposition) {
-        sp<android::Fence> outPresentFence;
+        sp<Fence> outPresentFence;
         uint32_t state = UINT32_MAX;
         error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
         if (error != HWC2::Error::HasChanges) {
@@ -689,7 +677,6 @@
     const auto hwcDisplayId = displayData.hwcDisplay->getId();
     mPhysicalDisplayIdMap.erase(hwcDisplayId);
     mDisplayData.erase(displayId);
-    mVSyncCounts.erase(displayId);
 
     // TODO(b/74619554): Select internal/external display from remaining displays.
     if (hwcDisplayId == mInternalHwcDisplayId) {
@@ -755,25 +742,19 @@
     return matrix;
 }
 
-// Converts a PixelFormat to a human-readable string.  Max 11 chars.
-// (Could use a table of prefab String8 objects.)
-/*
-static String8 getFormatStr(PixelFormat format) {
-    switch (format) {
-    case PIXEL_FORMAT_RGBA_8888:    return String8("RGBA_8888");
-    case PIXEL_FORMAT_RGBX_8888:    return String8("RGBx_8888");
-    case PIXEL_FORMAT_RGB_888:      return String8("RGB_888");
-    case PIXEL_FORMAT_RGB_565:      return String8("RGB_565");
-    case PIXEL_FORMAT_BGRA_8888:    return String8("BGRA_8888");
-    case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
-                                    return String8("ImplDef");
-    default:
-        String8 result;
-        result.appendFormat("? %08x", format);
-        return result;
-    }
+status_t HWComposer::getDisplayedContentSamplingAttributes(DisplayId displayId,
+                                                           ui::PixelFormat* outFormat,
+                                                           ui::Dataspace* outDataspace,
+                                                           uint8_t* outComponentMask) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error =
+            mDisplayData[displayId]
+                    .hwcDisplay->getDisplayedContentSamplingAttributes(outFormat, outDataspace,
+                                                                       outComponentMask);
+    if (error == HWC2::Error::Unsupported) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    return NO_ERROR;
 }
-*/
 
 bool HWComposer::isUsingVrComposer() const {
     return getComposer()->isUsingVrComposer();
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b78433d..0375f74 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -17,49 +17,26 @@
 #ifndef ANDROID_SF_HWCOMPOSER_H
 #define ANDROID_SF_HWCOMPOSER_H
 
-#include "HWC2.h"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/Fence.h>
-#include <ui/GraphicTypes.h>
-#include <utils/BitSet.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-#include <utils/StrongPointer.h>
-#include <utils/Thread.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
+#include <cstdint>
 #include <memory>
+#include <mutex>
 #include <optional>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
+#include <android-base/thread_annotations.h>
+#include <ui/Fence.h>
+#include <ui/GraphicTypes.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
 #include "DisplayIdentification.h"
-
-extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
-                           const struct timespec *request,
-                           struct timespec *remain);
-
-struct framebuffer_device_t;
-
-namespace HWC2 {
-    class Device;
-    class Display;
-}
+#include "HWC2.h"
 
 namespace android {
-// ---------------------------------------------------------------------------
 
-class DisplayDevice;
-class Fence;
-class FloatRect;
 class GraphicBuffer;
-class NativeHandle;
-class Region;
 class String8;
 class TestableSurfaceFlinger;
 struct CompositionInfo;
@@ -71,7 +48,7 @@
 class HWComposer
 {
 public:
-    explicit HWComposer(std::unique_ptr<android::Hwc2::Composer> composer);
+    explicit HWComposer(std::unique_ptr<Hwc2::Composer> composer);
 
     ~HWComposer();
 
@@ -82,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,
@@ -148,6 +127,11 @@
 
     mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace);
 
+    // Returns the attributes of the color sampling engine.
+    status_t getDisplayedContentSamplingAttributes(DisplayId displayId, ui::PixelFormat* outFormat,
+                                                   ui::Dataspace* outDataspace,
+                                                   uint8_t* outComponentMask);
+
     // Events handling ---------------------------------------------------------
 
     // Returns stable display ID (and display name on connection of new or previously disconnected
@@ -177,7 +161,7 @@
     // for debugging ----------------------------------------------------------
     void dump(String8& out) const;
 
-    android::Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); }
+    Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); }
 
     // TODO(b/74619554): Remove special cases for internal/external display.
     std::optional<hwc2_display_t> getInternalHwcDisplayId() const { return mInternalHwcDisplayId; }
@@ -194,8 +178,6 @@
 
     static void validateChange(HWC2::Composition from, HWC2::Composition to);
 
-    struct cb_context;
-
     struct DisplayData {
         bool isVirtual = false;
         bool hasClientComposition = false;
@@ -209,11 +191,16 @@
         mutable std::unordered_map<int32_t,
                 std::shared_ptr<const HWC2::Display::Config>> configMap;
 
-        // protected by mVsyncLock
-        HWC2::Vsync vsyncEnabled = HWC2::Vsync::Disable;
-
         bool validateWasSkipped;
         HWC2::Error presentError;
+
+        bool vsyncTraceToggle = false;
+
+        std::mutex vsyncEnabledLock;
+        HWC2::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = HWC2::Vsync::Disable;
+
+        mutable std::mutex lastHwVsyncLock;
+        nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0;
     };
 
     std::unordered_map<DisplayId, DisplayData> mDisplayData;
@@ -227,25 +214,11 @@
     std::optional<hwc2_display_t> mExternalHwcDisplayId;
     bool mHasMultiDisplaySupport = false;
 
-    // protect mDisplayData from races between prepare and dump
-    mutable Mutex mDisplayLock;
-
-    cb_context* mCBContext = nullptr;
-    std::unordered_map<DisplayId, size_t> mVSyncCounts;
-
     std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
     uint32_t mNextVirtualDisplayId = 0;
     uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()};
-
-    // protected by mLock
-    mutable Mutex mLock;
-    mutable std::unordered_map<DisplayId, nsecs_t> mLastHwVSync;
-
-    // thread-safe
-    mutable Mutex mVsyncLock;
 };
 
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
 
 #endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1e910ce..4116ea0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -52,6 +52,7 @@
 #include "SurfaceFlinger.h"
 
 #include "DisplayHardware/HWComposer.h"
+#include "TimeStats/TimeStats.h"
 
 #include <renderengine/RenderEngine.h>
 
@@ -101,6 +102,7 @@
     mCurrentState.dataspace = ui::Dataspace::UNKNOWN;
     mCurrentState.hdrMetadata.validTypes = 0;
     mCurrentState.surfaceDamageRegion.clear();
+    mCurrentState.cornerRadius = 0.0f;
     mCurrentState.api = -1;
     mCurrentState.hasColorTransform = false;
 
@@ -123,7 +125,7 @@
 
     mFrameTracker.logAndResetStats(mName);
 
-    destroyAllHwcLayers();
+    destroyAllHwcLayersPlusChildren();
 
     mFlinger->onLayerDestroyed();
 }
@@ -175,6 +177,14 @@
     }
 }
 
+void Layer::addToCurrentState() {
+    mRemovedFromCurrentState = false;
+
+    for (const auto& child : mCurrentChildren) {
+        child->addToCurrentState();
+    }
+}
+
 // ---------------------------------------------------------------------------
 // set-up
 // ---------------------------------------------------------------------------
@@ -226,17 +236,21 @@
     return true;
 }
 
-void Layer::destroyAllHwcLayers() {
+void Layer::destroyHwcLayersForAllDisplays() {
     size_t numLayers = getBE().mHwcLayers.size();
     for (size_t i = 0; i < numLayers; ++i) {
         LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.empty(), "destroyAllHwcLayers failed");
         destroyHwcLayer(getBE().mHwcLayers.begin()->first);
     }
+}
+
+void Layer::destroyAllHwcLayersPlusChildren() {
+    destroyHwcLayersForAllDisplays();
     LOG_ALWAYS_FATAL_IF(!getBE().mHwcLayers.empty(),
                         "All hardware composer layers should have been destroyed");
 
     for (const sp<Layer>& child : mDrawingChildren) {
-        child->destroyAllHwcLayers();
+        child->destroyAllHwcLayersPlusChildren();
     }
 }
 
@@ -367,30 +381,43 @@
     // layerstack space, and convert-back to layer space.
     // if there are no window scaling involved, this operation will map to full
     // pixels in the buffer.
-    // FIXME: the 3 lines below can produce slightly incorrect clipping when we have
-    // a viewport clipping and a window transform. we should use floating point to fix this.
 
-    Rect activeCrop(getActiveWidth(s), getActiveHeight(s));
-    Rect crop = getCrop(s);
-    if (!crop.isEmpty()) {
-        activeCrop.intersect(crop, &activeCrop);
-    }
-
+    FloatRect activeCropFloat = computeBounds();
     ui::Transform t = getTransform();
-    activeCrop = t.transform(activeCrop);
-    if (!activeCrop.intersect(display->getViewport(), &activeCrop)) {
+    // Transform to screen space.
+    activeCropFloat = t.transform(activeCropFloat);
+    activeCropFloat = activeCropFloat.intersect(display->getViewport().toFloatRect());
+    // Back to layer space to work with the content crop.
+    activeCropFloat = t.inverse().transform(activeCropFloat);
+    // This needs to be here as transform.transform(Rect) computes the
+    // transformed rect and then takes the bounding box of the result before
+    // returning. This means
+    // transform.inverse().transform(transform.transform(Rect)) != Rect
+    // in which case we need to make sure the final rect is clipped to the
+    // display bounds.
+    Rect activeCrop{activeCropFloat};
+    if (!activeCrop.intersect(getBufferSize(s), &activeCrop)) {
         activeCrop.clear();
     }
-
-    const auto& p = mDrawingParent.promote();
-    if (p != nullptr) {
-        auto parentCrop = p->computeInitialCrop(display);
-        activeCrop.intersect(parentCrop, &activeCrop);
-    }
-
     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.
@@ -399,24 +426,8 @@
     // In addition there is a WM-specified crop we pull from our drawing state.
     const State& s(getDrawingState());
 
-    // Screen space to make reduction to parent crop clearer.
     Rect activeCrop = computeInitialCrop(display);
-    ui::Transform t = getTransform();
-    // Back to layer space to work with the content crop.
-    activeCrop = t.inverse().transform(activeCrop);
-
-    // This needs to be here as transform.transform(Rect) computes the
-    // transformed rect and then takes the bounding box of the result before
-    // returning. This means
-    // transform.inverse().transform(transform.transform(Rect)) != Rect
-    // in which case we need to make sure the final rect is clipped to the
-    // display bounds.
-    if (!activeCrop.intersect(Rect(getActiveWidth(s), getActiveHeight(s)), &activeCrop)) {
-        activeCrop.clear();
-    }
-
-    // subtract the transparent region and snap to the bounds
-    activeCrop = reduce(activeCrop, getActiveTransparentRegion(s));
+    Rect bufferSize = getBufferSize(s);
 
     // Transform the window crop to match the buffer coordinate system,
     // which means using the inverse of the current transform set on the
@@ -437,8 +448,8 @@
                         ui::Transform(invTransform)).getOrientation();
     }
 
-    int winWidth = getActiveWidth(s);
-    int winHeight = getActiveHeight(s);
+    int winWidth = bufferSize.getWidth();
+    int winHeight = bufferSize.getHeight();
     if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
         // If the activeCrop has been rotate the ends are rotated but not
         // the space itself so when transforming ends back we can't rely on
@@ -450,10 +461,10 @@
         if (is_h_flipped == is_v_flipped) {
             invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H;
         }
-        winWidth = getActiveHeight(s);
-        winHeight = getActiveWidth(s);
+        std::swap(winWidth, winHeight);
     }
-    const Rect winCrop = activeCrop.transform(invTransform, getActiveWidth(s), getActiveHeight(s));
+    const Rect winCrop =
+            activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight());
 
     // below, crop is intersected with winCrop expressed in crop's coordinate space
     float xScale = crop.getWidth() / float(winWidth);
@@ -489,6 +500,8 @@
 
     // this gives us only the "orientation" component of the transform
     const State& s(getDrawingState());
+    const int bufferWidth = getBufferSize(s).getWidth();
+    const int bufferHeight = getBufferSize(s).getHeight();
     auto blendMode = HWC2::BlendMode::None;
     if (!isOpaque(s) || getAlpha() != 1.0f) {
         blendMode =
@@ -519,16 +532,15 @@
         // transform.inverse().transform(transform.transform(Rect)) != Rect
         // in which case we need to make sure the final rect is clipped to the
         // display bounds.
-        if (!activeCrop.intersect(Rect(getActiveWidth(s), getActiveHeight(s)), &activeCrop)) {
+        if (!activeCrop.intersect(Rect(bufferWidth, bufferHeight), &activeCrop)) {
             activeCrop.clear();
         }
         // mark regions outside the crop as transparent
-        activeTransparentRegion.orSelf(Rect(0, 0, getActiveWidth(s), activeCrop.top));
-        activeTransparentRegion.orSelf(
-                Rect(0, activeCrop.bottom, getActiveWidth(s), getActiveHeight(s)));
+        activeTransparentRegion.orSelf(Rect(0, 0, bufferWidth, activeCrop.top));
+        activeTransparentRegion.orSelf(Rect(0, activeCrop.bottom, bufferWidth, bufferHeight));
         activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom));
         activeTransparentRegion.orSelf(
-                Rect(activeCrop.right, activeCrop.top, getActiveWidth(s), activeCrop.bottom));
+                Rect(activeCrop.right, activeCrop.top, bufferWidth, activeCrop.bottom));
     }
 
     // computeBounds returns a FloatRect to provide more accuracy during the
@@ -664,11 +676,7 @@
 
     // Apply the layer's transform, followed by the display's global transform
     // Here we're guaranteed that the layer's transform preserves rects
-    Rect win(getActiveWidth(s), getActiveHeight(s));
-    Rect crop = getCrop(s);
-    if (!crop.isEmpty()) {
-        win.intersect(crop, &win);
-    }
+    Rect win = getCroppedBufferSize(s);
     // Subtract the transparent region and snap to the bounds
     Rect bounds = reduce(win, getActiveTransparentRegion(s));
     Rect frame(getTransform().transform(bounds));
@@ -1021,6 +1029,10 @@
 uint32_t Layer::doTransaction(uint32_t flags) {
     ATRACE_CALL();
 
+    if (mLayerDetached) {
+        return 0;
+    }
+
     pushPendingState();
     State c = getCurrentState();
     if (!applyPendingStates(&c)) {
@@ -1053,6 +1065,11 @@
         clearSyncPoints();
     }
 
+    if (mCurrentState.inputInfoChanged) {
+        flags |= eInputInfoChanged;
+        mCurrentState.inputInfoChanged = false;
+    }
+
     // Commit the transaction
     commitTransaction(c);
     mCurrentState.callbackHandles = {};
@@ -1221,6 +1238,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;
@@ -1495,14 +1523,14 @@
 void Layer::onDisconnect() {
     Mutex::Autolock lock(mFrameEventHistoryMutex);
     mFrameEventHistory.onDisconnect();
-    mTimeStats.onDisconnect(getSequence());
+    mFlinger->mTimeStats->onDestroy(getSequence());
 }
 
 void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
                                      FrameEventHistoryDelta* outDelta) {
     if (newTimestamps) {
-        mTimeStats.setPostTime(getSequence(), newTimestamps->frameNumber, getName().c_str(),
-                               newTimestamps->postedTime);
+        mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
+                                          getName().c_str(), newTimestamps->postedTime);
     }
 
     Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -1553,6 +1581,9 @@
         return false;
     }
 
+    if (attachChildren()) {
+        setTransactionFlags(eTransactionNeeded);
+    }
     for (const sp<Layer>& child : mCurrentChildren) {
         newParent->addChild(child);
 
@@ -1590,6 +1621,10 @@
     }
     newParent->addChild(this);
 
+    if (!newParent->isRemovedFromCurrentState()) {
+        addToCurrentState();
+    }
+
     sp<Client> client(mClientRef.promote());
     sp<Client> newParentClient(newParent->mClientRef.promote());
 
@@ -1597,6 +1632,13 @@
         client->updateParent(newParent);
     }
 
+    if (mLayerDetached) {
+        mLayerDetached = false;
+        setTransactionFlags(eTransactionNeeded);
+    }
+    if (attachChildren()) {
+        setTransactionFlags(eTransactionNeeded);
+    }
     return true;
 }
 
@@ -1605,7 +1647,7 @@
         sp<Client> parentClient = mClientRef.promote();
         sp<Client> client(child->mClientRef.promote());
         if (client != nullptr && parentClient != client) {
-            client->detachLayer(child.get());
+            child->mLayerDetached = true;
             child->detachChildren();
         }
     }
@@ -1613,6 +1655,23 @@
     return true;
 }
 
+bool Layer::attachChildren() {
+    bool changed = false;
+    for (const sp<Layer>& child : mCurrentChildren) {
+        sp<Client> parentClient = mClientRef.promote();
+        sp<Client> client(child->mClientRef.promote());
+        if (client != nullptr && parentClient != client) {
+            if (child->mLayerDetached) {
+                child->mLayerDetached = false;
+                changed = true;
+            }
+            changed |= child->attachChildren();
+        }
+    }
+
+    return changed;
+}
+
 bool Layer::setColorTransform(const mat4& matrix) {
     static const mat4 identityMatrix = mat4();
 
@@ -1888,6 +1947,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];
@@ -1897,6 +1976,13 @@
     mDrawingParent = mCurrentParent;
 }
 
+void Layer::setInputInfo(const InputWindowInfo& info) {
+    mCurrentState.inputInfo = info;
+    mCurrentState.modified = true;
+    mCurrentState.inputInfoChanged = true;
+    setTransactionFlags(eTransactionNeeded);
+}
+
 void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) {
     const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
     const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
@@ -1941,6 +2027,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);
@@ -2024,6 +2111,33 @@
     return mRemovedFromCurrentState;
 }
 
+InputWindowInfo Layer::fillInputInfo(const Rect& screenBounds) {
+    InputWindowInfo info = mDrawingState.inputInfo;
+    info.frameLeft = screenBounds.left + info.surfaceInset;
+    info.frameTop = screenBounds.top + info.surfaceInset;
+    info.frameRight = screenBounds.right - info.surfaceInset;
+    info.frameBottom = screenBounds.bottom - info.surfaceInset;
+
+    ui::Transform t = getTransform();
+    const float xScale = t.sx();
+    const float yScale = t.sy();
+    if (xScale != 1.0f || yScale != 1.0f) {
+        info.windowXScale *= 1.0f / xScale;
+        info.windowYScale *= 1.0f / yScale;
+        info.touchableRegion.scaleSelf(xScale, yScale);
+    }
+
+    info.touchableRegion = info.touchableRegion.translate(
+            screenBounds.left,
+            screenBounds.top);
+    info.visible = isVisible();
+    return info;
+}
+
+bool Layer::hasInput() const {
+    return mDrawingState.inputInfo.token != nullptr;
+}
+
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 40ebe1e..84fafc5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -22,6 +22,7 @@
 #include <gui/BufferQueue.h>
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
+#include <input/InputWindow.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/vec4.h>
 #include <renderengine/Mesh.h>
@@ -47,7 +48,6 @@
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
-#include "TimeStats/TimeStats.h"
 #include "TransactionCompletedThread.h"
 
 #include "DisplayHardware/HWComposer.h"
@@ -109,6 +109,7 @@
     enum { // flags for doTransaction()
         eDontUpdateGeometryState = 0x00000001,
         eVisibleRegion = 0x00000002,
+        eInputInfoChanged = 0x00000004
     };
 
     struct Geometry {
@@ -123,6 +124,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;
@@ -165,6 +177,10 @@
         SortedVector<wp<Layer>> zOrderRelatives;
 
         half4 color;
+        float cornerRadius;
+
+        bool inputInfoChanged;
+        InputWindowInfo inputInfo;
 
         // The fields below this point are only used by BufferStateLayer
         Geometry active;
@@ -250,6 +266,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);
@@ -263,6 +286,8 @@
     virtual void setChildrenDrawingParent(const sp<Layer>& layer);
     virtual bool reparent(const sp<IBinder>& newParentHandle);
     virtual bool detachChildren();
+    bool attachChildren();
+    bool isLayerDetached() const { return mLayerDetached; }
     virtual bool setColorTransform(const mat4& matrix);
     virtual const mat4& getColorTransform() const;
     virtual bool hasColorTransform() const;
@@ -355,7 +380,6 @@
     // to avoid grabbing the lock again to avoid deadlock
     virtual bool isCreatedFromMainThread() const { return false; }
 
-
     bool isRemovedFromCurrentState() const;
 
     void writeToProto(LayerProto* layerInfo,
@@ -485,6 +509,11 @@
      */
     void onRemovedFromCurrentState();
 
+    /*
+     * Called when the layer is added back to the current state list.
+     */
+    void addToCurrentState();
+
     // Updates the transform hint in our SurfaceFlingerConsumer to match
     // the current orientation of the display device.
     void updateTransformHint(const sp<const DisplayDevice>& display) const;
@@ -506,7 +535,8 @@
 
     bool createHwcLayer(HWComposer* hwc, DisplayId displayId);
     bool destroyHwcLayer(DisplayId displayId);
-    void destroyAllHwcLayers();
+    void destroyHwcLayersForAllDisplays();
+    void destroyAllHwcLayersPlusChildren();
 
     bool hasHwcLayer(DisplayId displayId) const { return getBE().mHwcLayers.count(displayId) > 0; }
 
@@ -561,6 +591,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);
@@ -590,6 +627,12 @@
     int32_t getZ() const;
     virtual void pushPendingState();
 
+    /**
+     * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
+     * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+     */
+    virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
+
 protected:
     // constant
     sp<SurfaceFlinger> mFlinger;
@@ -627,11 +670,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);
@@ -707,6 +754,10 @@
     bool getPremultipledAlpha() const;
 
     bool mPendingHWCDestroy{false};
+    void setInputInfo(const InputWindowInfo& info);
+
+    InputWindowInfo fillInputInfo(const Rect& screenBounds);
+    bool hasInput() const;
 
 protected:
     // -----------------------------------------------------------------------
@@ -737,8 +788,6 @@
     FenceTimeline mAcquireTimeline;
     FenceTimeline mReleaseTimeline;
 
-    TimeStats& mTimeStats = TimeStats::getInstance();
-
     // main thread
     sp<GraphicBuffer> mActiveBuffer;
     ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN;
@@ -776,6 +825,9 @@
 
     mutable LayerBE mBE;
 
+    // Can only be accessed with the SF state lock held.
+    bool mLayerDetached{false};
+
 private:
     /**
      * Returns an unsorted vector of all layers that are part of this tree.
@@ -806,12 +858,6 @@
      * bounds are constrained by its parent bounds.
      */
     Rect getCroppedBufferSize(const Layer::State& s) const;
-
-    /**
-     * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
-     * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
-     */
-    virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index e944eb9..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,12 +40,17 @@
 
 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();
 }
 
 const std::unordered_map<std::string, nsecs_t>& LayerHistory::get(size_t index) const {
-    return mElements.at(index);
+    // 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((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 1a7f9cd..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 {
 
 /*
@@ -42,19 +44,21 @@
     ~LayerHistory();
 
     // Method for inserting layers and their requested present time into the ring buffer.
-    // The elements are going to be inserted into an unordered_map at the position of
-    // mCounter.
+    // The elements are going to be inserted into an unordered_map at the position 'now'.
     void insert(const std::string layerName, nsecs_t presentTime);
     // Method for incrementing the current slot in the ring buffer. It also clears the
     // unordered_map, if it was created previously.
     void incrementCounter();
-    // Returns unordered_map at the given at index.
+    // Returns unordered_map at the given at index. The index is decremented from 'now'. For
+    // example, 0 is now, 1 is previous frame.
     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 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 4286cc9..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>
@@ -30,6 +31,7 @@
 
 #include <gui/ISurfaceComposer.h>
 #include <ui/DisplayStatInfo.h>
+#include <utils/Timers.h>
 #include <utils/Trace.h>
 
 #include "DispSync.h"
@@ -37,6 +39,7 @@
 #include "EventControlThread.h"
 #include "EventThread.h"
 #include "InjectVSyncSource.h"
+#include "SchedulerUtils.h"
 
 namespace android {
 
@@ -205,44 +208,20 @@
     mHWVsyncAvailable = makeAvailable;
 }
 
-void Scheduler::addNewFrameTimestamp(const nsecs_t newFrameTimestamp, bool isAutoTimestamp) {
-    ATRACE_INT("AutoTimestamp", isAutoTimestamp);
-    // Video does not have timestamp automatically set, so we discard timestamps that are
-    // coming in from other sources for now.
-    if (isAutoTimestamp) {
-        return;
-    }
-    int64_t differenceMs = (newFrameTimestamp - mPreviousFrameTimestamp) / 1000000;
-    mPreviousFrameTimestamp = newFrameTimestamp;
+void Scheduler::addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
+                                            const std::string layerName) {
+    // This is V1 logic. It calculates the average FPS based on the timestamp frequency
+    // regardless of which layer the timestamp came from.
+    // For now, the averages and FPS are recorded in the systrace.
+    determineTimestampAverage(isAutoTimestamp, framePresentTime);
 
-    if (differenceMs < 10 || differenceMs > 100) {
-        // Dismiss noise.
-        return;
-    }
-    ATRACE_INT("TimestampDiff", differenceMs);
-
-    mTimeDifferences[mCounter % ARRAY_SIZE] = differenceMs;
-    mCounter++;
-    nsecs_t average = calculateAverage();
-    ATRACE_INT("TimestampAverage", average);
-
-    // 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) {
-        ATRACE_INT("FPS", 60);
-    } else if (average > 31 && average < 34) {
-        ATRACE_INT("FPS", 30);
-        updateFrameSkipping(1);
-        return;
-    } else if (average > 39 && average < 42) {
-        ATRACE_INT("FPS", 24);
-    }
-    updateFrameSkipping(0);
+    // This is V2 logic. It calculates the average and median timestamp difference based on the
+    // individual layer history. The results are recorded in the systrace.
+    determineLayerTimestampStats(layerName, framePresentTime);
 }
 
-nsecs_t Scheduler::calculateAverage() const {
-    nsecs_t sum = std::accumulate(mTimeDifferences.begin(), mTimeDifferences.end(), 0);
-    return (sum / ARRAY_SIZE);
+void Scheduler::incrementFrameCounter() {
+    mLayerHistory.incrementCounter();
 }
 
 void Scheduler::updateFrameSkipping(const int64_t skipCount) {
@@ -254,4 +233,80 @@
     }
 }
 
+void Scheduler::determineLayerTimestampStats(const std::string layerName,
+                                             const nsecs_t framePresentTime) {
+    mLayerHistory.insert(layerName, framePresentTime);
+    std::vector<int64_t> differencesMs;
+
+    // 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) {
+            if (layer.first != layerName) {
+                continue;
+            }
+            int64_t differenceMs = (newestPresentTime - layer.second) / 1000000;
+            // Dismiss noise.
+            if (differenceMs > 10 && differenceMs < 60) {
+                differencesMs.push_back(differenceMs);
+            }
+            IF_ALOGV() { differencesText += (std::to_string(differenceMs) + " "); }
+            newestPresentTime = layer.second;
+        }
+    }
+    ALOGV("Layer %s timestamp intervals: %s", layerName.c_str(), differencesText.c_str());
+
+    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(), scheduler::calculate_median(&differencesMs));
+
+        const auto tagMode = "TimestampMode_" + layerName;
+        ATRACE_INT(tagMode.c_str(), scheduler::calculate_mode(differencesMs));
+    }
+}
+
+void Scheduler::determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime) {
+    ATRACE_INT("AutoTimestamp", isAutoTimestamp);
+
+    // Video does not have timestamp automatically set, so we discard timestamps that are
+    // coming in from other sources for now.
+    if (isAutoTimestamp) {
+        return;
+    }
+    int64_t differenceMs = (framePresentTime - mPreviousFrameTimestamp) / 1000000;
+    mPreviousFrameTimestamp = framePresentTime;
+
+    if (differenceMs < 10 || differenceMs > 100) {
+        // Dismiss noise.
+        return;
+    }
+    ATRACE_INT("TimestampDiff", differenceMs);
+
+    mTimeDifferences[mCounter % scheduler::ARRAY_SIZE] = differenceMs;
+    mCounter++;
+    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 (mean > 14 && mean < 18) {
+        ATRACE_INT("FPS", 60);
+    } else if (mean > 31 && mean < 34) {
+        ATRACE_INT("FPS", 30);
+        return;
+    } else if (mean > 39 && mean < 42) {
+        ATRACE_INT("FPS", 24);
+    }
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index dd1f24b..764ad00 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -26,6 +26,8 @@
 #include "EventControlThread.h"
 #include "EventThread.h"
 #include "InjectVSyncSource.h"
+#include "LayerHistory.h"
+#include "SchedulerUtils.h"
 
 namespace android {
 
@@ -103,7 +105,11 @@
     void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
     void setIgnorePresentFences(bool ignore);
     void makeHWSyncAvailable(bool makeAvailable);
-    void addNewFrameTimestamp(const nsecs_t newFrameTimestamp, bool isAutoTimestamp);
+    // Adds the present time for given layer to the history of present times.
+    void addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
+                                     const std::string layerName);
+    // Increments counter in the layer history to indicate that SF has started a new frame.
+    void incrementFrameCounter();
 
 protected:
     virtual std::unique_ptr<EventThread> makeEventThread(
@@ -114,6 +120,12 @@
 private:
     nsecs_t calculateAverage() const;
     void updateFrameSkipping(const int64_t skipCount);
+    // 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);
+    // 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);
 
     // TODO(b/113612090): Instead of letting BufferQueueLayer to access mDispSync directly, it
     // should make request to Scheduler to compute next refresh.
@@ -147,9 +159,10 @@
     // 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;
 };
 
 } // namespace android
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 7150660..a8de576 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -37,6 +37,8 @@
 
 #include <dvr/vr_flinger.h>
 
+#include <input/IInputFlinger.h>
+
 #include <ui/ColorSpace.h>
 #include <ui/DebugUtils.h>
 #include <ui/DisplayInfo.h>
@@ -93,6 +95,7 @@
 #include "Scheduler/InjectVSyncSource.h"
 #include "Scheduler/MessageQueue.h"
 #include "Scheduler/Scheduler.h"
+#include "TimeStats/TimeStats.h"
 
 #include <cutils/compiler.h>
 
@@ -261,6 +264,7 @@
         mDebugInTransaction(0),
         mLastTransactionTime(0),
         mForceFullDamage(false),
+        mTimeStats(factory.createTimeStats()),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
         mRefreshStartTime(0),
@@ -554,6 +558,13 @@
     if (window != 0) {
         window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
     }
+    sp<IBinder> input(defaultServiceManager()->getService(
+            String16("inputflinger")));
+    if (input == nullptr) {
+        ALOGE("Failed to link to input service");
+    } else {
+        mInputFlinger = interface_cast<IInputFlinger>(input);
+    }
 
     if (mVrFlinger) {
       mVrFlinger->OnBootFinished();
@@ -1105,6 +1116,23 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
+                                                               ui::PixelFormat* outFormat,
+                                                               ui::Dataspace* outDataspace,
+                                                               uint8_t* outComponentMask) const {
+    if (!outFormat || !outDataspace || !outComponentMask) {
+        return BAD_VALUE;
+    }
+    Mutex::Autolock _l(mStateLock);
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display || !display->getId()) {
+        ALOGE("getDisplayedContentSamplingAttributes: Bad display token: %p", display.get());
+        return BAD_VALUE;
+    }
+    return getHwComposer().getDisplayedContentSamplingAttributes(*display->getId(), outFormat,
+                                                                 outDataspace, outComponentMask);
+}
+
 status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
     postMessageSync(new LambdaMessage([&] {
         Mutex::Autolock _l(mStateLock);
@@ -1489,6 +1517,10 @@
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
+            if (mUseScheduler) {
+                // This call is made each time SF wakes up and creates a new frame.
+                mScheduler->incrementFrameCounter();
+            }
             bool frameMissed = !mHadClientComposition &&
                     mPreviousPresentFence != Fence::NO_FENCE &&
                     (mPreviousPresentFence->getSignalTime() ==
@@ -1496,7 +1528,7 @@
             mFrameMissedCount += frameMissed;
             ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
             if (frameMissed) {
-                mTimeStats.incrementMissedFrames();
+                mTimeStats->incrementMissedFrames();
                 if (mPropagateBackpressure) {
                     signalLayerUpdate();
                     break;
@@ -1657,6 +1689,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);
@@ -1679,8 +1715,6 @@
     mDrawingState.colorMatrixChanged = false;
 
     for (const auto& [token, display] : mDisplays) {
-        getBE().mCompositionInfo[token].clear();
-
         for (auto& layer : display->getVisibleLayersSortedByZ()) {
             const auto displayId = display->getId();
             layer->getBE().compositionInfo.compositionType = layer->getCompositionType(displayId);
@@ -1919,12 +1953,12 @@
         mAnimFrameTracker.advanceFrame();
     }
 
-    mTimeStats.incrementTotalFrames();
+    mTimeStats->incrementTotalFrames();
     if (mHadClientComposition) {
-        mTimeStats.incrementClientCompositionFrames();
+        mTimeStats->incrementClientCompositionFrames();
     }
 
-    mTimeStats.setPresentFenceGlobal(presentFenceTime);
+    mTimeStats->setPresentFenceGlobal(presentFenceTime);
 
     if (display && getHwComposer().isConnected(*display->getId()) && !display->isPoweredOn()) {
         return;
@@ -1964,6 +1998,17 @@
     ATRACE_CALL();
     ALOGV("rebuildLayerStacks");
 
+    // We need to clear these out now as these may be holding on to a
+    // HWC2::Layer reference at the same time as the LayerBE::HWCInfo structure
+    // also holds a reference. When the set of visible layers is recomputed,
+    // some layers may be destroyed if the only thing keeping them alive was
+    // that list of visible layers associated with each display. The layer
+    // destruction code asserts that the HWC2::Layer is properly destroyed, but
+    // that doesn't happen if SurfaceFlingerBE::mCompositionInfo keeps it alive.
+    for (const auto& [token, display] : mDisplays) {
+        getBE().mCompositionInfo[token].clear();
+    }
+
     // rebuild the visible layer list per screen
     if (CC_UNLIKELY(mVisibleRegionsDirty)) {
         ATRACE_NAME("rebuildLayerStacks VR Dirty");
@@ -2337,14 +2382,6 @@
     auto nativeWindow = nativeWindowSurface->getNativeWindow();
     creationArgs.nativeWindow = nativeWindow;
 
-    /*
-     * Create our display's surface
-     */
-    std::unique_ptr<renderengine::Surface> renderSurface = getRenderEngine().createSurface();
-    renderSurface->setCritical(isInternalDisplay);
-    renderSurface->setAsync(state.isVirtual());
-    creationArgs.renderSurface = std::move(renderSurface);
-
     // Make sure that composition can never be stalled by a virtual display
     // consumer that isn't processing buffers fast enough. We have to do this
     // here, in case the display is composed entirely by HWC.
@@ -2568,6 +2605,7 @@
      * (perform the transaction for each of them if needed)
      */
 
+    bool inputChanged = false;
     if (transactionFlags & eTraversalNeeded) {
         mCurrentState.traverseInZOrder([&](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
@@ -2576,6 +2614,10 @@
             const uint32_t flags = layer->doTransaction(0);
             if (flags & Layer::eVisibleRegion)
                 mVisibleRegionsDirty = true;
+
+            if (flags & Layer::eInputInfoChanged) {
+                inputChanged = true;
+            }
         });
     }
 
@@ -2685,9 +2727,26 @@
 
     commitTransaction();
 
+    if ((inputChanged || mVisibleRegionsDirty) && mInputFlinger) {
+        updateInputWindows();
+    }
+
     updateCursorAsync();
 }
 
+void SurfaceFlinger::updateInputWindows() {
+    ATRACE_CALL();
+
+    Vector<InputWindowInfo> inputHandles;
+
+    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
+        if (layer->hasInput()) {
+            inputHandles.add(layer->fillInputInfo(layer->computeScreenBounds()));
+        }
+    });
+    mInputFlinger->setInputWindows(inputHandles);
+}
+
 void SurfaceFlinger::updateCursorAsync()
 {
     for (const auto& [token, display] : mDisplays) {
@@ -2701,6 +2760,17 @@
     }
 }
 
+void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
+    if (layer->hasReadyFrame()) {
+        const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+        if (layer->shouldPresentNow(expectedPresentTime)) {
+            bool ignored = false;
+            layer->latchBuffer(ignored, systemTime(), Fence::NO_FENCE);
+        }
+    }
+    layer->releasePendingBuffer(systemTime());
+}
+
 void SurfaceFlinger::commitTransaction()
 {
     if (!mLayersPendingRemoval.isEmpty()) {
@@ -2714,11 +2784,9 @@
             // showing at its last configured state until we eventually
             // abandon the buffer queue.
             if (l->isRemovedFromCurrentState()) {
-                l->destroyAllHwcLayers();
-                // destroyAllHwcLayers traverses to children, but releasePendingBuffer
-                // doesn't in other scenarios. So we have to traverse explicitly here.
                 l->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* child) {
-                    child->releasePendingBuffer(systemTime());
+                    child->destroyHwcLayersForAllDisplays();
+                    latchAndReleaseBuffer(child);
                 });
             }
         }
@@ -2795,6 +2863,7 @@
         if (CC_LIKELY(layer->isVisible())) {
             const bool translucent = !layer->isOpaque(s);
             Rect bounds(layer->computeScreenBounds());
+
             visibleRegion.set(bounds);
             ui::Transform tr = layer->getTransform();
             if (!visibleRegion.isEmpty()) {
@@ -2813,6 +2882,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;
@@ -3022,8 +3092,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;
@@ -3101,7 +3173,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);
@@ -3170,7 +3242,7 @@
         } else {
             if (parent->isRemovedFromCurrentState()) {
                 ALOGE("addClientLayer called with a removed parent");
-                return NAME_NOT_FOUND;
+                lbc->onRemovedFromCurrentState();
             }
             parent->addChild(lbc);
         }
@@ -3198,6 +3270,10 @@
 }
 
 status_t SurfaceFlinger::removeLayerLocked(const Mutex& lock, const sp<Layer>& layer) {
+    if (layer->isLayerDetached()) {
+        return NO_ERROR;
+    }
+
     const auto& p = layer->getParent();
     ssize_t index;
     if (p != nullptr) {
@@ -3517,6 +3593,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,
@@ -3606,7 +3686,10 @@
     if (what & layer_state_t::eSidebandStreamChanged) {
         if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
     }
-
+    if (what & layer_state_t::eInputInfoChanged) {
+        layer->setInputInfo(s.inputInfo);
+        flags |= eTraversalNeeded;
+    }
     std::vector<sp<CallbackHandle>> callbackHandles;
     if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
         mTransactionCompletedThread.run();
@@ -3663,11 +3746,24 @@
             result = createBufferStateLayer(client, uniqueName, w, h, flags, handle, &layer);
             break;
         case ISurfaceComposerClient::eFXSurfaceColor:
+            // check if buffer size is set for color layer.
+            if (w > 0 || h > 0) {
+                ALOGE("createLayer() failed, w or h cannot be set for color layer (w=%d, h=%d)",
+                      int(w), int(h));
+                return BAD_VALUE;
+            }
+
             result = createColorLayer(client,
                     uniqueName, w, h, flags,
                     handle, &layer);
             break;
         case ISurfaceComposerClient::eFXSurfaceContainer:
+            // check if buffer size is set for container layer.
+            if (w > 0 || h > 0) {
+                ALOGE("createLayer() failed, w or h cannot be set for container layer (w=%d, h=%d)",
+                      int(w), int(h));
+                return BAD_VALUE;
+            }
             result = createContainerLayer(client,
                     uniqueName, w, h, flags,
                     handle, &layer);
@@ -3962,7 +4058,7 @@
     }
 
     if (display->isPrimary()) {
-        mTimeStats.setPowerMode(mode);
+        mTimeStats->setPowerMode(mode);
     }
 
     ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
@@ -4105,7 +4201,7 @@
 
             if ((index < numArgs) && (args[index] == String16("--timestats"))) {
                 index++;
-                mTimeStats.parseArgs(asProto, args, index, result);
+                mTimeStats->parseArgs(asProto, args, index, result);
                 dumpAll = false;
             }
         }
@@ -4681,7 +4777,8 @@
         case SET_ACTIVE_CONFIG:
         case SET_ACTIVE_COLOR_MODE:
         case INJECT_VSYNC:
-        case SET_POWER_MODE: {
+        case SET_POWER_MODE:
+        case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: {
             if (!callingThreadHasUnscopedSurfaceFlingerAccess()) {
                 IPCThreadState* ipc = IPCThreadState::self();
                 ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
@@ -5117,12 +5214,14 @@
         const ui::Transform& getTransform() const override { return mTransform; }
         Rect getBounds() const override {
             const Layer::State& layerState(mLayer->getDrawingState());
-            return Rect(mLayer->getActiveWidth(layerState), mLayer->getActiveHeight(layerState));
+            return mLayer->getBufferSize(layerState);
         }
         int getHeight() const override {
-            return mLayer->getActiveHeight(mLayer->getDrawingState());
+            return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
         }
-        int getWidth() const override { return mLayer->getActiveWidth(mLayer->getDrawingState()); }
+        int getWidth() const override {
+            return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
+        }
         bool isSecure() const override { return false; }
         bool needsFiltering() const override { return mNeedsFiltering; }
         Rect getSourceCrop() const override {
@@ -5196,12 +5295,12 @@
     Rect crop(sourceCrop);
     if (sourceCrop.width() <= 0) {
         crop.left = 0;
-        crop.right = parent->getActiveWidth(parent->getCurrentState());
+        crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
     }
 
     if (sourceCrop.height() <= 0) {
         crop.top = 0;
-        crop.bottom = parent->getActiveHeight(parent->getCurrentState());
+        crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
     }
 
     int32_t reqWidth = crop.width() * frameScale;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f98dddf..9039a14 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -72,7 +72,6 @@
 #include "Scheduler/MessageQueue.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncModulator.h"
-#include "TimeStats/TimeStats.h"
 
 #include <map>
 #include <mutex>
@@ -98,10 +97,12 @@
 class EventThread;
 class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
+class IInputFlinger;
 class InjectVSyncSource;
 class Layer;
 class Surface;
 class SurfaceFlingerBE;
+class TimeStats;
 class VSyncSource;
 struct CompositionInfo;
 
@@ -479,6 +480,9 @@
     status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
                                       ui::Dataspace* outWideColorGamutDataspace,
                                       ui::PixelFormat* outWideColorGamutPixelFormat) const override;
+    virtual status_t getDisplayedContentSamplingAttributes(
+            const sp<IBinder>& display, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace,
+            uint8_t* outComponentMask) const override;
 
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
@@ -531,6 +535,7 @@
     void handleTransaction(uint32_t transactionFlags);
     void handleTransactionLocked(uint32_t transactionFlags);
 
+    void updateInputWindows();
     void updateCursorAsync();
 
     /* handlePageFlip - latch a new buffer if available and compute the dirty
@@ -547,6 +552,7 @@
     // Can only be called from the main thread or with mStateLock held
     uint32_t setTransactionFlags(uint32_t flags);
     uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
+    void latchAndReleaseBuffer(const sp<Layer>& layer);
     void commitTransaction();
     bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
     uint32_t setClientStateLocked(const ComposerState& composerState);
@@ -920,7 +926,7 @@
     std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)};
     SurfaceTracing mTracing;
     LayerStats mLayerStats;
-    TimeStats& mTimeStats = TimeStats::getInstance();
+    std::unique_ptr<TimeStats> mTimeStats;
     bool mUseHwcVirtualDisplays = false;
     std::atomic<uint32_t> mFrameMissedCount{0};
 
@@ -986,6 +992,8 @@
     std::unique_ptr<Scheduler> mScheduler;
     sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
     sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
+
+    sp<IInputFlinger> mInputFlinger;
 };
 }; // namespace android
 
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index 1261aee..88649e3 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -33,6 +33,7 @@
 #include "Scheduler/EventControlThread.h"
 #include "Scheduler/MessageQueue.h"
 #include "Scheduler/Scheduler.h"
+#include "TimeStats/TimeStats.h"
 
 namespace android::surfaceflinger {
 
@@ -116,6 +117,10 @@
         sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) override {
             return new ColorLayer(args);
         }
+
+        std::unique_ptr<TimeStats> createTimeStats() override {
+            return std::make_unique<TimeStats>();
+        }
     };
     static Factory factory;
 
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 496bbf0..1c27cc7 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -44,6 +44,7 @@
 class StartPropertySetThread;
 class SurfaceFlinger;
 class SurfaceInterceptor;
+class TimeStats;
 
 struct DisplayDeviceCreationArgs;
 struct LayerCreationArgs;
@@ -82,6 +83,8 @@
     virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0;
     virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
 
+    virtual std::unique_ptr<TimeStats> createTimeStats() = 0;
+
 protected:
     ~Factory() = default;
 };
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/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 9730e8c..2b9f5c8 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -32,11 +32,6 @@
 
 namespace android {
 
-TimeStats& TimeStats::getInstance() {
-    static TimeStats sInstance;
-    return sInstance;
-}
-
 void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index,
                           String8& result) {
     ATRACE_CALL();
@@ -268,11 +263,9 @@
     if (!mTimeStatsTracker.count(layerID)) return;
     LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
-        ALOGE("[%d]-[%s]-timeRecords is already at its maximum size[%zu]. Please file a bug.",
+        ALOGE("[%d]-[%s]-timeRecords is at its maximum size[%zu]. Ignore this when unittesting.",
               layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
-        layerRecord.timeRecords.clear();
-        layerRecord.prevTimeRecord.ready = false;
-        layerRecord.waitData = -1;
+        mTimeStatsTracker.erase(layerID);
         return;
     }
     // For most media content, the acquireFence is invalid because the buffer is
@@ -283,7 +276,9 @@
                     {
                             .frameNumber = frameNumber,
                             .postTime = postTime,
+                            .latchTime = postTime,
                             .acquireTime = postTime,
+                            .desiredTime = postTime,
                     },
     };
     layerRecord.timeRecords.push_back(timeRecord);
@@ -394,18 +389,6 @@
     flushAvailableRecordsToStatsLocked(layerID);
 }
 
-void TimeStats::onDisconnect(int32_t layerID) {
-    if (!mEnabled.load()) return;
-
-    ATRACE_CALL();
-    ALOGV("[%d]-onDisconnect", layerID);
-
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    flushAvailableRecordsToStatsLocked(layerID);
-    mTimeStatsTracker.erase(layerID);
-}
-
 void TimeStats::onDestroy(int32_t layerID) {
     if (!mEnabled.load()) return;
 
@@ -414,25 +397,9 @@
 
     std::lock_guard<std::mutex> lock(mMutex);
     if (!mTimeStatsTracker.count(layerID)) return;
-    flushAvailableRecordsToStatsLocked(layerID);
     mTimeStatsTracker.erase(layerID);
 }
 
-void TimeStats::clearLayerRecord(int32_t layerID) {
-    if (!mEnabled.load()) return;
-
-    ATRACE_CALL();
-    ALOGV("[%d]-clearLayerRecord", layerID);
-
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
-    layerRecord.timeRecords.clear();
-    layerRecord.prevTimeRecord.ready = false;
-    layerRecord.waitData = -1;
-    layerRecord.droppedFrames = 0;
-}
-
 void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
     if (!mEnabled.load()) return;
 
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 184bf40..0b24c46 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -38,10 +38,6 @@
 class String8;
 
 class TimeStats {
-    // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
-    // static const size_t MAX_NUM_LAYER_RECORDS = 200;
-    static const size_t MAX_NUM_TIME_RECORDS = 64;
-
     struct FrameTime {
         uint64_t frameNumber = 0;
         nsecs_t postTime = 0;
@@ -80,8 +76,12 @@
     };
 
 public:
-    static TimeStats& getInstance();
+    TimeStats() = default;
+    ~TimeStats() = default;
+
     void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result);
+    bool isEnabled();
+
     void incrementTotalFrames();
     void incrementMissedFrames();
     void incrementClientCompositionFrames();
@@ -96,21 +96,19 @@
     void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime);
     void setPresentFence(int32_t layerID, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence);
-    // On producer disconnect with BufferQueue.
-    void onDisconnect(int32_t layerID);
-    // On layer tear down.
+    // Clean up the layer record
     void onDestroy(int32_t layerID);
-    // When SF is cleaning up the queue, clear the LayerRecord as well.
-    void clearLayerRecord(int32_t layerID);
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
     void removeTimeRecord(int32_t layerID, uint64_t frameNumber);
 
     void setPowerMode(int32_t powerMode);
     void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence);
 
-private:
-    TimeStats() = default;
+    // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
+    // static const size_t MAX_NUM_LAYER_RECORDS = 200;
+    static const size_t MAX_NUM_TIME_RECORDS = 64;
 
+private:
     bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
     void flushAvailableRecordsToStatsLocked(int32_t layerID);
     void flushPowerTimeLocked();
@@ -119,7 +117,6 @@
     void enable();
     void disable();
     void clear();
-    bool isEnabled();
     void dump(bool asProto, std::optional<uint32_t> maxLayers, String8& result);
 
     std::atomic<bool> mEnabled = false;
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/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 599ff5c..75ce4be 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -13,8 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "timestatsproto/TimeStatsHelper.h"
+
 #include <android-base/stringprintf.h>
-#include <timestatsproto/TimeStatsHelper.h>
+#include <inttypes.h>
 
 #include <array>
 
@@ -46,6 +48,14 @@
     hist[*iter]++;
 }
 
+int64_t TimeStatsHelper::Histogram::totalTime() const {
+    int64_t ret = 0;
+    for (const auto& ele : hist) {
+        ret += ele.first * ele.second;
+    }
+    return ret;
+}
+
 float TimeStatsHelper::Histogram::averageTime() const {
     int64_t ret = 0;
     int64_t count = 0;
@@ -87,12 +97,13 @@
 
 std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> maxLayers) const {
     std::string result = "SurfaceFlinger TimeStats:\n";
-    StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart));
-    StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd));
+    StringAppendF(&result, "statsStart = %" PRId64 "\n", statsStart);
+    StringAppendF(&result, "statsEnd = %" PRId64 "\n", statsEnd);
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "missedFrames = %d\n", missedFrames);
     StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
-    StringAppendF(&result, "displayOnTime = %lld ms\n", static_cast<long long int>(displayOnTime));
+    StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+    StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
     StringAppendF(&result, "presentToPresent histogram is as below:\n");
     result.append(presentToPresent.toString());
     const auto dumpStats = generateDumpStats(maxLayers);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 7d0fe79..5f40a1a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -34,6 +34,7 @@
         std::unordered_map<int32_t, int32_t> hist;
 
         void insert(int32_t delta);
+        int64_t totalTime() const;
         float averageTime() const;
         std::string toString() const;
     };
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 9b9dc57..389118a 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -30,6 +30,8 @@
 namespace android {
 
 TransactionCompletedThread::~TransactionCompletedThread() {
+    std::lock_guard lockThread(mThreadMutex);
+
     {
         std::lock_guard lock(mMutex);
         mKeepRunning = false;
@@ -50,11 +52,13 @@
 
 void TransactionCompletedThread::run() {
     std::lock_guard lock(mMutex);
-    if (mRunning) {
+    if (mRunning || !mKeepRunning) {
         return;
     }
     mDeathRecipient = new ThreadDeathRecipient();
     mRunning = true;
+
+    std::lock_guard lockThread(mThreadMutex);
     mThread = std::thread(&TransactionCompletedThread::threadMain, this);
 }
 
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index 5af4097..1612f69 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -90,7 +90,10 @@
         }
     };
 
-    std::thread mThread;
+    // Protects the creation and destruction of mThread
+    std::mutex mThreadMutex;
+
+    std::thread mThread GUARDED_BY(mThreadMutex);
 
     std::mutex mMutex;
     std::condition_variable_any mConditionVariable;
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 988454a..e414991 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -334,16 +334,10 @@
     virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
                                            const char* name, uint32_t width, uint32_t height,
                                            uint32_t flags = 0) {
-        auto layer =
-                client->createSurface(String8(name), width, height, PIXEL_FORMAT_RGBA_8888, flags);
-        EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
+        auto layer = createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags);
 
         Transaction t;
         t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
-        // If we are creating a color layer, set its crop since its size will be ignored.
-        if (flags == ISurfaceComposerClient::eFXSurfaceColor) {
-            t.setCrop_legacy(layer, Rect(0, 0, width, height));
-        }
 
         status_t error = t.apply();
         if (error != NO_ERROR) {
@@ -354,6 +348,15 @@
         return layer;
     }
 
+    virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
+                                             const char* name, uint32_t width, uint32_t height,
+                                             PixelFormat format, uint32_t flags,
+                                             SurfaceControl* parent = nullptr) {
+        auto layer = client->createSurface(String8(name), width, height, format, flags, parent);
+        EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
+        return layer;
+    }
+
     virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
                                            uint32_t flags = 0) {
         return createLayer(mClient, name, width, height, flags);
@@ -509,9 +512,9 @@
 
         mDisplayLayerStack = 0;
 
-        mBlackBgSurface = mClient->createSurface(String8("BaseSurface"), mDisplayWidth,
-                                                 mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
-                                                 ISurfaceComposerClient::eFXSurfaceColor);
+        mBlackBgSurface =
+                createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
+                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
 
         // set layer stack (b/68888219)
         Transaction t;
@@ -834,8 +837,9 @@
 
 TEST_P(LayerTypeTransactionTest, SetZNegative) {
     sp<SurfaceControl> parent =
-            LayerTransactionTest::createLayer("Parent", mDisplayWidth, mDisplayHeight,
+            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
                                               ISurfaceComposerClient::eFXSurfaceContainer);
+    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
@@ -892,8 +896,9 @@
 
 TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) {
     sp<SurfaceControl> parent =
-            LayerTransactionTest::createLayer("Parent", mDisplayWidth, mDisplayHeight,
+            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
                                               ISurfaceComposerClient::eFXSurfaceContainer);
+    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     sp<SurfaceControl> layerB;
@@ -1220,15 +1225,42 @@
     }
 }
 
+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;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(
-            colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceColor));
 
-    Transaction().setLayer(colorLayer, mLayerZBase + 1).apply();
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(colorLayer, mLayerZBase + 1)
+            .apply();
+
     {
         SCOPED_TRACE("default color");
         screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
@@ -1248,10 +1280,14 @@
 
 TEST_F(LayerTransactionTest, SetColorClamped) {
     sp<SurfaceControl> colorLayer;
-    ASSERT_NO_FATAL_FAILURE(
-            colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceColor));
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setColor(colorLayer, half3(2.0f, -1.0f, 0.0f))
+            .apply();
 
-    Transaction().setColor(colorLayer, half3(2.0f, -1.0f, 0.0f)).apply();
     screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
 }
 
@@ -1260,8 +1296,10 @@
     sp<SurfaceControl> colorLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(
-            colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceColor));
+    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
 
     const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
     const float alpha = 0.25f;
@@ -1285,9 +1323,10 @@
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
     ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer(
-            "childWithColor", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
-
+    ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
+                                                     0 /* buffer height */,
+                                                     ISurfaceComposerClient::eFXSurfaceColor));
+    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
     const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
     const float alpha = 0.25f;
     const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
@@ -2129,10 +2168,13 @@
 
 TEST_F(LayerTransactionTest, SetColorTransformBasic) {
     sp<SurfaceControl> colorLayer;
-    ASSERT_NO_FATAL_FAILURE(
-            colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
-
-    Transaction().setLayer(colorLayer, mLayerZBase + 1).apply();
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceColor));
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(colorLayer, mLayerZBase + 1)
+            .apply();
     {
         SCOPED_TRACE("default color");
         screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
@@ -3024,11 +3066,10 @@
     std::unique_ptr<ScreenCapture> sc;
 
     sp<SurfaceControl> childNoBuffer =
-            mClient->createSurface(String8("Bufferless child"), 10, 10,
-                                           PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    sp<SurfaceControl> childBuffer =
-            mClient->createSurface(String8("Buffered child"), 20, 20,
-                                           PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
+            createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */,
+                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20,
+                                                   PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
     fillSurfaceRGBA8(childBuffer, 200, 200, 200);
     SurfaceComposerClient::Transaction{}
             .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
@@ -3078,8 +3119,8 @@
 protected:
     void SetUp() override {
         LayerUpdateTest::SetUp();
-        mChild = mClient->createSurface(String8("Child surface"), 10, 10,
-                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+        mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+                               mFGSurfaceControl.get());
         fillSurfaceRGBA8(mChild, 200, 200, 200);
 
         {
@@ -3254,8 +3295,7 @@
 
 TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) {
     sp<SurfaceControl> mGrandChild =
-        mClient->createSurface(String8("Grand Child"), 10, 10,
-                PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+            createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
     fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
 
     {
@@ -3318,10 +3358,9 @@
 TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
     sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
     sp<SurfaceControl> mChildNewClient =
-            mNewComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+            createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
+                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
 
-    ASSERT_TRUE(mChildNewClient != nullptr);
     ASSERT_TRUE(mChildNewClient->isValid());
 
     fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
@@ -3356,6 +3395,61 @@
     }
 }
 
+TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
+    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> childNewClient =
+            newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+    ASSERT_TRUE(childNewClient != nullptr);
+    ASSERT_TRUE(childNewClient->isValid());
+
+    fillSurfaceRGBA8(childNewClient, 200, 200, 200);
+
+    Transaction()
+            .hide(mChild)
+            .show(childNewClient)
+            .setPosition(childNewClient, 10, 10)
+            .setPosition(mFGSurfaceControl, 64, 64)
+            .apply();
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    Transaction().detachChildren(mFGSurfaceControl).apply();
+    Transaction().hide(childNewClient).apply();
+
+    // Nothing should have changed.
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(64, 64);
+        mCapture->expectChildColor(74, 74);
+        mCapture->expectFGColor(84, 84);
+    }
+
+    sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
+    fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
+                   32);
+    Transaction()
+            .setLayer(newParentSurface, INT32_MAX - 1)
+            .show(newParentSurface)
+            .setPosition(newParentSurface, 20, 20)
+            .reparent(childNewClient, newParentSurface->getHandle())
+            .apply();
+    {
+        mCapture = screenshot();
+        // Child is now hidden.
+        mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
+    }
+}
+
 TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
     asTransaction([&](Transaction& t) {
         t.show(mChild);
@@ -3427,9 +3521,8 @@
     mChild.clear();
 
     // Now recreate it as hidden
-    mChild = mClient->createSurface(String8("Child surface"), 10, 10,
-                                            PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
-                                            mFGSurfaceControl.get());
+    mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888,
+                           ISurfaceComposerClient::eHidden, mFGSurfaceControl.get());
 
     // Show the child layer in a deferred transaction
     asTransaction([&](Transaction& t) {
@@ -3545,9 +3638,8 @@
 }
 
 TEST_F(ChildLayerTest, NestedChildren) {
-    sp<SurfaceControl> grandchild =
-            mClient->createSurface(String8("Grandchild surface"), 10, 10,
-                                           PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
     fillSurfaceRGBA8(grandchild, 50, 50, 50);
 
     {
@@ -3585,9 +3677,8 @@
 // Verify setting a size on a buffer layer has no effect.
 TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) {
     sp<SurfaceControl> bufferLayer =
-            mClient->createSurface(String8("BufferLayer"), 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
-                                   mFGSurfaceControl.get());
-    ASSERT_TRUE(bufferLayer != nullptr);
+            createSurface(mClient, "BufferLayer", 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
+                          mFGSurfaceControl.get());
     ASSERT_TRUE(bufferLayer->isValid());
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30));
     asTransaction([&](Transaction& t) { t.show(bufferLayer); });
@@ -3606,10 +3697,8 @@
 // which will crop the color layer.
 TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) {
     sp<SurfaceControl> colorLayer =
-            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   ISurfaceComposerClient::eFXSurfaceColor,
-                                   mFGSurfaceControl.get());
-    ASSERT_TRUE(colorLayer != nullptr);
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
     ASSERT_TRUE(colorLayer->isValid());
     asTransaction([&](Transaction& t) {
         t.setColor(colorLayer, half3{0, 0, 0});
@@ -3629,15 +3718,12 @@
 // Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has
 // a crop which will be used to crop the color layer.
 TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) {
-    sp<SurfaceControl> cropLayer =
-            mClient->createSurface(String8("CropLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   0 /* flags */, mFGSurfaceControl.get());
-    ASSERT_TRUE(cropLayer != nullptr);
+    sp<SurfaceControl> cropLayer = createSurface(mClient, "CropLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                                 0 /* flags */, mFGSurfaceControl.get());
     ASSERT_TRUE(cropLayer->isValid());
     sp<SurfaceControl> colorLayer =
-            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
-    ASSERT_TRUE(colorLayer != nullptr);
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
     ASSERT_TRUE(colorLayer->isValid());
     asTransaction([&](Transaction& t) {
         t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
@@ -3661,10 +3747,8 @@
 // Verify for boundless layer with no children, their transforms have no effect.
 TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) {
     sp<SurfaceControl> colorLayer =
-            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   ISurfaceComposerClient::eFXSurfaceColor,
-                                   mFGSurfaceControl.get());
-    ASSERT_TRUE(colorLayer != nullptr);
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
     ASSERT_TRUE(colorLayer->isValid());
     asTransaction([&](Transaction& t) {
         t.setPosition(colorLayer, 320, 320);
@@ -3686,20 +3770,16 @@
 // Verify for boundless layer with children, their transforms have an effect.
 TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) {
     sp<SurfaceControl> boundlessLayerRightShift =
-            mClient->createSurface(String8("BoundlessLayerRightShift"), 0, 0,
-                                   PIXEL_FORMAT_RGBA_8888, 0 /* flags */, mFGSurfaceControl.get());
-    ASSERT_TRUE(boundlessLayerRightShift != nullptr);
+            createSurface(mClient, "BoundlessLayerRightShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          0 /* flags */, mFGSurfaceControl.get());
     ASSERT_TRUE(boundlessLayerRightShift->isValid());
     sp<SurfaceControl> boundlessLayerDownShift =
-            mClient->createSurface(String8("BoundlessLayerLeftShift"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   0 /* flags */, boundlessLayerRightShift.get());
-    ASSERT_TRUE(boundlessLayerDownShift != nullptr);
+            createSurface(mClient, "BoundlessLayerLeftShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          0 /* flags */, boundlessLayerRightShift.get());
     ASSERT_TRUE(boundlessLayerDownShift->isValid());
     sp<SurfaceControl> colorLayer =
-            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   ISurfaceComposerClient::eFXSurfaceColor,
-                                   boundlessLayerDownShift.get());
-    ASSERT_TRUE(colorLayer != nullptr);
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceColor, boundlessLayerDownShift.get());
     ASSERT_TRUE(colorLayer->isValid());
     asTransaction([&](Transaction& t) {
         t.setPosition(boundlessLayerRightShift, 32, 0);
@@ -3760,16 +3840,13 @@
 
 // Verify for boundless root layers with children, their transforms have an effect.
 TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) {
-    sp<SurfaceControl> rootBoundlessLayer =
-            mClient->createSurface(String8("RootBoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   0 /* flags */);
-    ASSERT_TRUE(rootBoundlessLayer != nullptr);
+    sp<SurfaceControl> rootBoundlessLayer = createSurface(mClient, "RootBoundlessLayer", 0, 0,
+                                                          PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
     ASSERT_TRUE(rootBoundlessLayer->isValid());
     sp<SurfaceControl> colorLayer =
-            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   ISurfaceComposerClient::eFXSurfaceColor,
-                                   rootBoundlessLayer.get());
-    ASSERT_TRUE(colorLayer != nullptr);
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceColor, rootBoundlessLayer.get());
+
     ASSERT_TRUE(colorLayer->isValid());
     asTransaction([&](Transaction& t) {
         t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
@@ -3809,9 +3886,8 @@
 TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
-    sp<SurfaceControl> child =
-            mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
 
     SurfaceComposerClient::Transaction().show(child).apply(true);
@@ -3825,9 +3901,8 @@
 TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
-    sp<SurfaceControl> child =
-            mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
 
     SurfaceComposerClient::Transaction().show(child).apply(true);
@@ -3839,9 +3914,8 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureTransparent) {
-    sp<SurfaceControl> child =
-            mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
 
     fillSurfaceRGBA8(child, 200, 200, 200);
 
@@ -3859,9 +3933,9 @@
 TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
-    sp<SurfaceControl> child =
-            mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    ASSERT_NE(nullptr, child.get()) << "failed to create surface";
     sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
     fillSurfaceRGBA8(child, 200, 200, 200);
     fillSurfaceRGBA8(relative, 100, 100, 100);
@@ -3882,12 +3956,10 @@
 TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
-    sp<SurfaceControl> child =
-            mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
-    sp<SurfaceControl> relative =
-            mClient->createSurface(String8("Relative surface"), 10, 10,
-                                           PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
+                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
     fillSurfaceRGBA8(relative, 100, 100, 100);
 
@@ -3916,9 +3988,8 @@
     void SetUp() override {
         LayerUpdateTest::SetUp();
 
-        mChild =
-            mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                    0, mFGSurfaceControl.get());
+        mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+                               mFGSurfaceControl.get());
         fillSurfaceRGBA8(mChild, 200, 200, 200);
 
         SurfaceComposerClient::Transaction().show(mChild).apply(true);
@@ -3974,14 +4045,12 @@
 TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
-    sp<SurfaceControl> child =
-            mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
 
-    sp<SurfaceControl> grandchild =
-            mClient->createSurface(String8("Grandchild surface"), 5, 5,
-                                           PIXEL_FORMAT_RGBA_8888, 0, child.get());
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
 
     fillSurfaceRGBA8(grandchild, 50, 50, 50);
     SurfaceComposerClient::Transaction()
@@ -3998,9 +4067,8 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureChildOnly) {
-    sp<SurfaceControl> child =
-            mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
     auto childHandle = child->getHandle();
 
@@ -4013,15 +4081,13 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
-    sp<SurfaceControl> child =
-            mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
     auto childHandle = child->getHandle();
 
-    sp<SurfaceControl> grandchild =
-            mClient->createSurface(String8("Grandchild surface"), 5, 5,
-                                           PIXEL_FORMAT_RGBA_8888, 0, child.get());
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
     fillSurfaceRGBA8(grandchild, 50, 50, 50);
 
     SurfaceComposerClient::Transaction()
@@ -4040,9 +4106,8 @@
 
 TEST_F(ScreenCaptureTest, CaptureCrop) {
     sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-    sp<SurfaceControl> blueLayer =
-            mClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888,
-                                           0, redLayer.get());
+    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
 
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
@@ -4073,9 +4138,8 @@
 
 TEST_F(ScreenCaptureTest, CaptureSize) {
     sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-    sp<SurfaceControl> blueLayer =
-            mClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888,
-                                           0, redLayer.get());
+    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
 
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 356a880..16e0891 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -1214,7 +1214,7 @@
 protected:
     void SetUp() override {
         TransactionTest::SetUp();
-        mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+        mChild = mComposerClient->createSurface(String8("Child surface"), 0, 0,
                                                 PIXEL_FORMAT_RGBA_8888,
                                                 ISurfaceComposerClient::eFXSurfaceColor,
                                                 mFGSurfaceControl.get());
@@ -1222,6 +1222,7 @@
             TransactionScope ts(*sFakeComposer);
             ts.setColor(mChild,
                         {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
+            ts.setCrop_legacy(mChild, Rect(0, 0, 10, 10));
         }
 
         sFakeComposer->runVSyncAndWait();
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 58f1e9c..42b7146 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -31,6 +31,8 @@
         "EventThreadTest.cpp",
         "LayerHistoryTest.cpp",
         "SchedulerTest.cpp",
+        "SchedulerUtilsTest.cpp",
+        "TimeStatsTest.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 6d3e715..5a6aa92 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -94,6 +94,10 @@
         EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
         EXPECT_CALL(*mPrimaryDispSync, getPeriod())
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
 
         mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
         setupComposer(0);
@@ -140,7 +144,6 @@
     sp<DisplayDevice> mDisplay;
     sp<DisplayDevice> mExternalDisplay;
     sp<mock::DisplaySurface> mDisplaySurface = new mock::DisplaySurface();
-    renderengine::mock::Surface* mRenderSurface = new renderengine::mock::Surface();
     mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
 
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
@@ -246,8 +249,6 @@
         test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, DEFAULT_DISPLAY_ID,
                                                    false /* isVirtual */, true /* isPrimary */)
                                  .setDisplaySurface(test->mDisplaySurface)
-                                 .setRenderSurface(std::unique_ptr<renderengine::Surface>(
-                                         test->mRenderSurface))
                                  .setNativeWindow(test->mNativeWindow)
                                  .setSecure(Derived::IS_SECURE)
                                  .setPowerMode(Derived::INIT_POWER_MODE)
@@ -579,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())
@@ -625,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);
@@ -689,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 e8c8894..34cee3e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -135,7 +135,6 @@
     sp<mock::GraphicBufferConsumer> mConsumer;
     sp<mock::GraphicBufferProducer> mProducer;
     surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
-    renderengine::mock::Surface* mRenderSurface = nullptr;
 };
 
 DisplayTransactionTest::DisplayTransactionTest() {
@@ -342,22 +341,10 @@
         EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
                 .WillOnce(Return(test->mNativeWindow));
 
-        // For simplicity, we only expect to create a single render surface for
-        // each test.
-        ASSERT_TRUE(test->mRenderSurface == nullptr);
-        test->mRenderSurface = new renderengine::mock::Surface();
-        EXPECT_CALL(*test->mRenderEngine, createSurface())
-                .WillOnce(Return(ByMove(
-                        std::unique_ptr<renderengine::Surface>(test->mRenderSurface))));
         EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
         EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
-
-        // Creating a DisplayDevice requires getting default dimensions from the
-        // native window.
-        EXPECT_CALL(*test->mRenderSurface, setAsync(static_cast<bool>(ASYNC))).Times(1);
-        EXPECT_CALL(*test->mRenderSurface, setCritical(static_cast<bool>(CRITICAL))).Times(1);
     }
 
     static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
@@ -1285,7 +1272,7 @@
     // Insert display data so that the HWC thinks it created the virtual display.
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(displayId);
-    mFlinger.mutableHwcDisplayData()[*displayId] = {};
+    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
 
     setupNewDisplayDeviceInternalTest<Case>();
 }
@@ -1798,7 +1785,7 @@
     // A virtual display is set up but is removed from the current state.
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(displayId);
-    mFlinger.mutableHwcDisplayData()[*displayId] = {};
+    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
     Case::Display::injectHwcDisplay(this);
     auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
     existing.inject();
@@ -1946,12 +1933,10 @@
     // A display is set up
     auto nativeWindow = new mock::NativeWindow();
     auto displaySurface = new mock::DisplaySurface();
-    auto renderSurface = new renderengine::mock::Surface();
     sp<GraphicBuffer> buf = new GraphicBuffer();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
     display.setDisplaySurface(displaySurface);
-    display.setRenderSurface(std::unique_ptr<renderengine::Surface>(renderSurface));
     // Setup injection expections
     EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
             .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
@@ -1992,12 +1977,10 @@
     // A display is set up
     auto nativeWindow = new mock::NativeWindow();
     auto displaySurface = new mock::DisplaySurface();
-    auto renderSurface = new renderengine::mock::Surface();
     sp<GraphicBuffer> buf = new GraphicBuffer();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
     display.setDisplaySurface(displaySurface);
-    display.setRenderSurface(std::unique_ptr<renderengine::Surface>(renderSurface));
     // Setup injection expections
     EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
             .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
@@ -2570,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;
@@ -2585,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 {
@@ -2841,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:
@@ -2873,6 +2873,7 @@
     // --------------------------------------------------------------------
     // Preconditions
 
+    Case::Doze::setupComposerCallExpectations(this);
     auto display =
             Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
     Case::setInitialPrimaryHWVsyncEnabled(this,
@@ -2931,7 +2932,7 @@
     // Insert display data so that the HWC thinks it created the virtual display.
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(displayId);
-    mFlinger.mutableHwcDisplayData()[*displayId] = {};
+    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
 
     // A virtual display device is set up
     Case::Display::injectHwcDisplay(this);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index b518f10..3fb1a6e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -85,11 +85,13 @@
 
     // Because the counter was incremented, the elements were inserted into different
     // containers.
+    // We expect the get method to access the slot at the current counter of the index
+    // is 0.
     const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
     EXPECT_EQ(1, testMap1.size());
-    auto element = testMap1.find("TestLayer0");
-    EXPECT_EQ("TestLayer0", element->first);
-    EXPECT_EQ(0, element->second);
+    auto element = testMap1.find("TestLayer3");
+    EXPECT_EQ("TestLayer3", element->first);
+    EXPECT_EQ(3, element->second);
 
     const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(1);
     EXPECT_EQ(2, testMap2.size());
@@ -102,9 +104,9 @@
 
     const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(2);
     EXPECT_EQ(1, testMap3.size());
-    element = testMap3.find("TestLayer3");
-    EXPECT_EQ("TestLayer3", element->first);
-    EXPECT_EQ(3, element->second);
+    element = testMap3.find("TestLayer0");
+    EXPECT_EQ("TestLayer0", element->first);
+    EXPECT_EQ(0, element->second);
 
     // Testing accessing object at an empty container will return an empty map.
     const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(3);
@@ -113,7 +115,6 @@
 
 TEST_F(LayerHistoryTest, clearTheMap) {
     mLayerHistory->insert("TestLayer0", 0);
-    mLayerHistory->incrementCounter();
 
     const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
     EXPECT_EQ(1, testMap1.size());
@@ -121,6 +122,7 @@
     EXPECT_EQ("TestLayer0", element->first);
     EXPECT_EQ(0, element->second);
 
+    mLayerHistory->incrementCounter();
     // The array currently contains 30 elements.
     for (int i = 1; i < 30; ++i) {
         mLayerHistory->insert("TestLayer0", i);
@@ -140,5 +142,33 @@
     EXPECT_EQ(testMap3.end(), element);
 }
 
+TEST_F(LayerHistoryTest, testingGet) {
+    // The array currently contains 30 elements.
+    for (int i = 0; i < 30; ++i) {
+        const auto name = "TestLayer" + std::to_string(i);
+        mLayerHistory->insert(name, i);
+        mLayerHistory->incrementCounter();
+    }
+
+    // The counter should be set to 0, and the map at 0 should be cleared.
+    const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
+    EXPECT_EQ(0, testMap1.size());
+
+    // This should return (ARRAY_SIZE + (counter - 3)) % ARRAY_SIZE
+    const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(3);
+    EXPECT_EQ(1, testMap2.size());
+    auto element = testMap2.find("TestLayer27");
+    EXPECT_EQ("TestLayer27", element->first);
+    EXPECT_EQ(27, element->second);
+
+    // If the user gives an out of bound index, we should mod it with ARRAY_SIZE first,
+    // so requesting element 40 would be the same as requesting element 10.
+    const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(40);
+    EXPECT_EQ(1, testMap3.size());
+    element = testMap3.find("TestLayer20");
+    EXPECT_EQ("TestLayer20", element->first);
+    EXPECT_EQ(20, element->second);
+}
+
 } // namespace
 } // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index be91a9c..d0cf1b7 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -184,6 +184,5 @@
     EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
     ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
 }
-
 } // namespace
-} // namespace android
\ No newline at end of file
+} // 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/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a519f1f..6d4f7ef 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -28,6 +28,8 @@
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceInterceptor.h"
 
+#include "TimeStats/TimeStats.h"
+
 namespace android {
 
 class EventThread;
@@ -131,6 +133,11 @@
         return nullptr;
     }
 
+    std::unique_ptr<TimeStats> createTimeStats() override {
+        // TODO: Use test-fixture controlled factory
+        return std::make_unique<TimeStats>();
+    }
+
     using CreateBufferQueueFunction =
             std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
                                sp<IGraphicBufferConsumer>* /* outConsumer */,
@@ -474,11 +481,6 @@
             return *this;
         }
 
-        auto& setRenderSurface(std::unique_ptr<renderengine::Surface> renderSurface) {
-            mCreationArgs.renderSurface = std::move(renderSurface);
-            return *this;
-        }
-
         auto& setSecure(bool secure) {
             mCreationArgs.isSecure = secure;
             return *this;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
new file mode 100644
index 0000000..186ed79
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -0,0 +1,513 @@
+/*
+ * 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 "LibSurfaceFlingerUnittests"
+
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <random>
+
+#include "TimeStats/TimeStats.h"
+
+using namespace android::surfaceflinger;
+using namespace google::protobuf;
+
+namespace android {
+namespace {
+
+// clang-format off
+#define FMT_PROTO          true
+#define FMT_STRING         false
+#define LAYER_ID_0         0
+#define LAYER_ID_1         1
+#define LAYER_ID_INVALID   -1
+#define NUM_LAYERS         1
+#define NUM_LAYERS_INVALID "INVALID"
+
+enum InputCommand : int32_t {
+    ENABLE                 = 0,
+    DISABLE                = 1,
+    CLEAR                  = 2,
+    DUMP_ALL               = 3,
+    DUMP_MAXLAYERS_1       = 4,
+    DUMP_MAXLAYERS_INVALID = 5,
+    INPUT_COMMAND_BEGIN    = ENABLE,
+    INPUT_COMMAND_END      = DUMP_MAXLAYERS_INVALID,
+    INPUT_COMMAND_RANGE    = INPUT_COMMAND_END - INPUT_COMMAND_BEGIN + 1,
+};
+
+enum TimeStamp : int32_t {
+    POST                   = 0,
+    ACQUIRE                = 1,
+    ACQUIRE_FENCE          = 2,
+    LATCH                  = 3,
+    DESIRED                = 4,
+    PRESENT                = 5,
+    PRESENT_FENCE          = 6,
+    TIME_STAMP_BEGIN       = POST,
+    TIME_STAMP_END         = PRESENT,
+    TIME_STAMP_RANGE       = TIME_STAMP_END - TIME_STAMP_BEGIN + 1,
+};
+
+static const TimeStamp NORMAL_SEQUENCE[] = {
+        TimeStamp::POST,
+        TimeStamp::ACQUIRE,
+        TimeStamp::LATCH,
+        TimeStamp::DESIRED,
+        TimeStamp::PRESENT,
+};
+
+static const TimeStamp NORMAL_SEQUENCE_2[] = {
+        TimeStamp::POST,
+        TimeStamp::ACQUIRE_FENCE,
+        TimeStamp::LATCH,
+        TimeStamp::DESIRED,
+        TimeStamp::PRESENT_FENCE,
+};
+
+static const TimeStamp UNORDERED_SEQUENCE[] = {
+        TimeStamp::ACQUIRE,
+        TimeStamp::LATCH,
+        TimeStamp::POST,
+        TimeStamp::DESIRED,
+        TimeStamp::PRESENT,
+};
+
+static const TimeStamp INCOMPLETE_SEQUENCE[] = {
+        TimeStamp::POST,
+};
+// clang-format on
+
+class TimeStatsTest : public testing::Test {
+public:
+    TimeStatsTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    ~TimeStatsTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    std::string inputCommand(InputCommand cmd, bool useProto);
+
+    void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+
+    int32_t genRandomInt32(int32_t begin, int32_t end);
+
+    template <size_t N>
+    void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
+                          nsecs_t ts) {
+        for (size_t i = 0; i < N; i++, ts += 1000000) {
+            setTimeStamp(sequence[i], id, frameNumber, ts);
+        }
+    }
+
+    std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
+    std::unique_ptr<TimeStats> mTimeStats = std::make_unique<TimeStats>();
+};
+
+std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
+    size_t index = 0;
+    String8 result;
+    Vector<String16> args;
+
+    switch (cmd) {
+        case InputCommand::ENABLE:
+            args.push_back(String16("-enable"));
+            break;
+        case InputCommand::DISABLE:
+            args.push_back(String16("-disable"));
+            break;
+        case InputCommand::CLEAR:
+            args.push_back(String16("-clear"));
+            break;
+        case InputCommand::DUMP_ALL:
+            args.push_back(String16("-dump"));
+            break;
+        case InputCommand::DUMP_MAXLAYERS_1:
+            args.push_back(String16("-dump"));
+            args.push_back(String16("-maxlayers"));
+            args.push_back(String16(std::to_string(NUM_LAYERS).c_str()));
+            break;
+        case InputCommand::DUMP_MAXLAYERS_INVALID:
+            args.push_back(String16("-dump"));
+            args.push_back(String16("-maxlayers"));
+            args.push_back(String16(NUM_LAYERS_INVALID));
+            break;
+        default:
+            ALOGD("Invalid control command");
+    }
+
+    EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, index, result));
+    return std::string(result.string(), result.size());
+}
+
+static std::string genLayerName(int32_t layerID) {
+    return (layerID < 0 ? "invalid.dummy" : "com.dummy#") + std::to_string(layerID);
+}
+
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+    switch (type) {
+        case TimeStamp::POST:
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts));
+            break;
+        case TimeStamp::ACQUIRE:
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
+            break;
+        case TimeStamp::ACQUIRE_FENCE:
+            ASSERT_NO_FATAL_FAILURE(
+                    mTimeStats->setAcquireFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+            break;
+        case TimeStamp::LATCH:
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setLatchTime(id, frameNumber, ts));
+            break;
+        case TimeStamp::DESIRED:
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
+            break;
+        case TimeStamp::PRESENT:
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts));
+            break;
+        case TimeStamp::PRESENT_FENCE:
+            ASSERT_NO_FATAL_FAILURE(
+                    mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+            break;
+        default:
+            ALOGD("Invalid timestamp type");
+    }
+}
+
+int32_t TimeStatsTest::genRandomInt32(int32_t begin, int32_t end) {
+    std::uniform_int_distribution<int32_t> distr(begin, end);
+    return distr(mRandomEngine);
+}
+
+TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    ASSERT_TRUE(mTimeStats->isEnabled());
+
+    EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
+    ASSERT_FALSE(mTimeStats->isEnabled());
+}
+
+TEST_F(TimeStatsTest, canIncreaseGlobalStats) {
+    constexpr size_t TOTAL_FRAMES = 5;
+    constexpr size_t MISSED_FRAMES = 4;
+    constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    for (size_t i = 0; i < TOTAL_FRAMES; i++) {
+        ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
+    }
+    for (size_t i = 0; i < MISSED_FRAMES; i++) {
+        ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
+    }
+    for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) {
+        ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
+    }
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_TRUE(globalProto.has_total_frames());
+    EXPECT_EQ(TOTAL_FRAMES, globalProto.total_frames());
+    ASSERT_TRUE(globalProto.has_missed_frames());
+    EXPECT_EQ(MISSED_FRAMES, globalProto.missed_frames());
+    ASSERT_TRUE(globalProto.has_client_composition_frames());
+    EXPECT_EQ(CLIENT_COMPOSITION_FRAMES, globalProto.client_composition_frames());
+}
+
+TEST_F(TimeStatsTest, canInsertGlobalPresentToPresent) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
+
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000)));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)));
+
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_OFF));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(6000000)));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(8000000)));
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.present_to_present_size());
+    const SFTimeStatsHistogramBucketProto& histogramProto = globalProto.present_to_present().Get(0);
+    EXPECT_EQ(1, histogramProto.frame_count());
+    EXPECT_EQ(2, histogramProto.time_millis());
+}
+
+TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_layer_name());
+    EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(1, layerProto.total_frames());
+    ASSERT_EQ(6, layerProto.deltas_size());
+    for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
+        ASSERT_EQ(1, deltaProto.histograms_size());
+        const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+        EXPECT_EQ(1, histogramProto.frame_count());
+        if ("post2acquire" == deltaProto.delta_name()) {
+            EXPECT_EQ(1, histogramProto.time_millis());
+        } else if ("post2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(4, histogramProto.time_millis());
+        } else if ("acquire2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(3, histogramProto.time_millis());
+        } else if ("latch2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(2, histogramProto.time_millis());
+        } else if ("desired2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(1, histogramProto.time_millis());
+        } else if ("present2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(1, histogramProto.time_millis());
+        } else {
+            FAIL() << "Unknown delta_name: " << deltaProto.delta_name();
+        }
+    }
+}
+
+TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_INVALID, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_INVALID, 2, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canInsertMultipleLayersTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    EXPECT_EQ(2, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canInsertUnorderedLayerTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(UNORDERED_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_layer_name());
+    EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(1, layerProto.total_frames());
+    ASSERT_EQ(6, layerProto.deltas_size());
+    for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
+        ASSERT_EQ(1, deltaProto.histograms_size());
+        const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+        EXPECT_EQ(1, histogramProto.frame_count());
+        if ("post2acquire" == deltaProto.delta_name()) {
+            EXPECT_EQ(0, histogramProto.time_millis());
+        } else if ("post2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(2, histogramProto.time_millis());
+        } else if ("acquire2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(2, histogramProto.time_millis());
+        } else if ("latch2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(2, histogramProto.time_millis());
+        } else if ("desired2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(1, histogramProto.time_millis());
+        } else if ("present2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(1, histogramProto.time_millis());
+        } else {
+            FAIL() << "Unknown delta_name: " << deltaProto.delta_name();
+        }
+    }
+}
+
+TEST_F(TimeStatsTest, canRemoveTimeRecord) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(0, 2));
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canRecoverFromIncompleteTimeRecordError) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    uint64_t frameNumber = 1;
+    nsecs_t ts = 1000000;
+    insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) {
+        frameNumber++;
+        ts += 1000000;
+        insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, frameNumber, ts);
+    }
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, layerTimeStatsOnDestroy) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(0));
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canClearTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+    EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    EXPECT_EQ(0, globalProto.total_frames());
+    EXPECT_EQ(0, globalProto.missed_frames());
+    EXPECT_EQ(0, globalProto.client_composition_frames());
+    EXPECT_EQ(0, globalProto.present_to_present_size());
+    EXPECT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 3, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(
+            globalProto.ParseFromString(inputCommand(InputCommand::DUMP_MAXLAYERS_1, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_layer_name());
+    EXPECT_EQ(genLayerName(LAYER_ID_1), layerProto.layer_name());
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(2, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canDumpWithInvalidMaxLayers) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(
+            inputCommand(InputCommand::DUMP_MAXLAYERS_INVALID, FMT_PROTO)));
+
+    ASSERT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canSurviveMonkey) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    for (size_t i = 0; i < 10000000; ++i) {
+        const int32_t layerID = genRandomInt32(-1, 10);
+        const int32_t frameNumber = genRandomInt32(1, 10);
+        switch (genRandomInt32(0, 100)) {
+            case 0:
+                ALOGV("removeTimeRecord");
+                ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerID, frameNumber));
+                continue;
+            case 1:
+                ALOGV("onDestroy");
+                ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerID));
+                continue;
+        }
+        TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
+        const int32_t ts = genRandomInt32(1, 1000000000);
+        ALOGV("type[%d], layerID[%d], frameNumber[%d], ts[%d]", type, layerID, frameNumber, ts);
+        setTimeStamp(type, layerID, frameNumber, ts);
+    }
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index c0395c0..5a46c26 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -113,6 +113,9 @@
     MOCK_METHOD4(setLayerInfo, Error(Display, Layer, uint32_t, uint32_t));
     MOCK_METHOD3(getRenderIntents, Error(Display, ColorMode, std::vector<RenderIntent>*));
     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.cpp b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
index af54df6..fbfbc3f 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
@@ -26,9 +26,6 @@
 RenderEngine::RenderEngine() = default;
 RenderEngine::~RenderEngine() = default;
 
-Surface::Surface() = default;
-Surface::~Surface() = default;
-
 Image::Image() = default;
 Image::~Image() = default;
 
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
index afca63a..a416808 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
@@ -23,7 +23,6 @@
 #include <renderengine/LayerSettings.h>
 #include <renderengine/Mesh.h>
 #include <renderengine/RenderEngine.h>
-#include <renderengine/Surface.h>
 #include <renderengine/Texture.h>
 #include <ui/GraphicBuffer.h>
 
@@ -37,15 +36,12 @@
     ~RenderEngine() override;
 
     MOCK_METHOD0(createFramebuffer, std::unique_ptr<Framebuffer>());
-    MOCK_METHOD0(createSurface, std::unique_ptr<renderengine::Surface>());
     MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>());
     MOCK_CONST_METHOD0(primeCache, void());
     MOCK_METHOD1(dump, void(String8&));
     MOCK_CONST_METHOD0(useNativeFenceSync, bool());
     MOCK_CONST_METHOD0(useWaitSync, bool());
     MOCK_CONST_METHOD0(isCurrent, bool());
-    MOCK_METHOD1(setCurrentSurface, bool(const renderengine::Surface&));
-    MOCK_METHOD0(resetCurrentSurface, void());
     MOCK_METHOD0(flush, base::unique_fd());
     MOCK_METHOD0(finish, bool());
     MOCK_METHOD1(waitFence, bool(base::unique_fd*));
@@ -60,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());
@@ -82,23 +79,6 @@
                                 ANativeWindowBuffer* const, base::unique_fd*));
 };
 
-class Surface : public renderengine::Surface {
-public:
-    Surface();
-    ~Surface() override;
-
-    MOCK_METHOD1(setCritical, void(bool));
-    MOCK_METHOD1(setAsync, void(bool));
-    MOCK_METHOD1(setNativeWindow, void(ANativeWindow*));
-    MOCK_CONST_METHOD0(swapBuffers, void());
-    MOCK_CONST_METHOD0(queryRedSize, int32_t());
-    MOCK_CONST_METHOD0(queryGreenSize, int32_t());
-    MOCK_CONST_METHOD0(queryBlueSize, int32_t());
-    MOCK_CONST_METHOD0(queryAlphaSize, int32_t());
-    MOCK_CONST_METHOD0(getWidth, int32_t());
-    MOCK_CONST_METHOD0(getHeight, int32_t());
-};
-
 class Image : public renderengine::Image {
 public:
     Image();
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_channel.cpp b/services/vr/bufferhubd/buffer_channel.cpp
index 589b31a..cf072b6 100644
--- a/services/vr/bufferhubd/buffer_channel.cpp
+++ b/services/vr/bufferhubd/buffer_channel.cpp
@@ -52,8 +52,7 @@
       buffer_id(), /*consumer_count=*/0, buffer_node_->buffer_desc().width,
       buffer_node_->buffer_desc().height, buffer_node_->buffer_desc().layers,
       buffer_node_->buffer_desc().format, buffer_node_->buffer_desc().usage,
-      /*pending_count=*/0, /*state=*/0, /*signaled_mask=*/0,
-      /*index=*/0);
+      /*state=*/0, /*signaled_mask=*/0, /*index=*/0);
 }
 
 void BufferChannel::HandleImpulse(Message& /*message*/) {
@@ -83,10 +82,13 @@
   ATRACE_NAME("BufferChannel::OnImport");
   ALOGD_IF(TRACE, "BufferChannel::OnImport: buffer=%d.", buffer_id());
 
+  BorrowedHandle ashmem_handle =
+      BorrowedHandle(buffer_node_->metadata().ashmem_fd().get());
+
   // TODO(b/112057680) Move away from the GraphicBuffer-based IonBuffer.
   return BufferTraits<BorrowedHandle>{
       /*buffer_handle=*/buffer_node_->buffer_handle(),
-      /*metadata_handle=*/buffer_node_->metadata().ashmem_handle().Borrow(),
+      /*metadata_handle=*/ashmem_handle,
       /*id=*/buffer_id(),
       /*client_state_mask=*/client_state_mask_,
       /*metadata_size=*/buffer_node_->metadata().metadata_size(),
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index b73c47d..f50d292 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -56,8 +56,6 @@
   stream << " ";
   stream << std::setw(10) << "Usage";
   stream << " ";
-  stream << std::setw(9) << "Pending";
-  stream << " ";
   stream << std::setw(18) << "State";
   stream << " ";
   stream << std::setw(18) << "Signaled";
@@ -90,8 +88,6 @@
       stream << std::setw(8) << info.usage;
       stream << std::dec << std::setfill(' ');
       stream << " ";
-      stream << std::setw(9) << info.pending_count;
-      stream << " ";
       stream << "0x" << std::hex << std::setfill('0');
       stream << std::setw(16) << info.state;
       stream << " ";
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/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
index 98ef917..f936e95 100644
--- a/services/vr/bufferhubd/consumer_channel.cpp
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -153,11 +153,10 @@
   }
 }
 
-bool ConsumerChannel::OnProducerPosted() {
+void ConsumerChannel::OnProducerPosted() {
   acquired_ = false;
   released_ = false;
   SignalAvailable();
-  return true;
 }
 
 void ConsumerChannel::OnProducerClosed() {
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_hub.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
index e47ffa3..676617d 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
@@ -52,7 +52,6 @@
     uint32_t layer_count = 0;
     uint32_t format = 0;
     uint64_t usage = 0;
-    size_t pending_count = 0;
     uint64_t state = 0;
     uint64_t signaled_mask = 0;
     uint64_t index = 0;
@@ -63,8 +62,7 @@
 
     BufferInfo(int id, size_t consumer_count, uint32_t width, uint32_t height,
                uint32_t layer_count, uint32_t format, uint64_t usage,
-               size_t pending_count, uint64_t state, uint64_t signaled_mask,
-               uint64_t index)
+               uint64_t state, uint64_t signaled_mask, uint64_t index)
         : id(id),
           type(kProducerType),
           consumer_count(consumer_count),
@@ -73,7 +71,6 @@
           layer_count(layer_count),
           format(format),
           usage(usage),
-          pending_count(pending_count),
           state(state),
           signaled_mask(signaled_mask),
           index(index) {}
diff --git a/services/vr/bufferhubd/include/private/dvr/consumer_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
index 3298529..de0f23c 100644
--- a/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
@@ -26,7 +26,7 @@
   uint64_t client_state_mask() const { return client_state_mask_; }
   BufferInfo GetBufferInfo() const override;
 
-  bool OnProducerPosted();
+  void OnProducerPosted();
   void OnProducerClosed();
 
  private:
diff --git a/services/vr/bufferhubd/include/private/dvr/producer_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
index 3ad9c70..4734439 100644
--- a/services/vr/bufferhubd/include/private/dvr/producer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
@@ -62,8 +62,7 @@
   pdx::Status<void> OnConsumerRelease(Message& message,
                                       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);
@@ -74,9 +73,6 @@
 
  private:
   std::vector<ConsumerChannel*> consumer_channels_;
-  // This counts the number of consumers left to process this buffer. If this is
-  // zero then the producer can re-acquire ownership.
-  int pending_consumers_;
 
   IonBuffer buffer_;
 
@@ -91,7 +87,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..ab1d94c 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -56,8 +56,6 @@
                                  uint64_t usage, size_t user_metadata_size,
                                  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) {
@@ -184,7 +182,7 @@
 
   return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(),
                     buffer_.height(), buffer_.layer_count(), buffer_.format(),
-                    buffer_.usage(), pending_consumers_,
+                    buffer_.usage(),
                     buffer_state_->load(std::memory_order_acquire),
                     signaled_mask, metadata_header_->queue_index);
 }
@@ -255,16 +253,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 +274,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 +309,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,22 +326,51 @@
       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();
   }
 
   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,18 +420,13 @@
            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
   // that no frame is silently dropped.
-  pending_consumers_ = 0;
   for (auto consumer : consumer_channels_) {
-    if (consumer->OnProducerPosted())
-      pending_consumers_++;
+    consumer->OnProducerPosted();
   }
-  ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers",
-           pending_consumers_);
 
   return {};
 }
@@ -419,23 +434,8 @@
 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) {
-    ALOGE(
-        "ProducerChannel::OnGain: Producer (id=%d) is gaining a buffer that "
-        "still has %d pending consumer(s).",
-        buffer_id(), pending_consumers_);
-    return ErrorStatus(EBUSY);
-  }
 
   ClearAvailable();
-  producer_owns_ = true;
   post_fence_.close();
   return {std::move(returned_fence_)};
 }
@@ -449,10 +449,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 +487,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 +501,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 +512,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) {
@@ -538,75 +531,53 @@
     }
   }
 
-  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));
-
+  uint64_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  if (BufferHubDefs::IsClientReleased(current_buffer_state,
+                                      ~orphaned_consumer_bit_mask_)) {
+    SignalAvailable();
     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_,
+          "%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;
     }
-
-    SignalAvailable();
   }
 
-  ALOGE_IF(
-      pending_consumers_ && BufferHubDefs::IsBufferReleased(
-                                buffer_state_->load(std::memory_order_acquire)),
-      "ProducerChannel::OnConsumerRelease: buffer state inconsistent: "
-      "pending_consumers=%d, buffer buffer is in releaed state.",
-      pending_consumers_);
   return {};
 }
 
-void ProducerChannel::DecrementPendingConsumers() {
-  if (pending_consumers_ == 0) {
-    ALOGE("ProducerChannel::DecrementPendingConsumers: no pending consumer.");
-    return;
+void ProducerChannel::OnConsumerOrphaned(const uint64_t& consumer_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;
+
+  uint64_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  if (BufferHubDefs::IsClientReleased(current_buffer_state,
+                                      ~orphaned_consumer_bit_mask_)) {
+    SignalAvailable();
   }
 
-  --pending_consumers_;
-  ALOGD_IF(TRACE,
-           "ProducerChannel::DecrementPendingConsumers: buffer_id=%d %d "
-           "consumers left",
-           buffer_id(), pending_consumers_);
-}
-
-void ProducerChannel::OnConsumerOrphaned(ConsumerChannel* channel) {
-  // 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;
-
   // 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 +591,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) {