Merge "TouchpadInputMapper: rotate pointer movement with display"
diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc
index 240aa49..5b08c77 100644
--- a/cmds/installd/installd.rc
+++ b/cmds/installd/installd.rc
@@ -1,6 +1,7 @@
 
 service installd /system/bin/installd
     class main
+    capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL SETGID SETUID SYS_ADMIN
 
 on early-boot
     mkdir /config/sdcardfs/extensions/1055
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 5fa9fda..09933d3 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -24,7 +24,6 @@
 #include <vector>
 
 #include <android/os/IInputConstants.h>
-#include "android/hardware/input/InputDeviceCountryCode.h"
 
 namespace android {
 
@@ -236,9 +235,7 @@
 
     void initialize(int32_t id, int32_t generation, int32_t controllerNumber,
                     const InputDeviceIdentifier& identifier, const std::string& alias,
-                    bool isExternal, bool hasMic,
-                    hardware::input::InputDeviceCountryCode countryCode =
-                            hardware::input::InputDeviceCountryCode::INVALID);
+                    bool isExternal, bool hasMic);
 
     inline int32_t getId() const { return mId; }
     inline int32_t getControllerNumber() const { return mControllerNumber; }
@@ -250,7 +247,6 @@
     }
     inline bool isExternal() const { return mIsExternal; }
     inline bool hasMic() const { return mHasMic; }
-    inline hardware::input::InputDeviceCountryCode getCountryCode() const { return mCountryCode; }
     inline uint32_t getSources() const { return mSources; }
 
     const MotionRange* getMotionRange(int32_t axis, uint32_t source) const;
@@ -310,7 +306,6 @@
     std::string mAlias;
     bool mIsExternal;
     bool mHasMic;
-    hardware::input::InputDeviceCountryCode mCountryCode;
     std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo;
     uint32_t mSources;
     int32_t mKeyboardType;
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 53a24af..e4c568e 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -566,9 +566,6 @@
 impl<'a> ReadableSubParcel<'a> {
     /// Read a type that implements [`Deserialize`] from the sub-parcel.
     pub fn read<D: Deserialize>(&self) -> Result<D> {
-        // The caller should have checked this,
-        // but it can't hurt to double-check
-        assert!(self.has_more_data());
         D::deserialize(&self.parcel)
     }
 
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index c241e4d..6f4c375 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -23,7 +23,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::ffi::c_void;
 use std::mem::{self, ManuallyDrop, MaybeUninit};
-use std::os::raw::{c_char, c_ulong};
+use std::os::raw::c_char;
 use std::ptr;
 use std::slice;
 
@@ -103,12 +103,8 @@
 unsafe extern "C" fn serialize_element<T: Serialize>(
     parcel: *mut sys::AParcel,
     array: *const c_void,
-    index: c_ulong,
+    index: usize,
 ) -> status_t {
-    // c_ulong and usize are the same, but we need the explicitly sized version
-    // so the function signature matches what bindgen generates.
-    let index = index as usize;
-
     let slice: &[T] = slice::from_raw_parts(array.cast(), index + 1);
 
     let mut parcel = match BorrowedParcel::from_raw(parcel) {
@@ -158,12 +154,8 @@
 unsafe extern "C" fn deserialize_element<T: Deserialize>(
     parcel: *const sys::AParcel,
     array: *mut c_void,
-    index: c_ulong,
+    index: usize,
 ) -> status_t {
-    // c_ulong and usize are the same, but we need the explicitly sized version
-    // so the function signature matches what bindgen generates.
-    let index = index as usize;
-
     let vec = &mut *(array as *mut Option<Vec<MaybeUninit<T>>>);
     let vec = match vec {
         Some(v) => v,
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8ddd18d..8f41cc1 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -26,7 +26,6 @@
 filegroup {
     name: "inputconstants_aidl",
     srcs: [
-        "android/hardware/input/InputDeviceCountryCode.aidl",
         "android/os/IInputConstants.aidl",
         "android/os/InputEventInjectionResult.aidl",
         "android/os/InputEventInjectionSync.aidl",
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index fb6c590..87333f2 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -26,7 +26,6 @@
 #include <input/InputEventLabels.h>
 
 using android::base::StringPrintf;
-using android::hardware::input::InputDeviceCountryCode;
 
 namespace android {
 
@@ -178,7 +177,6 @@
         mAlias(other.mAlias),
         mIsExternal(other.mIsExternal),
         mHasMic(other.mHasMic),
-        mCountryCode(other.mCountryCode),
         mKeyboardLayoutInfo(other.mKeyboardLayoutInfo),
         mSources(other.mSources),
         mKeyboardType(other.mKeyboardType),
@@ -197,7 +195,7 @@
 
 void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber,
                                  const InputDeviceIdentifier& identifier, const std::string& alias,
-                                 bool isExternal, bool hasMic, InputDeviceCountryCode countryCode) {
+                                 bool isExternal, bool hasMic) {
     mId = id;
     mGeneration = generation;
     mControllerNumber = controllerNumber;
@@ -205,7 +203,6 @@
     mAlias = alias;
     mIsExternal = isExternal;
     mHasMic = hasMic;
-    mCountryCode = countryCode;
     mSources = 0;
     mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
     mHasVibrator = false;
diff --git a/libs/input/android/hardware/input/InputDeviceCountryCode.aidl b/libs/input/android/hardware/input/InputDeviceCountryCode.aidl
deleted file mode 100644
index 6bb1a60..0000000
--- a/libs/input/android/hardware/input/InputDeviceCountryCode.aidl
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.input;
-
-/**
- * Constant for HID country code declared by a HID device. These constants are declared as AIDL to
- * be used by java and native input code.
- *
- * @hide
- */
-@Backing(type="int")
-enum InputDeviceCountryCode {
-    /**
-     * Used as default value where country code is not set in the device HID descriptor
-     */
-    INVALID = -1,
-
-    /**
-     * Used as default value when country code is not supported by the HID device. The HID
-     * descriptor sets "00" as the country code in this case.
-     */
-    NOT_SUPPORTED = 0,
-
-    /**
-     * Arabic
-     */
-    ARABIC = 1,
-
-    /**
-     * Belgian
-     */
-    BELGIAN = 2,
-
-    /**
-     * Canadian (Bilingual)
-     */
-    CANADIAN_BILINGUAL = 3,
-
-    /**
-     * Canadian (French)
-     */
-    CANADIAN_FRENCH = 4,
-
-    /**
-     * Czech Republic
-     */
-    CZECH_REPUBLIC = 5,
-
-    /**
-     * Danish
-     */
-    DANISH = 6,
-
-    /**
-     * Finnish
-     */
-    FINNISH = 7,
-
-    /**
-     * French
-     */
-    FRENCH = 8,
-
-    /**
-     * German
-     */
-    GERMAN = 9,
-
-    /**
-     * Greek
-     */
-    GREEK = 10,
-
-    /**
-     * Hebrew
-     */
-    HEBREW = 11,
-
-    /**
-     * Hungary
-     */
-    HUNGARY = 12,
-
-    /**
-     * International (ISO)
-     */
-    INTERNATIONAL = 13,
-
-    /**
-     * Italian
-     */
-    ITALIAN = 14,
-
-    /**
-     * Japan (Katakana)
-     */
-    JAPAN = 15,
-
-    /**
-     * Korean
-     */
-    KOREAN = 16,
-
-    /**
-     * Latin American
-     */
-    LATIN_AMERICAN = 17,
-
-    /**
-     * Netherlands (Dutch)
-     */
-    DUTCH = 18,
-
-    /**
-     * Norwegian
-     */
-    NORWEGIAN = 19,
-
-    /**
-     * Persian
-     */
-    PERSIAN = 20,
-
-    /**
-     * Poland
-     */
-    POLAND = 21,
-
-    /**
-     * Portuguese
-     */
-    PORTUGUESE = 22,
-
-    /**
-     * Russia
-     */
-    RUSSIA = 23,
-
-    /**
-     * Slovakia
-     */
-    SLOVAKIA = 24,
-
-    /**
-     * Spanish
-     */
-    SPANISH = 25,
-
-    /**
-     * Swedish
-     */
-    SWEDISH = 26,
-
-    /**
-     * Swiss (French)
-     */
-    SWISS_FRENCH = 27,
-
-    /**
-     * Swiss (German)
-     */
-    SWISS_GERMAN = 28,
-
-    /**
-     * Switzerland
-     */
-    SWITZERLAND = 29,
-
-    /**
-     * Taiwan
-     */
-    TAIWAN = 30,
-
-    /**
-     * Turkish_Q
-     */
-    TURKISH_Q = 31,
-
-    /**
-     * UK
-     */
-    UK = 32,
-
-    /**
-     * US
-     */
-    US = 33,
-
-    /**
-     * Yugoslavia
-     */
-    YUGOSLAVIA = 34,
-
-    /**
-     * Turkish_F
-     */
-    TURKISH_F = 35,
-}
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index 53fa8ce..1b69743 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -192,6 +192,31 @@
   return NO_ERROR;
 }
 
+/*
+ * Helper function copies the JPEG image from without EXIF.
+ *
+ * @param dest destination of the data to be written.
+ * @param source source of data being written.
+ * @param exif_pos position of the EXIF package, which is aligned with jpegdecoder.getEXIFPos().
+ *                 (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>).
+ * @param exif_size exif size without the initial 4 bytes, aligned with jpegdecoder.getEXIFSize().
+ */
+void copyJpegWithoutExif(jr_compressed_ptr dest,
+                         jr_compressed_ptr source,
+                         size_t exif_pos,
+                         size_t exif_size) {
+  memcpy(dest, source, sizeof(jpegr_compressed_struct));
+
+  const size_t exif_offset = 4; //exif_pos has 4 bypes offset to the FF sign
+  dest->length = source->length - exif_size - exif_offset;
+  dest->data = malloc(dest->length);
+
+  memcpy(dest->data, source->data, exif_pos - exif_offset);
+  memcpy((uint8_t*)dest->data + exif_pos - exif_offset,
+         (uint8_t*)source->data + exif_pos + exif_size,
+         source->length - exif_pos - exif_size);
+}
+
 /* Encode API-0 */
 status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                                   jpegr_transfer_function hdr_tf,
@@ -367,17 +392,18 @@
     return ERROR_JPEGR_DECODE_ERROR;
   }
 
+  // Update exif.
   jpegr_exif_struct exif;
   exif.data = nullptr;
   exif.length = 0;
-  // Delete EXIF package if it appears, and update exif.
+  jpegr_compressed_struct new_jpeg_image;
+  new_jpeg_image.data = nullptr;
+  new_jpeg_image.length = 0;
   if (jpeg_decoder.getEXIFPos() != 0) {
-    int new_length = compressed_jpeg_image->length - jpeg_decoder.getEXIFSize() - 4;
-    memcpy((uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - 4,
-           (uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos()
-                  + jpeg_decoder.getEXIFSize(),
-           compressed_jpeg_image->length - jpeg_decoder.getEXIFPos() - jpeg_decoder.getEXIFSize());
-    compressed_jpeg_image->length = new_length;
+    copyJpegWithoutExif(&new_jpeg_image,
+                        compressed_jpeg_image,
+                        jpeg_decoder.getEXIFPos(),
+                        jpeg_decoder.getEXIFSize());
     exif.data = jpeg_decoder.getEXIFPtr();
     exif.length = jpeg_decoder.getEXIFSize();
   }
@@ -395,7 +421,12 @@
   JPEGR_CHECK(updateExif(&exif, &new_exif));
 
   JPEGR_CHECK(appendRecoveryMap(
-          compressed_jpeg_image, &compressed_map, &new_exif, &metadata, dest));
+          new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image,
+          &compressed_map, &new_exif, &metadata, dest));
+
+  if (new_jpeg_image.data != nullptr) {
+    free(new_jpeg_image.data);
+  }
 
   return NO_ERROR;
 }
@@ -421,17 +452,18 @@
   uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
   uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
 
+  // Update exif.
   jpegr_exif_struct exif;
   exif.data = nullptr;
   exif.length = 0;
-  // Delete EXIF package if it appears, and update exif.
+  jpegr_compressed_struct new_jpeg_image;
+  new_jpeg_image.data = nullptr;
+  new_jpeg_image.length = 0;
   if (jpeg_decoder.getEXIFPos() != 0) {
-    int new_length = compressed_jpeg_image->length - jpeg_decoder.getEXIFSize() - 4;
-    memcpy((uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - 4,
-           (uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos()
-                  + jpeg_decoder.getEXIFSize(),
-           compressed_jpeg_image->length - jpeg_decoder.getEXIFPos() - jpeg_decoder.getEXIFSize());
-    compressed_jpeg_image->length = new_length;
+    copyJpegWithoutExif(&new_jpeg_image,
+                        compressed_jpeg_image,
+                        jpeg_decoder.getEXIFPos(),
+                        jpeg_decoder.getEXIFSize());
     exif.data = jpeg_decoder.getEXIFPtr();
     exif.length = jpeg_decoder.getEXIFSize();
   }
@@ -472,7 +504,12 @@
   JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
 
   JPEGR_CHECK(appendRecoveryMap(
-          compressed_jpeg_image, &compressed_map, &new_exif, &metadata, dest));
+          new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image,
+          &compressed_map, &new_exif, &metadata, dest));
+
+  if (new_jpeg_image.data != nullptr) {
+    free(new_jpeg_image.data);
+  }
 
   return NO_ERROR;
 }
@@ -864,7 +901,7 @@
     // 2 bytes: representing the length of the package
     // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
     // x bytes: length of xmp packet
-    const int length = 3 + nameSpaceLength + xmp.size();
+    const int length = 2 + nameSpaceLength + xmp.size();
     const uint8_t lengthH = ((length >> 8) & 0xff);
     const uint8_t lengthL = (length & 0xff);
     JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
diff --git a/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h b/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h
index 974e0fd..b95f011 100644
--- a/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h
+++ b/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h
@@ -23,7 +23,9 @@
 namespace mock {
 
 class FakeExternalTexture : public renderengine::ExternalTexture {
-    const sp<GraphicBuffer> mNullBuffer = nullptr;
+    const sp<GraphicBuffer> mEmptyBuffer =
+            sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
+                                    GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
     uint32_t mWidth;
     uint32_t mHeight;
     uint64_t mId;
@@ -34,7 +36,7 @@
     FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat,
                         uint64_t usage)
           : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {}
-    const sp<GraphicBuffer>& getBuffer() const { return mNullBuffer; }
+    const sp<GraphicBuffer>& getBuffer() const { return mEmptyBuffer; }
     bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
         return getId() == other.getId();
     }
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index e9fa599..b214750 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -62,7 +62,6 @@
 #define INDENT3 "      "
 
 using android::base::StringPrintf;
-using android::hardware::input::InputDeviceCountryCode;
 
 namespace android {
 
@@ -135,6 +134,46 @@
                                                                   {"green", LightColor::GREEN},
                                                                   {"blue", LightColor::BLUE}};
 
+// Mapping for country code to Layout info.
+// See bCountryCode in 6.2.1 of https://usb.org/sites/default/files/hid1_11.pdf.
+const std::unordered_map<std::int32_t, RawLayoutInfo> LAYOUT_INFOS =
+        {{0, RawLayoutInfo{.languageTag = "", .layoutType = ""}},             // NOT_SUPPORTED
+         {1, RawLayoutInfo{.languageTag = "ar-Arab", .layoutType = ""}},      // ARABIC
+         {2, RawLayoutInfo{.languageTag = "fr-BE", .layoutType = ""}},        // BELGIAN
+         {3, RawLayoutInfo{.languageTag = "fr-CA", .layoutType = ""}},        // CANADIAN_BILINGUAL
+         {4, RawLayoutInfo{.languageTag = "fr-CA", .layoutType = ""}},        // CANADIAN_FRENCH
+         {5, RawLayoutInfo{.languageTag = "cs", .layoutType = ""}},           // CZECH_REPUBLIC
+         {6, RawLayoutInfo{.languageTag = "da", .layoutType = ""}},           // DANISH
+         {7, RawLayoutInfo{.languageTag = "fi", .layoutType = ""}},           // FINNISH
+         {8, RawLayoutInfo{.languageTag = "fr-FR", .layoutType = ""}},        // FRENCH
+         {9, RawLayoutInfo{.languageTag = "de", .layoutType = ""}},           // GERMAN
+         {10, RawLayoutInfo{.languageTag = "el", .layoutType = ""}},          // GREEK
+         {11, RawLayoutInfo{.languageTag = "iw", .layoutType = ""}},          // HEBREW
+         {12, RawLayoutInfo{.languageTag = "hu", .layoutType = ""}},          // HUNGARY
+         {13, RawLayoutInfo{.languageTag = "en", .layoutType = "extended"}},  // INTERNATIONAL (ISO)
+         {14, RawLayoutInfo{.languageTag = "it", .layoutType = ""}},          // ITALIAN
+         {15, RawLayoutInfo{.languageTag = "ja", .layoutType = ""}},          // JAPAN
+         {16, RawLayoutInfo{.languageTag = "ko", .layoutType = ""}},          // KOREAN
+         {17, RawLayoutInfo{.languageTag = "es-419", .layoutType = ""}},      // LATIN_AMERICA
+         {18, RawLayoutInfo{.languageTag = "nl", .layoutType = ""}},          // DUTCH
+         {19, RawLayoutInfo{.languageTag = "nb", .layoutType = ""}},          // NORWEGIAN
+         {20, RawLayoutInfo{.languageTag = "fa", .layoutType = ""}},          // PERSIAN
+         {21, RawLayoutInfo{.languageTag = "pl", .layoutType = ""}},          // POLAND
+         {22, RawLayoutInfo{.languageTag = "pt", .layoutType = ""}},          // PORTUGUESE
+         {23, RawLayoutInfo{.languageTag = "ru", .layoutType = ""}},          // RUSSIA
+         {24, RawLayoutInfo{.languageTag = "sk", .layoutType = ""}},          // SLOVAKIA
+         {25, RawLayoutInfo{.languageTag = "es-ES", .layoutType = ""}},       // SPANISH
+         {26, RawLayoutInfo{.languageTag = "sv", .layoutType = ""}},          // SWEDISH
+         {27, RawLayoutInfo{.languageTag = "fr-CH", .layoutType = ""}},       // SWISS_FRENCH
+         {28, RawLayoutInfo{.languageTag = "de-CH", .layoutType = ""}},       // SWISS_GERMAN
+         {29, RawLayoutInfo{.languageTag = "de-CH", .layoutType = ""}},       // SWITZERLAND
+         {30, RawLayoutInfo{.languageTag = "zh-TW", .layoutType = ""}},       // TAIWAN
+         {31, RawLayoutInfo{.languageTag = "tr", .layoutType = "turkish_q"}}, // TURKISH_Q
+         {32, RawLayoutInfo{.languageTag = "en-GB", .layoutType = ""}},       // UK
+         {33, RawLayoutInfo{.languageTag = "en-US", .layoutType = ""}},       // US
+         {34, RawLayoutInfo{.languageTag = "", .layoutType = ""}},            // YUGOSLAVIA
+         {35, RawLayoutInfo{.languageTag = "tr", .layoutType = "turkish_f"}}}; // TURKISH_F
+
 static std::string sha1(const std::string& in) {
     SHA_CTX ctx;
     SHA1_Init(&ctx);
@@ -311,22 +350,27 @@
 }
 
 /**
- * Read country code information exposed through the sysfs path.
+ * Read country code information exposed through the sysfs path and convert it to Layout info.
  */
-static InputDeviceCountryCode readCountryCodeLocked(const std::filesystem::path& sysfsRootPath) {
+static std::optional<RawLayoutInfo> readLayoutConfiguration(
+        const std::filesystem::path& sysfsRootPath) {
     // Check the sysfs root path
-    int hidCountryCode = static_cast<int>(InputDeviceCountryCode::INVALID);
+    int32_t hidCountryCode = -1;
     std::string str;
     if (base::ReadFileToString(sysfsRootPath / "country", &str)) {
         hidCountryCode = std::stoi(str, nullptr, 16);
+        // Update this condition if new supported country codes are added to HID spec.
         if (hidCountryCode > 35 || hidCountryCode < 0) {
             ALOGE("HID country code should be in range [0, 35], but for sysfs path %s it was %d",
                   sysfsRootPath.c_str(), hidCountryCode);
-            return InputDeviceCountryCode::INVALID;
         }
     }
+    const auto it = LAYOUT_INFOS.find(hidCountryCode);
+    if (it != LAYOUT_INFOS.end()) {
+        return it->second;
+    }
 
-    return static_cast<InputDeviceCountryCode>(hidCountryCode);
+    return std::nullopt;
 }
 
 /**
@@ -1299,13 +1343,13 @@
     }
 }
 
-InputDeviceCountryCode EventHub::getCountryCode(int32_t deviceId) const {
+std::optional<RawLayoutInfo> EventHub::getRawLayoutInfo(int32_t deviceId) const {
     std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device == nullptr || !device->associatedDevice) {
-        return InputDeviceCountryCode::INVALID;
+        return std::nullopt;
     }
-    return device->associatedDevice->countryCode;
+    return device->associatedDevice->layoutInfo;
 }
 
 void EventHub::setExcludedDevices(const std::vector<std::string>& devices) {
@@ -1449,9 +1493,9 @@
 
     std::shared_ptr<const AssociatedDevice> associatedDevice = std::make_shared<AssociatedDevice>(
             AssociatedDevice{.sysfsRootPath = path,
-                             .countryCode = readCountryCodeLocked(path),
                              .batteryInfos = readBatteryConfiguration(path),
-                             .lightInfos = readLightsConfiguration(path)});
+                             .lightInfos = readLightsConfiguration(path),
+                             .layoutInfo = readLayoutConfiguration(path)});
 
     bool associatedDeviceChanged = false;
     for (const auto& [id, dev] : mDevices) {
@@ -2686,9 +2730,12 @@
                                  device->keyMap.keyLayoutFile.c_str());
             dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
                                  device->keyMap.keyCharacterMapFile.c_str());
-            dump += StringPrintf(INDENT3 "CountryCode: %d\n",
-                                 device->associatedDevice ? device->associatedDevice->countryCode
-                                                          : InputDeviceCountryCode::INVALID);
+            if (device->associatedDevice && device->associatedDevice->layoutInfo) {
+                dump += StringPrintf(INDENT3 "LanguageTag: %s\n",
+                                     device->associatedDevice->layoutInfo->languageTag.c_str());
+                dump += StringPrintf(INDENT3 "LayoutType: %s\n",
+                                     device->associatedDevice->layoutInfo->layoutType.c_str());
+            }
             dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
                                  device->configurationFile.c_str());
             dump += StringPrintf(INDENT3 "VideoDevice: %s\n",
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 13f40ee..6e78e82 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -37,8 +37,6 @@
 #include "TouchpadInputMapper.h"
 #include "VibratorInputMapper.h"
 
-using android::hardware::input::InputDeviceCountryCode;
-
 namespace android {
 
 InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
@@ -256,7 +254,6 @@
     mSources = 0;
     mClasses = ftl::Flags<InputDeviceClass>(0);
     mControllerNumber = 0;
-    mCountryCode = InputDeviceCountryCode::INVALID;
 
     for_each_subdevice([this](InputDeviceContext& context) {
         mClasses |= context.getDeviceClasses();
@@ -268,16 +265,6 @@
             }
             mControllerNumber = controllerNumber;
         }
-
-        InputDeviceCountryCode countryCode = context.getCountryCode();
-        if (countryCode != InputDeviceCountryCode::INVALID) {
-            if (mCountryCode != InputDeviceCountryCode::INVALID && mCountryCode != countryCode) {
-                ALOGW("InputDevice::configure(): %s device contains multiple unique country "
-                      "codes",
-                      getName().c_str());
-            }
-            mCountryCode = countryCode;
-        }
     });
 
     mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
@@ -465,7 +452,7 @@
 InputDeviceInfo InputDevice::getDeviceInfo() {
     InputDeviceInfo outDeviceInfo;
     outDeviceInfo.initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
-                             mHasMic, mCountryCode);
+                             mHasMic);
     for_each_mapper(
             [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(&outDeviceInfo); });
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 8a844b2..a3ecf41 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -42,7 +42,6 @@
 
 #include "TouchVideoDevice.h"
 #include "VibrationElement.h"
-#include "android/hardware/input/InputDeviceCountryCode.h"
 
 struct inotify_event;
 
@@ -207,6 +206,15 @@
     bool operator!=(const RawBatteryInfo&) const = default;
 };
 
+/* Layout information associated with the device */
+struct RawLayoutInfo {
+    std::string languageTag;
+    std::string layoutType;
+
+    bool operator==(const RawLayoutInfo&) const = default;
+    bool operator!=(const RawLayoutInfo&) const = default;
+};
+
 /*
  * Gets the class that owns an axis, in cases where multiple classes might claim
  * the same axis for different purposes.
@@ -308,8 +316,8 @@
             int32_t deviceId, int32_t lightId) const = 0;
     virtual void setLightIntensities(int32_t deviceId, int32_t lightId,
                                      std::unordered_map<LightColor, int32_t> intensities) = 0;
-    /* Query Country code associated with the input device. */
-    virtual hardware::input::InputDeviceCountryCode getCountryCode(int32_t deviceId) const = 0;
+    /* Query Layout info associated with the input device. */
+    virtual std::optional<RawLayoutInfo> getRawLayoutInfo(int32_t deviceId) const = 0;
     /* Query current input state. */
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
     virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
@@ -493,7 +501,7 @@
     void setLightIntensities(int32_t deviceId, int32_t lightId,
                              std::unordered_map<LightColor, int32_t> intensities) override final;
 
-    hardware::input::InputDeviceCountryCode getCountryCode(int32_t deviceId) const override final;
+    std::optional<RawLayoutInfo> getRawLayoutInfo(int32_t deviceId) const override final;
 
     void setExcludedDevices(const std::vector<std::string>& devices) override final;
 
@@ -556,9 +564,9 @@
     struct AssociatedDevice {
         // The sysfs root path of the misc device.
         std::filesystem::path sysfsRootPath;
-        hardware::input::InputDeviceCountryCode countryCode;
         std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos;
         std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos;
+        std::optional<RawLayoutInfo> layoutInfo;
 
         bool operator==(const AssociatedDevice&) const = default;
         bool operator!=(const AssociatedDevice&) const = default;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index b173618..7867029 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -167,7 +167,6 @@
     int32_t mId;
     int32_t mGeneration;
     int32_t mControllerNumber;
-    hardware::input::InputDeviceCountryCode mCountryCode;
     InputDeviceIdentifier mIdentifier;
     std::string mAlias;
     ftl::Flags<InputDeviceClass> mClasses;
@@ -326,9 +325,6 @@
     }
 
     inline std::vector<TouchVideoFrame> getVideoFrames() { return mEventHub->getVideoFrames(mId); }
-    inline hardware::input::InputDeviceCountryCode getCountryCode() const {
-        return mEventHub->getCountryCode(mId);
-    }
     inline int32_t getScanCodeState(int32_t scanCode) const {
         return mEventHub->getScanCodeState(mId, scanCode);
     }
@@ -361,6 +357,9 @@
     inline bool setKeyboardLayoutOverlay(std::shared_ptr<KeyCharacterMap> map) {
         return mEventHub->setKeyboardLayoutOverlay(mId, map);
     }
+    inline const std::optional<RawLayoutInfo> getRawLayoutInfo() {
+        return mEventHub->getRawLayoutInfo(mId);
+    }
     inline void vibrate(const VibrationElement& element) {
         return mEventHub->vibrate(mId, element);
     }
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 6f01449..d147d60 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -123,6 +123,12 @@
 
     if (mKeyboardLayoutInfo) {
         info->setKeyboardLayoutInfo(*mKeyboardLayoutInfo);
+    } else {
+        std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo();
+        if (layoutInfo) {
+            info->setKeyboardLayoutInfo(
+                    KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType));
+        }
     }
 }
 
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 289a780..6ac0bfb 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -120,8 +120,8 @@
     getDevice(deviceId)->keyCodeStates.replaceValueFor(keyCode, state);
 }
 
-void FakeEventHub::setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) {
-    getDevice(deviceId)->countryCode = countryCode;
+void FakeEventHub::setRawLayoutInfo(int32_t deviceId, RawLayoutInfo info) {
+    getDevice(deviceId)->layoutInfo = info;
 }
 
 void FakeEventHub::setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
@@ -389,9 +389,9 @@
     return AKEY_STATE_UNKNOWN;
 }
 
-InputDeviceCountryCode FakeEventHub::getCountryCode(int32_t deviceId) const {
+std::optional<RawLayoutInfo> FakeEventHub::getRawLayoutInfo(int32_t deviceId) const {
     Device* device = getDevice(deviceId);
-    return device ? device->countryCode : InputDeviceCountryCode::INVALID;
+    return device ? device->layoutInfo : std::nullopt;
 }
 
 int32_t FakeEventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index fb3c859..72f8ac0 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -30,10 +30,6 @@
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 
-#include "android/hardware/input/InputDeviceCountryCode.h"
-
-using android::hardware::input::InputDeviceCountryCode;
-
 namespace android {
 
 class FakeEventHub : public EventHubInterface {
@@ -67,7 +63,7 @@
         BitArray<MSC_MAX> mscBitmask;
         std::vector<VirtualKeyDefinition> virtualKeys;
         bool enabled;
-        InputDeviceCountryCode countryCode;
+        std::optional<RawLayoutInfo> layoutInfo;
 
         status_t enable() {
             enabled = true;
@@ -124,7 +120,7 @@
     void addRelativeAxis(int32_t deviceId, int32_t axis);
     void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value);
 
-    void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode);
+    void setRawLayoutInfo(int32_t deviceId, RawLayoutInfo info);
 
     void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state);
     void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state);
@@ -180,7 +176,7 @@
     std::vector<RawEvent> getEvents(int) override;
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
     int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
-    InputDeviceCountryCode getCountryCode(int32_t deviceId) const override;
+    std::optional<RawLayoutInfo> getRawLayoutInfo(int32_t deviceId) const override;
     int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
     int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
     status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 96d27b8..a464639 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -48,12 +48,9 @@
 #include "InputMapperTest.h"
 #include "InstrumentedInputReader.h"
 #include "TestConstants.h"
-#include "android/hardware/input/InputDeviceCountryCode.h"
 #include "input/DisplayViewport.h"
 #include "input/Input.h"
 
-using android::hardware::input::InputDeviceCountryCode;
-
 namespace android {
 
 using namespace ftl::flag_operators;
@@ -2240,17 +2237,6 @@
     ASSERT_EQ(ftl::Flags<InputDeviceClass>(0), mDevice->getClasses());
 }
 
-TEST_F(InputDeviceTest, CountryCodeCorrectlyMapped) {
-    mFakeEventHub->setCountryCode(EVENTHUB_ID, InputDeviceCountryCode::INTERNATIONAL);
-
-    // Configuration
-    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
-    InputReaderConfiguration config;
-    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
-
-    ASSERT_EQ(InputDeviceCountryCode::INTERNATIONAL, mDevice->getDeviceInfo().getCountryCode());
-}
-
 TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) {
     ASSERT_EQ(mDevice->isEnabled(), false);
 }
@@ -3600,6 +3586,20 @@
               deviceInfo.getKeyboardLayoutInfo()->layoutType);
 }
 
+TEST_F(KeyboardInputMapperTest, LayoutInfoCorrectlyMapped) {
+    mFakeEventHub->setRawLayoutInfo(EVENTHUB_ID,
+                                    RawLayoutInfo{.languageTag = "en", .layoutType = "extended"});
+
+    // Configuration
+    addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                               AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    InputReaderConfiguration config;
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
+
+    ASSERT_EQ("en", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->languageTag);
+    ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType);
+}
+
 // --- KeyboardInputMapperTest_ExternalDevice ---
 
 class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index a0910ea..7c9be5c 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -19,9 +19,6 @@
 #include <InputMapper.h>
 #include <InputReader.h>
 #include <ThreadSafeFuzzedDataProvider.h>
-#include "android/hardware/input/InputDeviceCountryCode.h"
-
-using android::hardware::input::InputDeviceCountryCode;
 
 constexpr size_t kValidTypes[] = {EV_SW,
                                   EV_SYN,
@@ -65,46 +62,6 @@
         BTN_TASK,
 };
 
-constexpr InputDeviceCountryCode kCountryCodes[] = {
-        InputDeviceCountryCode::INVALID,
-        InputDeviceCountryCode::NOT_SUPPORTED,
-        InputDeviceCountryCode::ARABIC,
-        InputDeviceCountryCode::BELGIAN,
-        InputDeviceCountryCode::CANADIAN_BILINGUAL,
-        InputDeviceCountryCode::CANADIAN_FRENCH,
-        InputDeviceCountryCode::CZECH_REPUBLIC,
-        InputDeviceCountryCode::DANISH,
-        InputDeviceCountryCode::FINNISH,
-        InputDeviceCountryCode::FRENCH,
-        InputDeviceCountryCode::GERMAN,
-        InputDeviceCountryCode::GREEK,
-        InputDeviceCountryCode::HEBREW,
-        InputDeviceCountryCode::HUNGARY,
-        InputDeviceCountryCode::INTERNATIONAL,
-        InputDeviceCountryCode::ITALIAN,
-        InputDeviceCountryCode::JAPAN,
-        InputDeviceCountryCode::KOREAN,
-        InputDeviceCountryCode::LATIN_AMERICAN,
-        InputDeviceCountryCode::DUTCH,
-        InputDeviceCountryCode::NORWEGIAN,
-        InputDeviceCountryCode::PERSIAN,
-        InputDeviceCountryCode::POLAND,
-        InputDeviceCountryCode::PORTUGUESE,
-        InputDeviceCountryCode::RUSSIA,
-        InputDeviceCountryCode::SLOVAKIA,
-        InputDeviceCountryCode::SPANISH,
-        InputDeviceCountryCode::SWEDISH,
-        InputDeviceCountryCode::SWISS_FRENCH,
-        InputDeviceCountryCode::SWISS_GERMAN,
-        InputDeviceCountryCode::SWITZERLAND,
-        InputDeviceCountryCode::TAIWAN,
-        InputDeviceCountryCode::TURKISH_Q,
-        InputDeviceCountryCode::UK,
-        InputDeviceCountryCode::US,
-        InputDeviceCountryCode::YUGOSLAVIA,
-        InputDeviceCountryCode::TURKISH_F,
-};
-
 constexpr size_t kMaxSize = 256;
 
 namespace android {
@@ -197,8 +154,8 @@
     void setLightIntensities(int32_t deviceId, int32_t lightId,
                              std::unordered_map<LightColor, int32_t> intensities) override{};
 
-    InputDeviceCountryCode getCountryCode(int32_t deviceId) const override {
-        return mFdp->PickValueInArray<InputDeviceCountryCode>(kCountryCodes);
+    std::optional<RawLayoutInfo> getRawLayoutInfo(int32_t deviceId) const override {
+        return std::nullopt;
     };
 
     int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 2a5bfae..b86782f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -137,6 +137,7 @@
         HwcBufferCache hwcBufferCache;
 
         // The previously-active buffer for this layer.
+        uint64_t activeBufferId;
         uint32_t activeBufferSlot;
 
         // Set to true when overridden info has been sent to HW composer
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index 34ed214..f0105b2 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -28,11 +28,6 @@
 }
 
 HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) {
-    // TODO(b/261930578): This is for unit tests which don't mock GraphicBuffers but instead send
-    // in nullptrs.
-    if (buffer == nullptr) {
-        return {0, nullptr};
-    }
     if (auto i = mCacheByBufferId.find(buffer->getId()); i != mCacheByBufferId.end()) {
         Cache& cache = i->second;
         // mark this cache slot as more recently used so it won't get evicted anytime soon
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 766d7ea..60a2c83 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -617,13 +617,24 @@
         return;
     }
 
+    // Uncache the active buffer last so that it's the first buffer to be purged from the cache
+    // next time a buffer is sent to this layer.
+    bool uncacheActiveBuffer = false;
+
     std::vector<uint32_t> slotsToClear;
     for (uint64_t bufferId : bufferIdsToUncache) {
-        uint32_t slot = state.hwc->hwcBufferCache.uncache(bufferId);
-        if (slot != UINT32_MAX) {
-            slotsToClear.push_back(slot);
+        if (bufferId == state.hwc->activeBufferId) {
+            uncacheActiveBuffer = true;
+        } else {
+            uint32_t slot = state.hwc->hwcBufferCache.uncache(bufferId);
+            if (slot != UINT32_MAX) {
+                slotsToClear.push_back(slot);
+            }
         }
     }
+    if (uncacheActiveBuffer) {
+        slotsToClear.push_back(state.hwc->hwcBufferCache.uncache(state.hwc->activeBufferId));
+    }
 
     hal::Error error =
             state.hwc->hwcLayer->setBufferSlotsToClear(slotsToClear, state.hwc->activeBufferSlot);
@@ -655,17 +666,20 @@
             hwcSlotAndBuffer = state.hwc->hwcBufferCache.getOverrideHwcSlotAndBuffer(
                     state.overrideInfo.buffer->getBuffer());
             hwcFence = state.overrideInfo.acquireFence;
+            // Keep track of the active buffer ID so when it's discarded we uncache it last so its
+            // slot will be used first, allowing the memory to be freed as soon as possible.
+            state.hwc->activeBufferId = state.overrideInfo.buffer->getBuffer()->getId();
         } else {
             hwcSlotAndBuffer =
                     state.hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer);
             hwcFence = outputIndependentState.acquireFence;
+            // Keep track of the active buffer ID so when it's discarded we uncache it last so its
+            // slot will be used first, allowing the memory to be freed as soon as possible.
+            state.hwc->activeBufferId = outputIndependentState.buffer->getId();
         }
-
         // Keep track of the active buffer slot, so we can restore it after clearing other buffer
         // slots.
-        if (hwcSlotAndBuffer.buffer) {
-            state.hwc->activeBufferSlot = hwcSlotAndBuffer.slot;
-        }
+        state.hwc->activeBufferSlot = hwcSlotAndBuffer.slot;
     }
 
     if (auto error = hwcLayer->setBuffer(hwcSlotAndBuffer.slot, hwcSlotAndBuffer.buffer, hwcFence);
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 0edc226..9ad2edb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -1318,6 +1318,7 @@
 struct OutputLayerUncacheBufferTest : public OutputLayerTest {
     static const sp<GraphicBuffer> kBuffer1;
     static const sp<GraphicBuffer> kBuffer2;
+    static const sp<GraphicBuffer> kBuffer3;
     static const sp<Fence> kFence;
 
     OutputLayerUncacheBufferTest() {
@@ -1343,6 +1344,10 @@
         sp<GraphicBuffer>::make(2, 3, PIXEL_FORMAT_RGBA_8888,
                                 AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
                                         AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
+const sp<GraphicBuffer> OutputLayerUncacheBufferTest::kBuffer3 =
+        sp<GraphicBuffer>::make(4, 5, PIXEL_FORMAT_RGBA_8888,
+                                AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+                                        AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
 const sp<Fence> OutputLayerUncacheBufferTest::kFence = sp<Fence>::make();
 
 TEST_F(OutputLayerUncacheBufferTest, canUncacheAndReuseSlot) {
@@ -1360,26 +1365,38 @@
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
     Mock::VerifyAndClearExpectations(&mHwcLayer);
 
-    // buffer slots are cleared in HWC
-    std::vector<uint32_t> slotsToClear = {0, 1};
-    EXPECT_CALL(mHwcLayer,
-                setBufferSlotsToClear(/*slotsToClear*/ slotsToClear, /*activeBufferSlot*/ 1));
-    mOutputLayer.uncacheBuffers({kBuffer1->getId(), kBuffer2->getId()});
+    // Buffer3 is stored in slot 2
+    mLayerFEState.buffer = kBuffer3;
+    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 2, kBuffer3, kFence));
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
     Mock::VerifyAndClearExpectations(&mHwcLayer);
 
-    // rather than allocating a new slot, the active buffer slot (slot 1) is reused first to free
-    // the memory as soon as possible
+    // Buffer2 becomes the active buffer again (with a nullptr) and reuses slot 1
+    mLayerFEState.buffer = kBuffer2;
+    sp<GraphicBuffer> nullBuffer = nullptr;
+    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, nullBuffer, kFence));
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+    Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+    // Buffer slots are cleared
+    std::vector<uint32_t> slotsToClear = {0, 2, 1}; // order doesn't matter
+    EXPECT_CALL(mHwcLayer, setBufferSlotsToClear(slotsToClear, /*activeBufferSlot*/ 1));
+    // Uncache the active buffer in between other buffers to exercise correct algorithmic behavior.
+    mOutputLayer.uncacheBuffers({kBuffer1->getId(), kBuffer2->getId(), kBuffer3->getId()});
+    Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+    // Buffer1 becomes active again, and rather than allocating a new slot, or re-using slot 0,
+    // the active buffer slot (slot 1 for Buffer2) is reused first, which allows HWC to free the
+    // memory for the active buffer. Note: slot 1 is different from the first and last buffer slot
+    // requested to be cleared in slotsToClear (slot 1), above, indicating that the algorithm
+    // correctly identifies the active buffer as the buffer in slot 1, despite ping-ponging.
     mLayerFEState.buffer = kBuffer1;
     EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer1, kFence));
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
     Mock::VerifyAndClearExpectations(&mHwcLayer);
-
-    // rather than allocating a new slot, slot 0 is reused
-    mLayerFEState.buffer = kBuffer2;
-    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 0, kBuffer2, kFence));
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
-                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 /*
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index f1a6c0e..ab98dbf 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -27,7 +27,6 @@
 #include <log/log.h>
 #include <mock/MockEventThread.h>
 #include <renderengine/ExternalTexture.h>
-#include <renderengine/mock/FakeExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <utils/String16.h>
 #include <string>
@@ -95,6 +94,30 @@
     }
 };
 
+class FakeExternalTexture : public renderengine::ExternalTexture {
+    const sp<GraphicBuffer> mNullBuffer = nullptr;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint64_t mId;
+    PixelFormat mPixelFormat;
+    uint64_t mUsage;
+
+public:
+    FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat,
+                        uint64_t usage)
+          : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {}
+    const sp<GraphicBuffer>& getBuffer() const { return mNullBuffer; }
+    bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+        return getId() == other.getId();
+    }
+    uint32_t getWidth() const override { return mWidth; }
+    uint32_t getHeight() const override { return mHeight; }
+    uint64_t getId() const override { return mId; }
+    PixelFormat getPixelFormat() const override { return mPixelFormat; }
+    uint64_t getUsage() const override { return mUsage; }
+    ~FakeExternalTexture() = default;
+};
+
 class MockSurfaceFlinger : public SurfaceFlinger {
 public:
     MockSurfaceFlinger(Factory& factory)
@@ -102,12 +125,10 @@
     std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
             BufferData& bufferData, const char* /* layerName */,
             uint64_t /* transactionId */) override {
-        return std::make_shared<renderengine::mock::FakeExternalTexture>(bufferData.getWidth(),
-                                                                         bufferData.getHeight(),
-                                                                         bufferData.getId(),
-                                                                         bufferData
-                                                                                 .getPixelFormat(),
-                                                                         bufferData.getUsage());
+        return std::make_shared<FakeExternalTexture>(bufferData.getWidth(), bufferData.getHeight(),
+                                                     bufferData.getId(),
+                                                     bufferData.getPixelFormat(),
+                                                     bufferData.getUsage());
     };
 
     // b/220017192 migrate from transact codes to ISurfaceComposer apis
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 06b9caa..ba77600 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -182,7 +182,9 @@
     sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
     std::vector<sp<Layer>> mAuxiliaryLayers;
 
-    sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make();
+    sp<GraphicBuffer> mBuffer =
+            sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
+                                    GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
     ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer();
 
     Hwc2::mock::Composer* mComposer = nullptr;
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index d58e644..223f4db 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -115,7 +115,9 @@
 
     TestableSurfaceFlinger mFlinger;
     sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
-    sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make();
+    sp<GraphicBuffer> mBuffer =
+            sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
+                                    GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
     Hwc2::mock::PowerAdvisor mPowerAdvisor;
 
     FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow};
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index c9fefe3..a78f42c 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -2508,10 +2508,6 @@
 // b/190578904
 TEST_P(RefreshRateSelectorTest,
        getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_Heuristic) {
-    if (g_noSlowTests) {
-        GTEST_SKIP();
-    }
-
     const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
     constexpr int kMaxRefreshRate = 240;
 
@@ -2540,10 +2536,6 @@
 }
 TEST_P(RefreshRateSelectorTest,
        getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_ExplicitDefault) {
-    if (g_noSlowTests) {
-        GTEST_SKIP();
-    }
-
     const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
     constexpr int kMaxRefreshRate = 240;
 
@@ -2572,10 +2564,6 @@
 }
 TEST_P(RefreshRateSelectorTest,
        getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_ExplicitExactOrMultiple) {
-    if (g_noSlowTests) {
-        GTEST_SKIP();
-    }
-
     const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
     constexpr int kMaxRefreshRate = 240;
 
@@ -2604,10 +2592,6 @@
 }
 TEST_P(RefreshRateSelectorTest,
        getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_ExplicitExact) {
-    if (g_noSlowTests) {
-        GTEST_SKIP();
-    }
-
     const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
     constexpr int kMaxRefreshRate = 240;