Merge "Remove "explicit" from HardwareBuffer move constructor"
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index a80da4e..12a7cff 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -8,6 +8,7 @@
socket dumpstate stream 0660 shell log
disabled
oneshot
+ capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG
# dumpstatez generates a zipped bugreport but also uses a socket to print the file location once
# it is finished.
@@ -16,9 +17,11 @@
class main
disabled
oneshot
+ capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG
# bugreportd starts dumpstate binder service and makes it wait for a listener to connect.
service bugreportd /system/bin/dumpstate -w
class main
disabled
oneshot
+ capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG
diff --git a/include/android/input.h b/include/android/input.h
index 5d19c5c..a0b46de 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -870,6 +870,14 @@
* The current event stream represents the user swiping with two fingers on a touchpad.
*/
AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE = 3,
+ /**
+ * Classification constant: multi-finger swipe.
+ *
+ * The current event stream represents the user swiping with three or more fingers on a
+ * touchpad. Unlike two-finger swipes, these are only to be handled by the system UI, which is
+ * why they have a separate constant from two-finger swipes.
+ */
+ AMOTION_EVENT_CLASSIFICATION_MULTI_FINGER_SWIPE = 4,
};
/**
diff --git a/include/input/Input.h b/include/input/Input.h
index 1a35196..7e62ac0 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -302,6 +302,12 @@
* The current gesture represents the user swiping with two fingers on a touchpad.
*/
TWO_FINGER_SWIPE = AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE,
+ /**
+ * The current gesture represents the user swiping with three or more fingers on a touchpad.
+ * Unlike two-finger swipes, these are only to be handled by the system UI, which is why they
+ * have a separate constant from two-finger swipes.
+ */
+ MULTI_FINGER_SWIPE = AMOTION_EVENT_CLASSIFICATION_MULTI_FINGER_SWIPE,
};
/**
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/tests/Android.bp b/libs/binder/tests/Android.bp
index a999d59..5db3187 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -100,6 +100,7 @@
"binderBinderUnitTest.cpp",
"binderStatusUnitTest.cpp",
"binderMemoryHeapBaseUnitTest.cpp",
+ "binderRecordedTransactionTest.cpp",
],
shared_libs: [
"libbinder",
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index 6a6e008..bc40864 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -180,7 +180,11 @@
mallocs++;
// Happens to be SM package length. We could switch to forking
// and registering our own service if it became an issue.
+#if defined(__LP64__)
EXPECT_EQ(bytes, 78);
+#else
+ EXPECT_EQ(bytes, 70);
+#endif
});
a_binder->getInterfaceDescriptor();
diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp
new file mode 100644
index 0000000..23864e6
--- /dev/null
+++ b/libs/binder/tests/binderRecordedTransactionTest.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#include <binder/BinderRecordReplay.h>
+#include <gtest/gtest.h>
+
+using android::Parcel;
+using android::status_t;
+using android::base::unique_fd;
+using android::binder::debug::RecordedTransaction;
+
+TEST(BinderRecordedTransaction, RoundTripEncoding) {
+ Parcel d;
+ d.writeInt32(12);
+ d.writeInt64(2);
+ Parcel r;
+ r.writeInt32(99);
+ auto transaction = RecordedTransaction::fromDetails(1, 42, d, r, 0);
+
+ auto file = std::tmpfile();
+ auto fd = unique_fd(fcntl(fileno(file), F_DUPFD, 1));
+
+ status_t status = transaction->dumpToFile(fd);
+ ASSERT_EQ(android::NO_ERROR, status);
+
+ std::rewind(file);
+
+ auto retrievedTransaction = RecordedTransaction::fromFile(fd);
+
+ EXPECT_EQ(retrievedTransaction->getCode(), 1);
+ EXPECT_EQ(retrievedTransaction->getFlags(), 42);
+ EXPECT_EQ(retrievedTransaction->getDataSize(), 12);
+ EXPECT_EQ(retrievedTransaction->getReplySize(), 4);
+ EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0);
+ EXPECT_EQ(retrievedTransaction->getVersion(), 0);
+
+ EXPECT_EQ(retrievedTransaction->getDataParcel().readInt32(), 12);
+ EXPECT_EQ(retrievedTransaction->getDataParcel().readInt64(), 2);
+ EXPECT_EQ(retrievedTransaction->getReplyParcel().readInt32(), 99);
+}
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 53e7de4..d32cd80 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -199,5 +199,23 @@
binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) {
+ FUZZ_LOG() << "about to marshal AParcel";
+ size_t start = provider.ConsumeIntegral<size_t>();
+ // limit 1MB to avoid OOM issues
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1000000);
+ uint8_t buffer[len];
+ binder_status_t status = AParcel_marshal(p.aParcel(), buffer, start, len);
+ FUZZ_LOG() << "status: " << status;
+ },
+ [](const NdkParcelAdapter& /*p*/, FuzzedDataProvider& provider) {
+ FUZZ_LOG() << "about to unmarshal AParcel";
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
+ std::vector<uint8_t> parcelData = provider.ConsumeBytes<uint8_t>(len);
+ const uint8_t* buffer = parcelData.data();
+ binder_status_t status = AParcel_unmarshal(AParcel_create(), buffer, len);
+ FUZZ_LOG() << "status: " << status;
+ },
+
};
// clang-format on
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/Input.cpp b/libs/input/Input.cpp
index d893cb9..000775b 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -72,6 +72,8 @@
return "DEEP_PRESS";
case MotionClassification::TWO_FINGER_SWIPE:
return "TWO_FINGER_SWIPE";
+ case MotionClassification::MULTI_FINGER_SWIPE:
+ return "MULTI_FINGER_SWIPE";
}
}
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/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
index 39c79c9..5993546 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
@@ -36,11 +36,11 @@
JpegDecoder();
~JpegDecoder();
/*
- * Decompresses JPEG image to raw image (YUV420planer or grey-scale) format. After calling
- * this method, call getDecompressedImage() to get the image.
+ * Decompresses JPEG image to raw image (YUV420planer, grey-scale or RGBA) format. After
+ * calling this method, call getDecompressedImage() to get the image.
* Returns false if decompressing the image fails.
*/
- bool decompressImage(const void* image, int length);
+ bool decompressImage(const void* image, int length, bool decodeToRGBA = false);
/*
* Returns the decompressed raw image buffer pointer. This method must be called only after
* calling decompressImage().
@@ -98,10 +98,11 @@
bool extractEXIF(const void* image, int length);
private:
- bool decode(const void* image, int length);
+ bool decode(const void* image, int length, bool decodeToRGBA);
// Returns false if errors occur.
bool decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, bool isSingleChannel);
bool decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest);
+ bool decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest);
bool decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest);
// Process 16 lines of Y and 16 lines of U/V each time.
// We must pass at least 16 scanlines according to libjpeg documentation.
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index 905bf16..05b1421 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -339,11 +339,11 @@
/*
* This method will tone map a HDR image to an SDR image.
*
- * @param uncompressed_p010_image (input) uncompressed P010 image
+ * @param src (input) uncompressed P010 image
* @param dest (output) tone mapping result as a YUV_420 image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- status_t toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+ status_t toneMap(jr_uncompressed_ptr src,
jr_uncompressed_ptr dest);
};
diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp
index c2a8f45..07b527c 100644
--- a/libs/jpegrecoverymap/jpegdecoder.cpp
+++ b/libs/jpegrecoverymap/jpegdecoder.cpp
@@ -93,7 +93,7 @@
JpegDecoder::~JpegDecoder() {
}
-bool JpegDecoder::decompressImage(const void* image, int length) {
+bool JpegDecoder::decompressImage(const void* image, int length, bool decodeToRGBA) {
if (image == nullptr || length <= 0) {
ALOGE("Image size can not be handled: %d", length);
return false;
@@ -101,7 +101,7 @@
mResultBuffer.clear();
mXMPBuffer.clear();
- if (!decode(image, length)) {
+ if (!decode(image, length, decodeToRGBA)) {
return false;
}
@@ -140,7 +140,7 @@
return mHeight;
}
-bool JpegDecoder::decode(const void* image, int length) {
+bool JpegDecoder::decode(const void* image, int length, bool decodeToRGBA) {
jpeg_decompress_struct cinfo;
jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
jpegrerror_mgr myerr;
@@ -210,15 +210,26 @@
mWidth = cinfo.image_width;
mHeight = cinfo.image_height;
- if (cinfo.jpeg_color_space == JCS_YCbCr) {
- mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
- } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
- mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
+ if (decodeToRGBA) {
+ if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
+ // We don't intend to support decoding grayscale to RGBA
+ return false;
+ }
+ // 4 bytes per pixel
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
+ cinfo.out_color_space = JCS_EXT_RGBA;
+ } else {
+ if (cinfo.jpeg_color_space == JCS_YCbCr) {
+ // 1 byte per pixel for Y, 0.5 byte per pixel for U+V
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
+ } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
+ }
+ cinfo.out_color_space = cinfo.jpeg_color_space;
+ cinfo.raw_data_out = TRUE;
}
- cinfo.raw_data_out = TRUE;
cinfo.dct_method = JDCT_IFAST;
- cinfo.out_color_space = cinfo.jpeg_color_space;
jpeg_start_decompress(&cinfo);
@@ -292,7 +303,10 @@
if (isSingleChannel) {
return decompressSingleChannel(cinfo, dest);
}
- return decompressYUV(cinfo, dest);
+ if (cinfo->out_color_space == JCS_EXT_RGBA)
+ return decompressRGBA(cinfo, dest);
+ else
+ return decompressYUV(cinfo, dest);
}
bool JpegDecoder::getCompressedImageParameters(const void* image, int length,
@@ -331,6 +345,20 @@
return true;
}
+bool JpegDecoder::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
+ JSAMPLE* decodeDst = (JSAMPLE*) dest;
+ uint32_t lines = 0;
+ // TODO: use batches for more effectiveness
+ while (lines < cinfo->image_height) {
+ uint32_t ret = jpeg_read_scanlines(cinfo, &decodeDst, 1);
+ if (ret == 0) {
+ break;
+ }
+ decodeDst += cinfo->image_width * 4;
+ lines++;
+ }
+ return lines == cinfo->image_height;
+}
bool JpegDecoder::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index bc69a83..ee68043 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -25,7 +25,6 @@
#include <image_io/jpeg/jpeg_scanner.h>
#include <image_io/jpeg/jpeg_info_builder.h>
#include <image_io/base/data_segment_data_source.h>
-#include <utils/Log.h>
#include <memory>
#include <sstream>
@@ -192,6 +191,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,
@@ -214,6 +238,9 @@
}
jpegr_uncompressed_struct uncompressed_yuv_420_image;
+ unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
+ uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2);
+ uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get();
JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
jpegr_uncompressed_struct map;
@@ -240,7 +267,7 @@
jpeg.length = jpeg_encoder.getCompressedImageSize();
jpegr_exif_struct new_exif;
- if (exif->data == nullptr) {
+ if (exif == nullptr || exif->data == nullptr) {
new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
} else {
new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
@@ -367,17 +394,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 +423,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 +454,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 +506,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;
}
@@ -505,23 +544,37 @@
if (compressed_jpegr_image == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
// TODO: fill EXIF data
(void) exif;
+ if (request_sdr) {
+ JpegDecoder jpeg_decoder;
+ if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
+ true)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+ jpegr_uncompressed_struct uncompressed_rgba_image;
+ uncompressed_rgba_image.data = jpeg_decoder.getDecompressedImagePtr();
+ uncompressed_rgba_image.width = jpeg_decoder.getDecompressedImageWidth();
+ uncompressed_rgba_image.height = jpeg_decoder.getDecompressedImageHeight();
+ memcpy(dest->data, uncompressed_rgba_image.data,
+ uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4);
+ dest->width = uncompressed_rgba_image.width;
+ dest->height = uncompressed_rgba_image.height;
+ return NO_ERROR;
+ }
+
jpegr_compressed_struct compressed_map;
jpegr_metadata metadata;
JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
-
JpegDecoder jpeg_decoder;
if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
return ERROR_JPEGR_DECODE_ERROR;
}
JpegDecoder recovery_map_decoder;
- if (!recovery_map_decoder.decompressImage(compressed_map.data,
- compressed_map.length)) {
+ if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
return ERROR_JPEGR_DECODE_ERROR;
}
@@ -530,26 +583,17 @@
map.width = recovery_map_decoder.getDecompressedImageWidth();
map.height = recovery_map_decoder.getDecompressedImageHeight();
-
jpegr_uncompressed_struct uncompressed_yuv_420_image;
uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
- jpeg_decoder.getXMPSize(), &metadata)) {
+ jpeg_decoder.getXMPSize(), &metadata)) {
return ERROR_JPEGR_DECODE_ERROR;
}
- if (request_sdr) {
- memcpy(dest->data, uncompressed_yuv_420_image.data,
- uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2);
- dest->width = uncompressed_yuv_420_image.width;
- dest->height = uncompressed_yuv_420_image.height;
- } else {
- JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
- }
-
+ JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
return NO_ERROR;
}
@@ -889,18 +933,39 @@
return NO_ERROR;
}
-status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+status_t RecoveryMap::toneMap(jr_uncompressed_ptr src,
jr_uncompressed_ptr dest) {
- if (uncompressed_p010_image == nullptr || dest == nullptr) {
+ if (src == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- dest->width = uncompressed_p010_image->width;
- dest->height = uncompressed_p010_image->height;
- unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2);
- dest->data = dest_data.get();
+ dest->width = src->width;
+ dest->height = src->height;
- // TODO: Tone map algorighm here.
+ size_t pixel_count = src->width * src->height;
+ for (size_t y = 0; y < src->height; ++y) {
+ for (size_t x = 0; x < src->width; ++x) {
+ size_t pixel_y_idx = x + y * src->width;
+ size_t pixel_uv_idx = x / 2 + (y / 2) * (src->width / 2);
+
+ uint16_t y_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_y_idx]
+ >> 6;
+ uint16_t u_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_count + pixel_uv_idx * 2]
+ >> 6;
+ uint16_t v_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_count + pixel_uv_idx * 2 + 1]
+ >> 6;
+
+ uint8_t* y = &reinterpret_cast<uint8_t*>(dest->data)[pixel_y_idx];
+ uint8_t* u = &reinterpret_cast<uint8_t*>(dest->data)[pixel_count + pixel_uv_idx];
+ uint8_t* v = &reinterpret_cast<uint8_t*>(dest->data)[pixel_count * 5 / 4 + pixel_uv_idx];
+
+ *y = static_cast<uint8_t>((y_uint >> 2) & 0xff);
+ *u = static_cast<uint8_t>((u_uint >> 2) & 0xff);
+ *v = static_cast<uint8_t>((v_uint >> 2) & 0xff);
+ }
+ }
+
+ dest->colorGamut = src->colorGamut;
return NO_ERROR;
}
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
index c3c6fd4..8ff12fb 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -114,58 +114,57 @@
}
/* Test Encode API-0 and decode */
-// TODO: enable when tonemapper is ready.
-//TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
-// int ret;
-//
-// // Load input files.
-// if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
-// FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
-// }
-// mRawP010Image.width = TEST_IMAGE_WIDTH;
-// mRawP010Image.height = TEST_IMAGE_HEIGHT;
-// mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
-//
-// RecoveryMap recoveryMap;
-//
-// jpegr_compressed_struct jpegR;
-// jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
-// jpegR.data = malloc(jpegR.maxLength);
-// ret = recoveryMap.encodeJPEGR(
-// &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr);
-// if (ret != OK) {
-// FAIL() << "Error code is " << ret;
-// }
-// if (SAVE_ENCODING_RESULT) {
-// // Output image data to file
-// std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
-// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
-// if (!imageFile.is_open()) {
-// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
-// }
-// imageFile.write((const char*)jpegR.data, jpegR.length);
-// }
-//
-// jpegr_uncompressed_struct decodedJpegR;
-// int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
-// decodedJpegR.data = malloc(decodedJpegRSize);
-// ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
-// if (ret != OK) {
-// FAIL() << "Error code is " << ret;
-// }
-// if (SAVE_DECODING_RESULT) {
-// // Output image data to file
-// std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
-// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
-// if (!imageFile.is_open()) {
-// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
-// }
-// imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
-// }
-//
-// free(jpegR.data);
-// free(decodedJpegR.data);
-//}
+TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
/* Test Encode API-1 and decode */
TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrThenDecode) {
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/libs/ui/Android.bp b/libs/ui/Android.bp
index d33dd34..ec0ab4e 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -48,7 +48,6 @@
integer_overflow: true,
misc_undefined: ["bounds"],
},
-
}
cc_library_static {
@@ -135,6 +134,7 @@
"Gralloc2.cpp",
"Gralloc3.cpp",
"Gralloc4.cpp",
+ "Gralloc5.cpp",
"GraphicBuffer.cpp",
"GraphicBufferAllocator.cpp",
"GraphicBufferMapper.cpp",
@@ -176,6 +176,7 @@
"libsync",
"libutils",
"liblog",
+ "libvndksupport",
],
export_shared_lib_headers: [
@@ -214,6 +215,8 @@
"libnativewindow_headers",
"libhardware_headers",
"libui_headers",
+ "libimapper_stablec",
+ "libimapper_providerutils",
],
export_static_lib_headers: [
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index f23f10a..e9b5dec 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -161,7 +161,7 @@
return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
}
-status_t Gralloc2Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+status_t Gralloc2Mapper::importBuffer(const native_handle_t* rawHandle,
buffer_handle_t* outBufferHandle) const {
Error error;
auto ret = mMapper->importBuffer(rawHandle,
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index 15c60bc..474d381 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -138,7 +138,7 @@
return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
}
-status_t Gralloc3Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+status_t Gralloc3Mapper::importBuffer(const native_handle_t* rawHandle,
buffer_handle_t* outBufferHandle) const {
Error error;
auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) {
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index f6ab7b2..7459466 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -196,7 +196,7 @@
return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
}
-status_t Gralloc4Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+status_t Gralloc4Mapper::importBuffer(const native_handle_t* rawHandle,
buffer_handle_t* outBufferHandle) const {
Error error;
auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) {
@@ -1233,7 +1233,10 @@
if (mAidlAllocator) {
AllocationResult result;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
auto status = mAidlAllocator->allocate(descriptor, bufferCount, &result);
+#pragma clang diagnostic pop // deprecation
if (!status.isOk()) {
error = status.getExceptionCode();
if (error == EX_SERVICE_SPECIFIC) {
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
new file mode 100644
index 0000000..6f196b8
--- /dev/null
+++ b/libs/ui/Gralloc5.cpp
@@ -0,0 +1,903 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Gralloc5"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <ui/Gralloc5.h>
+
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android/binder_manager.h>
+#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
+#include <binder/IPCThreadState.h>
+#include <dlfcn.h>
+#include <ui/FatVector.h>
+#include <vndksupport/linker.h>
+
+using namespace aidl::android::hardware::graphics::allocator;
+using namespace aidl::android::hardware::graphics::common;
+using namespace ::android::hardware::graphics::mapper;
+
+namespace android {
+
+static const auto kIAllocatorServiceName = IAllocator::descriptor + std::string("/default");
+static const auto kIAllocatorMinimumVersion = 2;
+
+// TODO(b/72323293, b/72703005): Remove these invalid bits from callers
+static constexpr uint64_t kRemovedUsageBits = static_cast<uint64_t>((1 << 10) | (1 << 13));
+
+typedef AIMapper_Error (*AIMapper_loadIMapperFn)(AIMapper *_Nullable *_Nonnull outImplementation);
+
+struct Gralloc5 {
+ std::shared_ptr<IAllocator> allocator;
+ AIMapper *mapper = nullptr;
+};
+
+static std::shared_ptr<IAllocator> waitForAllocator() {
+ if (__builtin_available(android 31, *)) {
+ if (!AServiceManager_isDeclared(kIAllocatorServiceName.c_str())) {
+ return nullptr;
+ }
+ auto allocator = IAllocator::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(kIAllocatorServiceName.c_str())));
+ if (!allocator) {
+ ALOGE("AIDL IAllocator declared but failed to get service");
+ return nullptr;
+ }
+
+ int32_t version = 0;
+ if (!allocator->getInterfaceVersion(&version).isOk()) {
+ ALOGE("Failed to query interface version");
+ return nullptr;
+ }
+ if (version < kIAllocatorMinimumVersion) {
+ return nullptr;
+ }
+ return allocator;
+ } else {
+ // TODO: LOG_ALWAYS_FATAL("libui is not backwards compatible");
+ return nullptr;
+ }
+}
+
+static void *loadIMapperLibrary() {
+ static void *imapperLibrary = []() -> void * {
+ auto allocator = waitForAllocator();
+ std::string mapperSuffix;
+ auto status = allocator->getIMapperLibrarySuffix(&mapperSuffix);
+ if (!status.isOk()) {
+ ALOGE("Failed to get IMapper library suffix");
+ return nullptr;
+ }
+
+ std::string lib_name = "mapper." + mapperSuffix + ".so";
+ void *so = android_load_sphal_library(lib_name.c_str(), RTLD_LOCAL | RTLD_NOW);
+ if (!so) {
+ ALOGE("Failed to load %s", lib_name.c_str());
+ }
+ return so;
+ }();
+ return imapperLibrary;
+}
+
+static const Gralloc5 &getInstance() {
+ static Gralloc5 instance = []() {
+ auto allocator = waitForAllocator();
+ if (!allocator) {
+ return Gralloc5{};
+ }
+ void *so = loadIMapperLibrary();
+ if (!so) {
+ return Gralloc5{};
+ }
+ auto loadIMapper = (AIMapper_loadIMapperFn)dlsym(so, "AIMapper_loadIMapper");
+ AIMapper *mapper = nullptr;
+ AIMapper_Error error = loadIMapper(&mapper);
+ if (error != AIMAPPER_ERROR_NONE) {
+ ALOGE("AIMapper_loadIMapper failed %d", error);
+ return Gralloc5{};
+ }
+ return Gralloc5{std::move(allocator), mapper};
+ }();
+ return instance;
+}
+
+template <StandardMetadataType T>
+static auto getStandardMetadata(AIMapper *mapper, buffer_handle_t bufferHandle)
+ -> decltype(StandardMetadata<T>::value::decode(nullptr, 0)) {
+ using Value = typename StandardMetadata<T>::value;
+ // TODO: Tune for common-case better
+ FatVector<uint8_t, 128> buffer;
+ int32_t sizeRequired = mapper->v5.getStandardMetadata(bufferHandle, static_cast<int64_t>(T),
+ buffer.data(), buffer.size());
+ if (sizeRequired < 0) {
+ ALOGW_IF(-AIMAPPER_ERROR_UNSUPPORTED != sizeRequired,
+ "Unexpected error %d from valid getStandardMetadata call", -sizeRequired);
+ return std::nullopt;
+ }
+ if ((size_t)sizeRequired > buffer.size()) {
+ buffer.resize(sizeRequired);
+ sizeRequired = mapper->v5.getStandardMetadata(bufferHandle, static_cast<int64_t>(T),
+ buffer.data(), buffer.size());
+ }
+ if (sizeRequired < 0 || (size_t)sizeRequired > buffer.size()) {
+ ALOGW("getStandardMetadata failed, received %d with buffer size %zd", sizeRequired,
+ buffer.size());
+ // Generate a fail type
+ return std::nullopt;
+ }
+ return Value::decode(buffer.data(), sizeRequired);
+}
+
+template <StandardMetadataType T>
+static AIMapper_Error setStandardMetadata(AIMapper *mapper, buffer_handle_t bufferHandle,
+ const typename StandardMetadata<T>::value_type &value) {
+ using Value = typename StandardMetadata<T>::value;
+ int32_t sizeRequired = Value::encode(value, nullptr, 0);
+ if (sizeRequired < 0) {
+ ALOGW("Failed to calculate required size");
+ return static_cast<AIMapper_Error>(-sizeRequired);
+ }
+ FatVector<uint8_t, 128> buffer;
+ buffer.resize(sizeRequired);
+ sizeRequired = Value::encode(value, buffer.data(), buffer.size());
+ if (sizeRequired < 0 || (size_t)sizeRequired > buffer.size()) {
+ ALOGW("Failed to encode with calculated size %d; buffer size %zd", sizeRequired,
+ buffer.size());
+ return static_cast<AIMapper_Error>(-sizeRequired);
+ }
+ return mapper->v5.setStandardMetadata(bufferHandle, static_cast<int64_t>(T), buffer.data(),
+ sizeRequired);
+}
+
+Gralloc5Allocator::Gralloc5Allocator(const Gralloc5Mapper &mapper) : mMapper(mapper) {
+ mAllocator = getInstance().allocator;
+}
+
+bool Gralloc5Allocator::isLoaded() const {
+ return mAllocator != nullptr;
+}
+
+static uint64_t getValidUsageBits() {
+ static const uint64_t validUsageBits = []() -> uint64_t {
+ uint64_t bits = 0;
+ for (const auto bit : ndk::enum_range<BufferUsage>{}) {
+ bits |= static_cast<int64_t>(bit);
+ }
+ return bits;
+ }();
+ return validUsageBits | kRemovedUsageBits;
+}
+
+static std::optional<BufferDescriptorInfo> makeDescriptor(std::string requestorName, uint32_t width,
+ uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage) {
+ uint64_t validUsageBits = getValidUsageBits();
+ if (usage & ~validUsageBits) {
+ ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64, usage & ~validUsageBits);
+ return std::nullopt;
+ }
+
+ BufferDescriptorInfo descriptorInfo{
+ .width = static_cast<int32_t>(width),
+ .height = static_cast<int32_t>(height),
+ .layerCount = static_cast<int32_t>(layerCount),
+ .format = static_cast<::aidl::android::hardware::graphics::common::PixelFormat>(format),
+ .usage = static_cast<BufferUsage>(usage),
+ };
+ auto nameLength = std::min(requestorName.length(), descriptorInfo.name.size() - 1);
+ memcpy(descriptorInfo.name.data(), requestorName.data(), nameLength);
+ requestorName.data()[nameLength] = 0;
+ return descriptorInfo;
+}
+
+std::string Gralloc5Allocator::dumpDebugInfo(bool less) const {
+ return mMapper.dumpBuffers(less);
+}
+
+status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t bufferCount, uint32_t *outStride,
+ buffer_handle_t *outBufferHandles, bool importBuffers) const {
+ auto descriptorInfo = makeDescriptor(requestorName, width, height, format, layerCount, usage);
+ if (!descriptorInfo) {
+ return BAD_VALUE;
+ }
+
+ AllocationResult result;
+ auto status = mAllocator->allocate2(*descriptorInfo, bufferCount, &result);
+ if (!status.isOk()) {
+ auto error = status.getExceptionCode();
+ if (error == EX_SERVICE_SPECIFIC) {
+ error = status.getServiceSpecificError();
+ }
+ if (error == OK) {
+ error = UNKNOWN_ERROR;
+ }
+ return error;
+ }
+
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ auto handle = makeFromAidl(result.buffers[i]);
+ auto error = mMapper.importBuffer(handle, &outBufferHandles[i]);
+ native_handle_delete(handle);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return error;
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = dupFromAidl(result.buffers[i]);
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t *>(outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ return NO_MEMORY;
+ }
+ }
+ }
+
+ *outStride = result.stride;
+
+ // Release all the resources held by AllocationResult (specifically any remaining FDs)
+ result = {};
+ // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+ // TODO: Re-enable this at some point if it's necessary. We can't do it now because libui
+ // is marked apex_available (b/214400477) and libbinder isn't (which of course is correct)
+ // IPCThreadState::self()->flushCommands();
+
+ return OK;
+}
+
+void Gralloc5Mapper::preload() {
+ // TODO(b/261858155): Implement. We can't bounce off of IAllocator for this because zygote can't
+ // use binder. So when an alternate strategy of retrieving the library prefix is available,
+ // use that here.
+}
+
+Gralloc5Mapper::Gralloc5Mapper() {
+ mMapper = getInstance().mapper;
+}
+
+bool Gralloc5Mapper::isLoaded() const {
+ return mMapper != nullptr && mMapper->version >= AIMAPPER_VERSION_5;
+}
+
+std::string Gralloc5Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
+ // TODO(b/261858392): Implement
+ (void)bufferHandle;
+ (void)less;
+ return {};
+}
+
+std::string Gralloc5Mapper::dumpBuffers(bool less) const {
+ // TODO(b/261858392): Implement
+ (void)less;
+ return {};
+}
+
+status_t Gralloc5Mapper::importBuffer(const native_handle_t *rawHandle,
+ buffer_handle_t *outBufferHandle) const {
+ return mMapper->v5.importBuffer(rawHandle, outBufferHandle);
+}
+
+void Gralloc5Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
+ mMapper->v5.freeBuffer(bufferHandle);
+}
+
+status_t Gralloc5Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+ uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t stride) const {
+ {
+ auto value = getStandardMetadata<StandardMetadataType::WIDTH>(mMapper, bufferHandle);
+ if (width != value) {
+ ALOGW("Width didn't match, expected %d got %" PRId64, width, value.value_or(-1));
+ return BAD_VALUE;
+ }
+ }
+ {
+ auto value = getStandardMetadata<StandardMetadataType::HEIGHT>(mMapper, bufferHandle);
+ if (height != value) {
+ ALOGW("Height didn't match, expected %d got %" PRId64, height, value.value_or(-1));
+ return BAD_VALUE;
+ }
+ }
+ {
+ auto value =
+ getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(mMapper,
+ bufferHandle);
+ if (static_cast<::aidl::android::hardware::graphics::common::PixelFormat>(format) !=
+ value) {
+ ALOGW("Format didn't match, expected %d got %s", format,
+ value.has_value() ? toString(*value).c_str() : "<null>");
+ return BAD_VALUE;
+ }
+ }
+ {
+ auto value = getStandardMetadata<StandardMetadataType::LAYER_COUNT>(mMapper, bufferHandle);
+ if (layerCount != value) {
+ ALOGW("Layer count didn't match, expected %d got %" PRId64, layerCount,
+ value.value_or(-1));
+ return BAD_VALUE;
+ }
+ }
+ {
+ auto value = getStandardMetadata<StandardMetadataType::USAGE>(mMapper, bufferHandle);
+ if (static_cast<BufferUsage>(usage) != value) {
+ ALOGW("Usage didn't match, expected %" PRIu64 " got %" PRId64, usage,
+ static_cast<int64_t>(value.value_or(BufferUsage::CPU_READ_NEVER)));
+ return BAD_VALUE;
+ }
+ }
+ {
+ (void)stride;
+ // TODO(b/261856851): Add StandardMetadataType::STRIDE && enable this
+ // auto value = getStandardMetadata<StandardMetadataType::STRIDE>(mMapper,
+ // bufferHandle); if (static_cast<BufferUsage>(usage) != value) {
+ // ALOGW("Layer count didn't match, expected %" PRIu64 " got %" PRId64, usage,
+ // static_cast<int64_t>(value.value_or(BufferUsage::CPU_READ_NEVER)));
+ // return BAD_VALUE;
+ // }
+ }
+ return OK;
+}
+
+void Gralloc5Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t *outNumFds,
+ uint32_t *outNumInts) const {
+ mMapper->v5.getTransportSize(bufferHandle, outNumFds, outNumInts);
+}
+
+status_t Gralloc5Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds,
+ int acquireFence, void **outData, int32_t *outBytesPerPixel,
+ int32_t *outBytesPerStride) const {
+ std::vector<ui::PlaneLayout> planeLayouts;
+ status_t err = getPlaneLayouts(bufferHandle, &planeLayouts);
+
+ if (err == NO_ERROR && !planeLayouts.empty()) {
+ if (outBytesPerPixel) {
+ int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits;
+ for (const auto &planeLayout : planeLayouts) {
+ if (bitsPerPixel != planeLayout.sampleIncrementInBits) {
+ bitsPerPixel = -1;
+ }
+ }
+ if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) {
+ *outBytesPerPixel = bitsPerPixel / 8;
+ } else {
+ *outBytesPerPixel = -1;
+ }
+ }
+ if (outBytesPerStride) {
+ int32_t bytesPerStride = planeLayouts.front().strideInBytes;
+ for (const auto &planeLayout : planeLayouts) {
+ if (bytesPerStride != planeLayout.strideInBytes) {
+ bytesPerStride = -1;
+ }
+ }
+ if (bytesPerStride >= 0) {
+ *outBytesPerStride = bytesPerStride;
+ } else {
+ *outBytesPerStride = -1;
+ }
+ }
+ }
+
+ auto status = mMapper->v5.lock(bufferHandle, usage, bounds, acquireFence, outData);
+
+ ALOGW_IF(status != AIMAPPER_ERROR_NONE, "lock(%p, ...) failed: %d", bufferHandle, status);
+ return static_cast<status_t>(status);
+}
+
+status_t Gralloc5Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds,
+ int acquireFence, android_ycbcr *outYcbcr) const {
+ if (!outYcbcr) {
+ return BAD_VALUE;
+ }
+
+ // TODO(b/262279301): Change the return type of ::unlock to unique_fd instead of int so that
+ // ignoring the return value "just works" instead
+ auto unlock = [this](buffer_handle_t bufferHandle) {
+ int fence = this->unlock(bufferHandle);
+ if (fence != -1) {
+ ::close(fence);
+ }
+ };
+
+ std::vector<ui::PlaneLayout> planeLayouts;
+ status_t error = getPlaneLayouts(bufferHandle, &planeLayouts);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ void *data = nullptr;
+ error = lock(bufferHandle, usage, bounds, acquireFence, &data, nullptr, nullptr);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ android_ycbcr ycbcr;
+
+ ycbcr.y = nullptr;
+ ycbcr.cb = nullptr;
+ ycbcr.cr = nullptr;
+ ycbcr.ystride = 0;
+ ycbcr.cstride = 0;
+ ycbcr.chroma_step = 0;
+
+ for (const auto &planeLayout : planeLayouts) {
+ for (const auto &planeLayoutComponent : planeLayout.components) {
+ if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) {
+ continue;
+ }
+
+ uint8_t *tmpData = static_cast<uint8_t *>(data) + planeLayout.offsetInBytes;
+
+ // Note that `offsetInBits` may not be a multiple of 8 for packed formats (e.g. P010)
+ // but we still want to point to the start of the first byte.
+ tmpData += (planeLayoutComponent.offsetInBits / 8);
+
+ uint64_t sampleIncrementInBytes;
+
+ auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value);
+ switch (type) {
+ case PlaneLayoutComponentType::Y:
+ if ((ycbcr.y != nullptr) || (planeLayout.sampleIncrementInBits % 8 != 0)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.y = tmpData;
+ ycbcr.ystride = planeLayout.strideInBytes;
+ break;
+
+ case PlaneLayoutComponentType::CB:
+ case PlaneLayoutComponentType::CR:
+ if (planeLayout.sampleIncrementInBits % 8 != 0) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8;
+ if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2) &&
+ (sampleIncrementInBytes != 4)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ if (ycbcr.cstride == 0 && ycbcr.chroma_step == 0) {
+ ycbcr.cstride = planeLayout.strideInBytes;
+ ycbcr.chroma_step = sampleIncrementInBytes;
+ } else {
+ if ((static_cast<int64_t>(ycbcr.cstride) != planeLayout.strideInBytes) ||
+ (ycbcr.chroma_step != sampleIncrementInBytes)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ }
+
+ if (type == PlaneLayoutComponentType::CB) {
+ if (ycbcr.cb != nullptr) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.cb = tmpData;
+ } else {
+ if (ycbcr.cr != nullptr) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.cr = tmpData;
+ }
+ break;
+ default:
+ break;
+ };
+ }
+ }
+
+ *outYcbcr = ycbcr;
+ return OK;
+}
+
+int Gralloc5Mapper::unlock(buffer_handle_t bufferHandle) const {
+ int fence = -1;
+ AIMapper_Error error = mMapper->v5.unlock(bufferHandle, &fence);
+ if (error != AIMAPPER_ERROR_NONE) {
+ ALOGW("unlock failed with error %d", error);
+ }
+ return fence;
+}
+
+status_t Gralloc5Mapper::isSupported(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ bool *outSupported) const {
+ auto descriptorInfo = makeDescriptor("", width, height, format, layerCount, usage);
+ if (!descriptorInfo) {
+ *outSupported = false;
+ return OK;
+ }
+ auto status = getInstance().allocator->isSupported(*descriptorInfo, outSupported);
+ if (!status.isOk()) {
+ ALOGW("IAllocator::isSupported error %d (%s)", status.getStatus(), status.getMessage());
+ *outSupported = false;
+ }
+ return OK;
+}
+
+status_t Gralloc5Mapper::getBufferId(buffer_handle_t bufferHandle, uint64_t *outBufferId) const {
+ auto value = getStandardMetadata<StandardMetadataType::BUFFER_ID>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outBufferId = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getName(buffer_handle_t bufferHandle, std::string *outName) const {
+ auto value = getStandardMetadata<StandardMetadataType::NAME>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outName = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getWidth(buffer_handle_t bufferHandle, uint64_t *outWidth) const {
+ auto value = getStandardMetadata<StandardMetadataType::WIDTH>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outWidth = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getHeight(buffer_handle_t bufferHandle, uint64_t *outHeight) const {
+ auto value = getStandardMetadata<StandardMetadataType::HEIGHT>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outHeight = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getLayerCount(buffer_handle_t bufferHandle,
+ uint64_t *outLayerCount) const {
+ auto value = getStandardMetadata<StandardMetadataType::LAYER_COUNT>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outLayerCount = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat *outPixelFormatRequested) const {
+ auto value = getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(mMapper,
+ bufferHandle);
+ if (value.has_value()) {
+ *outPixelFormatRequested = static_cast<ui::PixelFormat>(*value);
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t *outPixelFormatFourCC) const {
+ auto value =
+ getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_FOURCC>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outPixelFormatFourCC = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t *outPixelFormatModifier) const {
+ auto value =
+ getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_MODIFIER>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outPixelFormatModifier = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getUsage(buffer_handle_t bufferHandle, uint64_t *outUsage) const {
+ auto value = getStandardMetadata<StandardMetadataType::USAGE>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outUsage = static_cast<uint64_t>(*value);
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t *outAllocationSize) const {
+ auto value = getStandardMetadata<StandardMetadataType::ALLOCATION_SIZE>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outAllocationSize = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t *outProtectedContent) const {
+ auto value =
+ getStandardMetadata<StandardMetadataType::PROTECTED_CONTENT>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outProtectedContent = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getCompression(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType *outCompression) const {
+ auto value = getStandardMetadata<StandardMetadataType::COMPRESSION>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outCompression = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getCompression(buffer_handle_t bufferHandle,
+ ui::Compression *outCompression) const {
+ auto value = getStandardMetadata<StandardMetadataType::COMPRESSION>(mMapper, bufferHandle);
+ if (!value.has_value()) {
+ return UNKNOWN_TRANSACTION;
+ }
+ if (!gralloc4::isStandardCompression(*value)) {
+ return BAD_TYPE;
+ }
+ *outCompression = gralloc4::getStandardCompressionValue(*value);
+ return OK;
+}
+
+status_t Gralloc5Mapper::getInterlaced(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType *outInterlaced) const {
+ auto value = getStandardMetadata<StandardMetadataType::INTERLACED>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outInterlaced = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced *outInterlaced) const {
+ if (!outInterlaced) {
+ return BAD_VALUE;
+ }
+ ExtendableType interlaced;
+ status_t error = getInterlaced(bufferHandle, &interlaced);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardInterlaced(interlaced)) {
+ return BAD_TYPE;
+ }
+ *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+ return NO_ERROR;
+}
+
+status_t Gralloc5Mapper::getChromaSiting(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType *outChromaSiting) const {
+ auto value = getStandardMetadata<StandardMetadataType::CHROMA_SITING>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outChromaSiting = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting *outChromaSiting) const {
+ if (!outChromaSiting) {
+ return BAD_VALUE;
+ }
+ ExtendableType chromaSiting;
+ status_t error = getChromaSiting(bufferHandle, &chromaSiting);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+ return BAD_TYPE;
+ }
+ *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+ return NO_ERROR;
+}
+
+status_t Gralloc5Mapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout> *outPlaneLayouts) const {
+ auto value = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outPlaneLayouts = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDataspace(buffer_handle_t bufferHandle,
+ ui::Dataspace *outDataspace) const {
+ auto value = getStandardMetadata<StandardMetadataType::DATASPACE>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outDataspace = static_cast<ui::Dataspace>(*value);
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::setDataspace(buffer_handle_t bufferHandle, ui::Dataspace dataspace) const {
+ return setStandardMetadata<StandardMetadataType::DATASPACE>(mMapper, bufferHandle,
+ static_cast<Dataspace>(dataspace));
+}
+
+status_t Gralloc5Mapper::getBlendMode(buffer_handle_t bufferHandle,
+ ui::BlendMode *outBlendMode) const {
+ auto value = getStandardMetadata<StandardMetadataType::BLEND_MODE>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outBlendMode = static_cast<ui::BlendMode>(*value);
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086> *outSmpte2086) const {
+ auto value = getStandardMetadata<StandardMetadataType::SMPTE2086>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outSmpte2086 = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::setSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086> smpte2086) const {
+ return setStandardMetadata<StandardMetadataType::SMPTE2086>(mMapper, bufferHandle, smpte2086);
+}
+
+status_t Gralloc5Mapper::getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3> *outCta861_3) const {
+ auto value = getStandardMetadata<StandardMetadataType::CTA861_3>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outCta861_3 = *value;
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::setCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3> cta861_3) const {
+ return setStandardMetadata<StandardMetadataType::CTA861_3>(mMapper, bufferHandle, cta861_3);
+}
+
+status_t Gralloc5Mapper::getSmpte2094_40(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>> *outSmpte2094_40) const {
+ auto value = getStandardMetadata<StandardMetadataType::SMPTE2094_40>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outSmpte2094_40 = std::move(*value);
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::setSmpte2094_40(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>> smpte2094_40) const {
+ return setStandardMetadata<StandardMetadataType::SMPTE2094_40>(mMapper, bufferHandle,
+ smpte2094_40);
+}
+
+status_t Gralloc5Mapper::getSmpte2094_10(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>> *outSmpte2094_10) const {
+ auto value = getStandardMetadata<StandardMetadataType::SMPTE2094_10>(mMapper, bufferHandle);
+ if (value.has_value()) {
+ *outSmpte2094_10 = std::move(*value);
+ return OK;
+ }
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::setSmpte2094_10(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>> smpte2094_10) const {
+ return setStandardMetadata<StandardMetadataType::SMPTE2094_10>(mMapper, bufferHandle,
+ smpte2094_10);
+}
+
+status_t Gralloc5Mapper::getDefaultPixelFormatFourCC(uint32_t, uint32_t, PixelFormat, uint32_t,
+ uint64_t, uint32_t *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDefaultPixelFormatModifier(uint32_t, uint32_t, PixelFormat, uint32_t,
+ uint64_t, uint64_t *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDefaultAllocationSize(uint32_t, uint32_t, PixelFormat, uint32_t,
+ uint64_t, uint64_t *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDefaultProtectedContent(uint32_t, uint32_t, PixelFormat, uint32_t,
+ uint64_t, uint64_t *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDefaultCompression(
+ uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
+ aidl::android::hardware::graphics::common::ExtendableType *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDefaultCompression(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
+ ui::Compression *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDefaultInterlaced(
+ uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
+ aidl::android::hardware::graphics::common::ExtendableType *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDefaultInterlaced(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
+ ui::Interlaced *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDefaultChromaSiting(
+ uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
+ aidl::android::hardware::graphics::common::ExtendableType *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDefaultChromaSiting(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
+ ui::ChromaSiting *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+status_t Gralloc5Mapper::getDefaultPlaneLayouts(uint32_t, uint32_t, PixelFormat, uint32_t, uint64_t,
+ std::vector<ui::PlaneLayout> *) const {
+ // TODO(b/261857910): Remove
+ return UNKNOWN_TRANSACTION;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 3f958ba..c0abec2 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -34,6 +34,7 @@
#include <ui/Gralloc2.h>
#include <ui/Gralloc3.h>
#include <ui/Gralloc4.h>
+#include <ui/Gralloc5.h>
#include <ui/GraphicBufferMapper.h>
namespace android {
@@ -48,23 +49,27 @@
GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
- mAllocator = std::make_unique<const Gralloc4Allocator>(
- reinterpret_cast<const Gralloc4Mapper&>(mMapper.getGrallocMapper()));
- if (mAllocator->isLoaded()) {
- return;
+ switch (mMapper.getMapperVersion()) {
+ case GraphicBufferMapper::GRALLOC_5:
+ mAllocator = std::make_unique<const Gralloc5Allocator>(
+ reinterpret_cast<const Gralloc5Mapper&>(mMapper.getGrallocMapper()));
+ break;
+ case GraphicBufferMapper::GRALLOC_4:
+ mAllocator = std::make_unique<const Gralloc4Allocator>(
+ reinterpret_cast<const Gralloc4Mapper&>(mMapper.getGrallocMapper()));
+ break;
+ case GraphicBufferMapper::GRALLOC_3:
+ mAllocator = std::make_unique<const Gralloc3Allocator>(
+ reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper()));
+ break;
+ case GraphicBufferMapper::GRALLOC_2:
+ mAllocator = std::make_unique<const Gralloc2Allocator>(
+ reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
+ break;
}
- mAllocator = std::make_unique<const Gralloc3Allocator>(
- reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper()));
- if (mAllocator->isLoaded()) {
- return;
- }
- mAllocator = std::make_unique<const Gralloc2Allocator>(
- reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
- if (mAllocator->isLoaded()) {
- return;
- }
-
- LOG_ALWAYS_FATAL("gralloc-allocator is missing");
+ LOG_ALWAYS_FATAL_IF(!mAllocator->isLoaded(),
+ "Failed to load matching allocator for mapper version %d",
+ mMapper.getMapperVersion());
}
GraphicBufferAllocator::~GraphicBufferAllocator() {}
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index a98e697..6002a6d 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -36,6 +36,7 @@
#include <ui/Gralloc2.h>
#include <ui/Gralloc3.h>
#include <ui/Gralloc4.h>
+#include <ui/Gralloc5.h>
#include <ui/GraphicBuffer.h>
#include <system/graphics.h>
@@ -49,9 +50,15 @@
Gralloc2Mapper::preload();
Gralloc3Mapper::preload();
Gralloc4Mapper::preload();
+ Gralloc5Mapper::preload();
}
GraphicBufferMapper::GraphicBufferMapper() {
+ mMapper = std::make_unique<const Gralloc5Mapper>();
+ if (mMapper->isLoaded()) {
+ mMapperVersion = Version::GRALLOC_5;
+ return;
+ }
mMapper = std::make_unique<const Gralloc4Mapper>();
if (mMapper->isLoaded()) {
mMapperVersion = Version::GRALLOC_4;
@@ -82,15 +89,14 @@
ALOGD("%s", s.c_str());
}
-status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
- uint32_t width, uint32_t height, uint32_t layerCount,
- PixelFormat format, uint64_t usage, uint32_t stride,
- buffer_handle_t* outHandle)
-{
+status_t GraphicBufferMapper::importBuffer(const native_handle_t* rawHandle, uint32_t width,
+ uint32_t height, uint32_t layerCount, PixelFormat format,
+ uint64_t usage, uint32_t stride,
+ buffer_handle_t* outHandle) {
ATRACE_CALL();
buffer_handle_t bufferHandle;
- status_t error = mMapper->importBuffer(hardware::hidl_handle(rawHandle), &bufferHandle);
+ status_t error = mMapper->importBuffer(rawHandle, &bufferHandle);
if (error != NO_ERROR) {
ALOGW("importBuffer(%p) failed: %d", rawHandle, error);
return error;
@@ -109,6 +115,11 @@
return NO_ERROR;
}
+status_t GraphicBufferMapper::importBufferNoValidate(const native_handle_t* rawHandle,
+ buffer_handle_t* outHandle) {
+ return mMapper->importBuffer(rawHandle, outHandle);
+}
+
void GraphicBufferMapper::getTransportSize(buffer_handle_t handle,
uint32_t* outTransportNumFds, uint32_t* outTransportNumInts)
{
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 6101d4b..b494cbe 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -39,14 +39,11 @@
return "";
}
- virtual status_t createDescriptor(void* bufferDescriptorInfo,
- void* outBufferDescriptor) const = 0;
-
// Import a buffer that is from another HAL, another process, or is
// cloned.
//
// The returned handle must be freed with freeBuffer.
- virtual status_t importBuffer(const hardware::hidl_handle& rawHandle,
+ virtual status_t importBuffer(const native_handle_t* rawHandle,
buffer_handle_t* outBufferHandle) const = 0;
virtual void freeBuffer(buffer_handle_t bufferHandle) const = 0;
@@ -269,11 +266,6 @@
std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
return INVALID_OPERATION;
}
-
- virtual std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
- listSupportedMetadataTypes() const {
- return {};
- }
};
// A wrapper to IAllocator
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index f570c42..a7b6f492 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -38,9 +38,9 @@
bool isLoaded() const override;
- status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
+ status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const;
- status_t importBuffer(const hardware::hidl_handle& rawHandle,
+ status_t importBuffer(const native_handle_t* rawHandle,
buffer_handle_t* outBufferHandle) const override;
void freeBuffer(buffer_handle_t bufferHandle) const override;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index 93a5077..7367549 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -37,9 +37,9 @@
bool isLoaded() const override;
- status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
+ status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const;
- status_t importBuffer(const hardware::hidl_handle& rawHandle,
+ status_t importBuffer(const native_handle_t* rawHandle,
buffer_handle_t* outBufferHandle) const override;
void freeBuffer(buffer_handle_t bufferHandle) const override;
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index cf023c9..6bc5ce5 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -42,9 +42,9 @@
std::string dumpBuffer(buffer_handle_t bufferHandle, bool less = true) const override;
std::string dumpBuffers(bool less = true) const;
- status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
+ status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const;
- status_t importBuffer(const hardware::hidl_handle& rawHandle,
+ status_t importBuffer(const native_handle_t* rawHandle,
buffer_handle_t* outBufferHandle) const override;
void freeBuffer(buffer_handle_t bufferHandle) const override;
diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h
new file mode 100644
index 0000000..bc10169
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc5.h
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/graphics/allocator/IAllocator.h>
+#include <android/hardware/graphics/mapper/IMapper.h>
+#include <ui/Gralloc.h>
+
+namespace android {
+
+class Gralloc5Mapper : public GrallocMapper {
+public:
+public:
+ static void preload();
+
+ Gralloc5Mapper();
+
+ [[nodiscard]] bool isLoaded() const override;
+
+ [[nodiscard]] std::string dumpBuffer(buffer_handle_t bufferHandle, bool less) const override;
+
+ [[nodiscard]] std::string dumpBuffers(bool less = true) const;
+
+ [[nodiscard]] status_t importBuffer(const native_handle_t *rawHandle,
+ buffer_handle_t *outBufferHandle) const override;
+
+ void freeBuffer(buffer_handle_t bufferHandle) const override;
+
+ [[nodiscard]] status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+ uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t stride) const override;
+
+ void getTransportSize(buffer_handle_t bufferHandle, uint32_t *outNumFds,
+ uint32_t *outNumInts) const override;
+
+ [[nodiscard]] status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds,
+ int acquireFence, void **outData, int32_t *outBytesPerPixel,
+ int32_t *outBytesPerStride) const override;
+
+ [[nodiscard]] status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds,
+ int acquireFence, android_ycbcr *ycbcr) const override;
+
+ [[nodiscard]] int unlock(buffer_handle_t bufferHandle) const override;
+
+ [[nodiscard]] status_t isSupported(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ bool *outSupported) const override;
+
+ [[nodiscard]] status_t getBufferId(buffer_handle_t bufferHandle,
+ uint64_t *outBufferId) const override;
+
+ [[nodiscard]] status_t getName(buffer_handle_t bufferHandle,
+ std::string *outName) const override;
+
+ [[nodiscard]] status_t getWidth(buffer_handle_t bufferHandle,
+ uint64_t *outWidth) const override;
+
+ [[nodiscard]] status_t getHeight(buffer_handle_t bufferHandle,
+ uint64_t *outHeight) const override;
+
+ [[nodiscard]] status_t getLayerCount(buffer_handle_t bufferHandle,
+ uint64_t *outLayerCount) const override;
+
+ [[nodiscard]] status_t getPixelFormatRequested(
+ buffer_handle_t bufferHandle, ui::PixelFormat *outPixelFormatRequested) const override;
+
+ [[nodiscard]] status_t getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t *outPixelFormatFourCC) const override;
+
+ [[nodiscard]] status_t getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t *outPixelFormatModifier) const override;
+
+ [[nodiscard]] status_t getUsage(buffer_handle_t bufferHandle,
+ uint64_t *outUsage) const override;
+
+ [[nodiscard]] status_t getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t *outAllocationSize) const override;
+
+ [[nodiscard]] status_t getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t *outProtectedContent) const override;
+
+ [[nodiscard]] status_t getCompression(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType
+ *outCompression) const override;
+
+ [[nodiscard]] status_t getCompression(buffer_handle_t bufferHandle,
+ ui::Compression *outCompression) const override;
+
+ [[nodiscard]] status_t getInterlaced(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType
+ *outInterlaced) const override;
+
+ [[nodiscard]] status_t getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced *outInterlaced) const override;
+
+ [[nodiscard]] status_t getChromaSiting(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType
+ *outChromaSiting) const override;
+
+ [[nodiscard]] status_t getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting *outChromaSiting) const override;
+
+ [[nodiscard]] status_t getPlaneLayouts(
+ buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout> *outPlaneLayouts) const override;
+
+ [[nodiscard]] status_t getDataspace(buffer_handle_t bufferHandle,
+ ui::Dataspace *outDataspace) const override;
+
+ [[nodiscard]] status_t setDataspace(buffer_handle_t bufferHandle,
+ ui::Dataspace dataspace) const override;
+
+ [[nodiscard]] status_t getBlendMode(buffer_handle_t bufferHandle,
+ ui::BlendMode *outBlendMode) const override;
+
+ [[nodiscard]] status_t getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086> *outSmpte2086) const override;
+
+ [[nodiscard]] status_t setSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086> smpte2086) const override;
+
+ [[nodiscard]] status_t getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3> *outCta861_3) const override;
+
+ [[nodiscard]] status_t setCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3> cta861_3) const override;
+
+ [[nodiscard]] status_t getSmpte2094_40(
+ buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>> *outSmpte2094_40) const override;
+
+ [[nodiscard]] status_t setSmpte2094_40(
+ buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>> smpte2094_40) const override;
+
+ [[nodiscard]] status_t getSmpte2094_10(
+ buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>> *outSmpte2094_10) const override;
+
+ [[nodiscard]] status_t setSmpte2094_10(
+ buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>> smpte2094_10) const override;
+
+ [[nodiscard]] status_t getDefaultPixelFormatFourCC(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t *outPixelFormatFourCC) const override;
+
+ [[nodiscard]] status_t getDefaultPixelFormatModifier(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint64_t *outPixelFormatModifier) const override;
+
+ [[nodiscard]] status_t getDefaultAllocationSize(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t *outAllocationSize) const override;
+
+ [[nodiscard]] status_t getDefaultProtectedContent(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t *outProtectedContent) const override;
+
+ [[nodiscard]] status_t getDefaultCompression(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType *outCompression)
+ const override;
+
+ [[nodiscard]] status_t getDefaultCompression(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ ui::Compression *outCompression) const override;
+
+ [[nodiscard]] status_t getDefaultInterlaced(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType *outInterlaced)
+ const override;
+
+ [[nodiscard]] status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Interlaced *outInterlaced) const override;
+
+ [[nodiscard]] status_t getDefaultChromaSiting(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType *outChromaSiting)
+ const override;
+
+ [[nodiscard]] status_t getDefaultChromaSiting(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ ui::ChromaSiting *outChromaSiting) const override;
+
+ [[nodiscard]] status_t getDefaultPlaneLayouts(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, std::vector<ui::PlaneLayout> *outPlaneLayouts) const override;
+
+private:
+ void unlockBlocking(buffer_handle_t bufferHandle) const;
+
+ AIMapper *mMapper = nullptr;
+};
+
+class Gralloc5Allocator : public GrallocAllocator {
+public:
+ Gralloc5Allocator(const Gralloc5Mapper &mapper);
+
+ [[nodiscard]] bool isLoaded() const override;
+
+ [[nodiscard]] std::string dumpDebugInfo(bool less) const override;
+
+ [[nodiscard]] status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t bufferCount, uint32_t *outStride,
+ buffer_handle_t *outBufferHandles,
+ bool importBuffers) const override;
+
+private:
+ const Gralloc5Mapper &mMapper;
+ std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAllocator;
+};
+
+} // namespace android
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 507fa35..51c6e92 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -42,9 +42,10 @@
{
public:
enum Version {
- GRALLOC_2,
+ GRALLOC_2 = 2,
GRALLOC_3,
GRALLOC_4,
+ GRALLOC_5,
};
static void preloadHal();
static inline GraphicBufferMapper& get() { return getInstance(); }
@@ -54,10 +55,11 @@
// The imported outHandle must be freed with freeBuffer when no longer
// needed. rawHandle is owned by the caller.
- status_t importBuffer(buffer_handle_t rawHandle,
- uint32_t width, uint32_t height, uint32_t layerCount,
- PixelFormat format, uint64_t usage, uint32_t stride,
- buffer_handle_t* outHandle);
+ status_t importBuffer(const native_handle_t* rawHandle, uint32_t width, uint32_t height,
+ uint32_t layerCount, PixelFormat format, uint64_t usage, uint32_t stride,
+ buffer_handle_t* outHandle);
+
+ status_t importBufferNoValidate(const native_handle_t* rawHandle, buffer_handle_t* outHandle);
status_t freeBuffer(buffer_handle_t handle);
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index d2c940f..d55ab28 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -197,6 +197,9 @@
// The keyboard layout association has changed.
CHANGE_KEYBOARD_LAYOUT_ASSOCIATION = 1 << 11,
+ // The stylus button reporting configurations has changed.
+ CHANGE_STYLUS_BUTTON_REPORTING = 1 << 12,
+
// All devices must be reopened.
CHANGE_MUST_REOPEN = 1 << 31,
};
@@ -309,6 +312,10 @@
// The set of currently disabled input devices.
std::set<int32_t> disabledDevices;
+ // True if stylus button reporting through motion events should be enabled, in which case
+ // stylus button state changes are reported through motion events.
+ bool stylusButtonMotionEventsEnabled;
+
InputReaderConfiguration()
: virtualKeyQuietTime(0),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f,
@@ -329,7 +336,8 @@
pointerGestureMovementSpeedRatio(0.8f),
pointerGestureZoomSpeedRatio(0.3f),
showTouches(false),
- pointerCaptureRequest() {}
+ pointerCaptureRequest(),
+ stylusButtonMotionEventsEnabled(true) {}
static std::string changesToString(uint32_t changes);
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/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 9a7af40..4d51aee 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -88,6 +88,14 @@
return rotatedDisplaySize;
}
+static int32_t filterButtonState(InputReaderConfiguration& config, int32_t buttonState) {
+ if (!config.stylusButtonMotionEventsEnabled) {
+ buttonState &=
+ ~(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY | AMOTION_EVENT_BUTTON_STYLUS_SECONDARY);
+ }
+ return buttonState;
+}
+
// --- RawPointerData ---
void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
@@ -1400,8 +1408,9 @@
next.readTime = readTime;
// Sync button state.
- next.buttonState =
- mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState();
+ next.buttonState = filterButtonState(mConfig,
+ mTouchButtonAccumulator.getButtonState() |
+ mCursorButtonAccumulator.getButtonState());
// Sync scroll
next.rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
@@ -1640,7 +1649,9 @@
void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus()) {
// If any of the external buttons are already pressed by the touch device, ignore them.
- const int32_t pressedButtons = ~mCurrentRawState.buttonState & mExternalStylusState.buttons;
+ const int32_t pressedButtons =
+ filterButtonState(mConfig,
+ ~mCurrentRawState.buttonState & mExternalStylusState.buttons);
const int32_t releasedButtons =
mExternalStylusButtonsApplied & ~mExternalStylusState.buttons;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index c563dba..3b51be8 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -16,11 +16,14 @@
#include "../Macros.h"
+#include <optional>
+
#include <android/input.h>
#include <linux/input-event-codes.h>
#include <log/log_main.h>
#include "TouchCursorInputMapperCommon.h"
#include "TouchpadInputMapper.h"
+#include "ui/Rotation.h"
namespace android {
@@ -89,7 +92,7 @@
mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
mPointerController(getContext()->getPointerController(getDeviceId())),
mStateConverter(deviceContext),
- mGestureConverter(*getContext(), getDeviceId()) {
+ mGestureConverter(*getContext(), deviceContext, getDeviceId()) {
mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
// Even though we don't explicitly delete copy/move semantics, it's safe to
@@ -111,6 +114,22 @@
return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD;
}
+std::list<NotifyArgs> TouchpadInputMapper::configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) {
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ std::optional<int32_t> displayId = mPointerController->getDisplayId();
+ ui::Rotation orientation = ui::ROTATION_0;
+ if (displayId.has_value()) {
+ if (auto viewport = config->getDisplayViewportById(*displayId); viewport) {
+ orientation = getInverseRotation(viewport->orientation);
+ }
+ }
+ mGestureConverter.setOrientation(orientation);
+ }
+ return {};
+}
+
std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
mStateConverter.reset();
mGestureConverter.reset();
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index b3bc831..3a92211 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -25,6 +25,7 @@
#include "EventHub.h"
#include "InputDevice.h"
#include "InputMapper.h"
+#include "InputReaderBase.h"
#include "NotifyArgs.h"
#include "gestures/GestureConverter.h"
#include "gestures/HardwareStateConverter.h"
@@ -39,6 +40,9 @@
~TouchpadInputMapper();
uint32_t getSources() const override;
+ [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 23216d3..ffc0523 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -17,6 +17,7 @@
#include "gestures/GestureConverter.h"
#include <android/input.h>
+#include <linux/input-event-codes.h>
#include "TouchCursorInputMapperCommon.h"
#include "input/Input.h"
@@ -44,10 +45,14 @@
} // namespace
-GestureConverter::GestureConverter(InputReaderContext& readerContext, int32_t deviceId)
+GestureConverter::GestureConverter(InputReaderContext& readerContext,
+ const InputDeviceContext& deviceContext, int32_t deviceId)
: mDeviceId(deviceId),
mReaderContext(readerContext),
- mPointerController(readerContext.getPointerController(deviceId)) {}
+ mPointerController(readerContext.getPointerController(deviceId)) {
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
+}
void GestureConverter::reset() {
mButtonState = 0;
@@ -60,6 +65,15 @@
return {handleMove(when, readTime, gesture)};
case kGestureTypeButtonsChange:
return handleButtonsChange(when, readTime, gesture);
+ case kGestureTypeSwipe:
+ return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx,
+ gesture.details.swipe.dy);
+ case kGestureTypeFourFingerSwipe:
+ return handleMultiFingerSwipe(when, readTime, 4, gesture.details.four_finger_swipe.dx,
+ gesture.details.four_finger_swipe.dy);
+ case kGestureTypeSwipeLift:
+ case kGestureTypeFourFingerSwipeLift:
+ return handleMultiFingerSwipeLift(when, readTime);
default:
// TODO(b/251196347): handle more gesture types.
return {};
@@ -67,13 +81,12 @@
}
NotifyArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
- PointerProperties props;
- props.clear();
- props.id = 0;
- props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ float deltaX = gesture.details.move.dx;
+ float deltaY = gesture.details.move.dy;
+ rotateDelta(mOrientation, &deltaX, &deltaY);
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
- mPointerController->move(gesture.details.move.dx, gesture.details.move.dy);
+ mPointerController->move(deltaX, deltaY);
mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
float xCursorPosition, yCursorPosition;
mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
@@ -82,14 +95,15 @@
coords.clear();
coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
- coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, gesture.details.move.dx);
- coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, gesture.details.move.dy);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
const bool down = isPointerDown(mButtonState);
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState,
- /* pointerCount= */ 1, &props, &coords, xCursorPosition, yCursorPosition);
+ /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition,
+ yCursorPosition);
}
std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime,
@@ -99,11 +113,6 @@
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- PointerProperties props;
- props.clear();
- props.id = 0;
- props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
float xCursorPosition, yCursorPosition;
mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
@@ -127,15 +136,16 @@
newButtonState |= actionButton;
pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
actionButton, newButtonState,
- /* pointerCount= */ 1, &props, &coords,
- xCursorPosition, yCursorPosition));
+ /* pointerCount= */ 1, mFingerProps.data(),
+ &coords, xCursorPosition, yCursorPosition));
}
}
if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
mDownTime = when;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
/* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
- &props, &coords, xCursorPosition, yCursorPosition));
+ mFingerProps.data(), &coords, xCursorPosition,
+ yCursorPosition));
}
out.splice(out.end(), pressEvents);
@@ -151,19 +161,109 @@
newButtonState &= ~actionButton;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
actionButton, newButtonState, /* pointerCount= */ 1,
- &props, &coords, xCursorPosition, yCursorPosition));
+ mFingerProps.data(), &coords, xCursorPosition,
+ yCursorPosition));
}
}
if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
- newButtonState, /* pointerCount= */ 1, &props, &coords,
- xCursorPosition, yCursorPosition));
+ newButtonState, /* pointerCount= */ 1, mFingerProps.data(),
+ &coords, xCursorPosition, yCursorPosition));
}
mButtonState = newButtonState;
return out;
}
+[[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when,
+ nsecs_t readTime,
+ uint32_t fingerCount,
+ float dx, float dy) {
+ std::list<NotifyArgs> out = {};
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
+ // If the user changes the number of fingers mid-way through a swipe (e.g. they start with
+ // three and then put a fourth finger down), the gesture library will treat it as two
+ // separate swipes with an appropriate lift event between them, so we don't have to worry
+ // about the finger count changing mid-swipe.
+ mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE;
+ mSwipeFingerCount = fingerCount;
+
+ constexpr float FAKE_FINGER_SPACING = 100;
+ float xCoord = xCursorPosition - FAKE_FINGER_SPACING * (mSwipeFingerCount - 1) / 2;
+ for (size_t i = 0; i < mSwipeFingerCount; i++) {
+ PointerCoords& coords = mFakeFingerCoords[i];
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCoord);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+ xCoord += FAKE_FINGER_SPACING;
+ }
+
+ mDownTime = when;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
+ /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
+ mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
+ yCursorPosition));
+ for (size_t i = 1; i < mSwipeFingerCount; i++) {
+ out.push_back(makeMotionArgs(when, readTime,
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ /* actionButton= */ 0, mButtonState,
+ /* pointerCount= */ i + 1, mFingerProps.data(),
+ mFakeFingerCoords.data(), xCursorPosition,
+ yCursorPosition));
+ }
+ }
+ for (size_t i = 0; i < mSwipeFingerCount; i++) {
+ PointerCoords& coords = mFakeFingerCoords[i];
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) + dx);
+ // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate
+ // the Y values.
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) - dy);
+ }
+ float xOffset = dx / (mXAxisInfo.maxValue - mXAxisInfo.minValue);
+ // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate the Y
+ // values.
+ float yOffset = -dy / (mYAxisInfo.maxValue - mYAxisInfo.minValue);
+ mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
+ mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
+ mButtonState, /* pointerCount= */ mSwipeFingerCount,
+ mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
+ yCursorPosition));
+ return out;
+}
+
+[[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipeLift(nsecs_t when,
+ nsecs_t readTime) {
+ std::list<NotifyArgs> out = {};
+ if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
+ return out;
+ }
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
+ mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
+
+ for (size_t i = mSwipeFingerCount; i > 1; i--) {
+ out.push_back(makeMotionArgs(when, readTime,
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ ((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ /* actionButton= */ 0, mButtonState, /* pointerCount= */ i,
+ mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
+ yCursorPosition));
+ }
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP,
+ /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
+ mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
+ yCursorPosition));
+ mCurrentClassification = MotionClassification::NONE;
+ mSwipeFingerCount = 0;
+ return out;
+}
+
NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
int32_t actionButton, int32_t buttonState,
uint32_t pointerCount,
@@ -177,10 +277,10 @@
mPointerController->getDisplayId(), /* policyFlags= */ 0, action,
/* actionButton= */ actionButton, /* flags= */ 0,
mReaderContext.getGlobalMetaState(), buttonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
- pointerProperties, pointerCoords,
- /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition,
- yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {});
+ mCurrentClassification, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
+ pointerProperties, pointerCoords, /* xPrecision= */ 1.0f,
+ /* yPrecision= */ 1.0f, xCursorPosition, yCursorPosition,
+ /* downTime= */ mDownTime, /* videoFrames= */ {});
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index dc11f24..ae5581d 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -16,14 +16,18 @@
#pragma once
+#include <array>
#include <list>
#include <memory>
#include <PointerControllerInterface.h>
#include <utils/Timers.h>
+#include "EventHub.h"
+#include "InputDevice.h"
#include "InputReaderContext.h"
#include "NotifyArgs.h"
+#include "ui/Rotation.h"
#include "include/gestures.h"
@@ -33,8 +37,10 @@
// PointerController calls.
class GestureConverter {
public:
- GestureConverter(InputReaderContext& readerContext, int32_t deviceId);
+ GestureConverter(InputReaderContext& readerContext, const InputDeviceContext& deviceContext,
+ int32_t deviceId);
+ void setOrientation(ui::Rotation orientation) { mOrientation = orientation; }
void reset();
[[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
@@ -44,6 +50,10 @@
NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
+ [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime,
+ uint32_t fingerCount, float dx,
+ float dy);
+ [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipeLift(nsecs_t when, nsecs_t readTime);
NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
int32_t actionButton, int32_t buttonState,
@@ -56,10 +66,27 @@
InputReaderContext& mReaderContext;
std::shared_ptr<PointerControllerInterface> mPointerController;
+ ui::Rotation mOrientation = ui::ROTATION_0;
+ RawAbsoluteAxisInfo mXAxisInfo;
+ RawAbsoluteAxisInfo mYAxisInfo;
+
// The current button state according to the gestures library, but converted into MotionEvent
// button values (AMOTION_EVENT_BUTTON_...).
uint32_t mButtonState = 0;
nsecs_t mDownTime = 0;
+
+ MotionClassification mCurrentClassification = MotionClassification::NONE;
+ uint32_t mSwipeFingerCount = 0;
+ static constexpr size_t MAX_FAKE_FINGERS = 4;
+ // We never need any PointerProperties other than the finger tool type, so we can just keep a
+ // const array of them.
+ const std::array<PointerProperties, MAX_FAKE_FINGERS> mFingerProps = {{
+ {.id = 0, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+ {.id = 1, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+ {.id = 2, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+ {.id = 3, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+ }};
+ std::array<PointerCoords, MAX_FAKE_FINGERS> mFakeFingerCoords = {};
};
} // namespace android
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/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index f755356..bb8a30e 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -201,6 +201,10 @@
mConfig.wheelVelocityControlParameters = params;
}
+void FakeInputReaderPolicy::setStylusButtonMotionEventsEnabled(bool enabled) {
+ mConfig.stylusButtonMotionEventsEnabled = enabled;
+}
+
void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) {
*outConfig = mConfig;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 862ff0b..9ec3217 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -75,6 +75,7 @@
float getPointerGestureMovementSpeedRatio();
float getPointerGestureZoomSpeedRatio();
void setVelocityControlParams(const VelocityControlParameters& params);
+ void setStylusButtonMotionEventsEnabled(bool enabled);
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 91efd1a..1c7ec76 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -29,6 +29,7 @@
#include "TestInputListener.h"
#include "TestInputListenerMatchers.h"
#include "include/gestures.h"
+#include "ui/Rotation.h"
namespace android {
@@ -37,6 +38,7 @@
class GestureConverterTest : public testing::Test {
protected:
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t EVENTHUB_ID = 1;
static constexpr stime_t ARBITRARY_GESTURE_TIME = 1.2;
static constexpr float POINTER_X = 100;
static constexpr float POINTER_Y = 200;
@@ -47,6 +49,9 @@
mFakeListener = std::make_unique<TestInputListener>();
mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
*mFakeListener);
+ mDevice = newDevice();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, -500, 500, 0, 0, 20);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, -500, 500, 0, 0, 20);
mFakePointerController = std::make_shared<FakePointerController>();
mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
@@ -54,15 +59,32 @@
mFakePolicy->setPointerController(mFakePointerController);
}
+ std::shared_ptr<InputDevice> newDevice() {
+ InputDeviceIdentifier identifier;
+ identifier.name = "device";
+ identifier.location = "USB1";
+ identifier.bus = 0;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, /* generation= */ 2,
+ identifier);
+ mReader->pushNextDevice(device);
+ mFakeEventHub->addDevice(EVENTHUB_ID, identifier.name, InputDeviceClass::TOUCHPAD,
+ identifier.bus);
+ mReader->loopOnce();
+ return device;
+ }
+
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
std::unique_ptr<TestInputListener> mFakeListener;
std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
std::shared_ptr<FakePointerController> mFakePointerController;
};
TEST_F(GestureConverterTest, Move) {
- GestureConverter converter(*mReader->getContext(), DEVICE_ID);
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
@@ -77,8 +99,27 @@
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(95, 210));
}
+TEST_F(GestureConverterTest, Move_Rotated) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setOrientation(ui::ROTATION_90);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithButtonState(0),
+ WithPressure(0.0f)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110, 205));
+}
+
TEST_F(GestureConverterTest, ButtonsChange) {
- GestureConverter converter(*mReader->getContext(), DEVICE_ID);
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
// Press left and right buttons at once
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -143,7 +184,8 @@
}
TEST_F(GestureConverterTest, DragWithButton) {
- GestureConverter converter(*mReader->getContext(), DEVICE_ID);
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
// Press the button
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -197,4 +239,228 @@
WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
}
+TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAndOffsetsAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
+ /* dy= */ 0);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ -5,
+ /* dy= */ 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionClassification(MotionClassification::NONE),
+ WithGestureOffset(0, 0, EPSILON)));
+}
+
+TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) {
+ // The gestures library will "lock" a swipe into the dimension it starts in. For example, if you
+ // start swiping up and then start moving left or right, it'll return gesture events with only Y
+ // deltas until you lift your fingers and start swiping again. That's why each of these tests
+ // only checks movement in one dimension.
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
+ /* dy= */ 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ ASSERT_EQ(4u, args.size());
+
+ // Three fake fingers should be created. We don't actually care where they are, so long as they
+ // move appropriately.
+ NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ PointerCoords finger0Start = arg.pointerCoords[0];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ PointerCoords finger1Start = arg.pointerCoords[1];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ PointerCoords finger2Start = arg.pointerCoords[2];
+ args.pop_front();
+
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0, -0.01, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 10);
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 10);
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 10);
+
+ Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 0, /* dy= */ 5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ ASSERT_EQ(1u, args.size());
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0, -0.005, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 15);
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 15);
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15);
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
+TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 10, /* dy= */ 0);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ ASSERT_EQ(5u, args.size());
+
+ // Four fake fingers should be created. We don't actually care where they are, so long as they
+ // move appropriately.
+ NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ PointerCoords finger0Start = arg.pointerCoords[0];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ PointerCoords finger1Start = arg.pointerCoords[1];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ PointerCoords finger2Start = arg.pointerCoords[2];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ PointerCoords finger3Start = arg.pointerCoords[3];
+ args.pop_front();
+
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0.01, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
+ EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY());
+
+ Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 5, /* dy= */ 0);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ ASSERT_EQ(1u, args.size());
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0.005, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
+ EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY());
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+ ASSERT_EQ(4u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 96d27b8..95d35f4 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;
@@ -2023,6 +2020,56 @@
WithDeviceId(touchscreenId))));
}
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonMotionEventsDisabled) {
+ TestFixture::mFakePolicy->setStylusButtonMotionEventsEnabled(false);
+ TestFixture::mReader->requestRefreshConfiguration(
+ InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING);
+
+ const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
+ const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
+ const auto stylusId = TestFixture::mStylusInfo.getId();
+
+ // Start a stylus gesture. By the time this event is processed, the configuration change that
+ // was requested is guaranteed to be completed.
+ TestFixture::mTouchscreen->sendSlot(FIRST_SLOT);
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendDown(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ // Press and release a stylus button. Each change only generates a MOVE motion event.
+ // Key events are unaffected.
+ TestFixture::mStylus->pressKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ TestFixture::mStylus->releaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ // Finish the stylus gesture.
+ TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+}
+
// --- ExternalStylusIntegrationTest ---
// Verify the behavior of an external stylus. An external stylus can report pressure or button
@@ -2240,17 +2287,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 +3636,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 {
@@ -6577,6 +6627,46 @@
WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0))));
}
+TEST_F(SingleTouchInputMapperTest, StylusButtonMotionEventsDisabled) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+
+ mFakePolicy->setStylusButtonMotionEventsEnabled(false);
+
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ // Press a stylus button.
+ processKey(mapper, BTN_STYLUS, 1);
+ processSync(mapper);
+
+ // Start a touch gesture and ensure that the stylus button is not reported.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithButtonState(0))));
+
+ // Release and press the stylus button again.
+ processKey(mapper, BTN_STYLUS, 0);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+ processKey(mapper, BTN_STYLUS, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+
+ // Release the touch gesture.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsSetToTouchNavigation_setsCorrectType) {
mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation");
prepareDisplay(ui::ROTATION_0);
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index e5a4b14..64c2c75 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -16,6 +16,8 @@
#pragma once
+#include <cmath>
+
#include <android/input.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -66,6 +68,11 @@
return arg.keyCode == keyCode;
}
+MATCHER_P(WithPointerCount, count, "MotionEvent with specified number of pointers") {
+ *result_listener << "expected " << count << " pointer(s), but got " << arg.pointerCount;
+ return arg.pointerCount == count;
+}
+
MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {
const auto argX = arg.pointerCoords[0].getX();
const auto argY = arg.pointerCoords[0].getY();
@@ -82,6 +89,17 @@
return argX == x && argY == y;
}
+MATCHER_P3(WithGestureOffset, dx, dy, epsilon,
+ "InputEvent with specified touchpad gesture offset") {
+ const auto argGestureX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET);
+ const auto argGestureY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET);
+ const double xDiff = fabs(argGestureX - dx);
+ const double yDiff = fabs(argGestureY - dy);
+ *result_listener << "expected gesture offset (" << dx << ", " << dy << ") within " << epsilon
+ << ", but got (" << argGestureX << ", " << argGestureY << ")";
+ return xDiff <= epsilon && yDiff <= epsilon;
+}
+
MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
*result_listener << "expected pressure " << pressure << ", but got " << argPressure;
@@ -100,6 +118,13 @@
return arg.flags == flags;
}
+MATCHER_P(WithMotionClassification, classification,
+ "InputEvent with specified MotionClassification") {
+ *result_listener << "expected classification " << motionClassificationToString(classification)
+ << ", but got " << motionClassificationToString(arg.classification);
+ return arg.classification == classification;
+}
+
MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") {
*result_listener << "expected button state " << buttons << ", but got " << arg.buttonState;
return arg.buttonState == buttons;
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/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 82d0295..a3a62a1 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -850,6 +850,11 @@
// Unregister call backs.
return 0;
}
+ if (!mService->isWhiteListedPackage(mPackageName)) {
+ ALOGE("App not allowed to inject data, dropping event"
+ "package=%s uid=%d", mPackageName.string(), mUid);
+ return 0;
+ }
sensors_event_t sensor_event;
memcpy(&sensor_event, buf, sizeof(sensors_event_t));
std::shared_ptr<SensorInterface> si =
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 6504b79..0cfc72f 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -52,6 +52,7 @@
#include "SensorEventConnection.h"
#include "SensorRecord.h"
#include "SensorRegistrationInfo.h"
+#include "SensorServiceUtils.h"
#include <inttypes.h>
#include <math.h>
@@ -571,7 +572,11 @@
// enable sensors and recover all sensor direct report
enableAllSensorsLocked(&connLock);
}
- if (mCurrentOperatingMode == DATA_INJECTION) {
+ if (mCurrentOperatingMode == REPLAY_DATA_INJECTION) {
+ dev.disableAllSensors();
+ }
+ if (mCurrentOperatingMode == DATA_INJECTION ||
+ mCurrentOperatingMode == REPLAY_DATA_INJECTION) {
resetToNormalModeLocked();
}
mWhiteListedPackage.clear();
@@ -595,6 +600,27 @@
// Transition to data injection mode supported only from NORMAL mode.
return INVALID_OPERATION;
}
+ } else if (args.size() == 2 && args[0] == String16("replay_data_injection")
+ && !SensorServiceUtil::isUserBuild()) {
+ if (mCurrentOperatingMode == NORMAL) {
+ dev.disableAllSensors();
+ // Use DATA_INJECTION here since this value goes to the HAL and the HAL doesn't
+ // have an understanding of replay vs. normal data injection.
+ status_t err = dev.setMode(DATA_INJECTION);
+ if (err == NO_ERROR) {
+ mCurrentOperatingMode = REPLAY_DATA_INJECTION;
+ }
+ // Re-enable sensors.
+ dev.enableAllSensors();
+ mWhiteListedPackage.setTo(String8(args[1]));
+ return NO_ERROR;
+ } else if (mCurrentOperatingMode == REPLAY_DATA_INJECTION) {
+ // Already in REPLAY_DATA_INJECTION mode. Treat this as a no_op.
+ return NO_ERROR;
+ } else {
+ // Transition to data injection mode supported only from NORMAL mode.
+ return INVALID_OPERATION;
+ }
} else if (args.size() == 1 && args[0] == String16("--proto")) {
return dumpProtoLocked(fd, &connLock);
} else if (!mSensors.hasAnySensor()) {
@@ -658,6 +684,14 @@
break;
case DATA_INJECTION:
result.appendFormat(" DATA_INJECTION : %s\n", mWhiteListedPackage.string());
+ break;
+ case REPLAY_DATA_INJECTION:
+ result.appendFormat(" REPLAY_DATA_INJECTION : %s\n",
+ mWhiteListedPackage.string());
+ break;
+ default:
+ result.appendFormat(" UNKNOWN\n");
+ break;
}
result.appendFormat("Sensor Privacy: %s\n",
mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
@@ -1498,8 +1532,10 @@
sp<ISensorEventConnection> SensorService::createSensorEventConnection(const String8& packageName,
int requestedMode, const String16& opPackageName, const String16& attributionTag) {
- // Only 2 modes supported for a SensorEventConnection ... NORMAL and DATA_INJECTION.
- if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) {
+ // Only 3 modes supported for a SensorEventConnection ... NORMAL, DATA_INJECTION and
+ // REPLAY_DATA_INJECTION.
+ if (requestedMode != NORMAL && requestedMode != DATA_INJECTION &&
+ requestedMode != REPLAY_DATA_INJECTION) {
return nullptr;
}
resetTargetSdkVersionCache(opPackageName);
@@ -1520,8 +1556,9 @@
String16 connOpPackageName =
(opPackageName == String16("")) ? String16(connPackageName) : opPackageName;
sp<SensorEventConnection> result(new SensorEventConnection(this, uid, connPackageName,
- requestedMode == DATA_INJECTION, connOpPackageName, attributionTag));
- if (requestedMode == DATA_INJECTION) {
+ requestedMode == DATA_INJECTION || requestedMode == REPLAY_DATA_INJECTION,
+ connOpPackageName, attributionTag));
+ if (requestedMode == DATA_INJECTION || requestedMode == REPLAY_DATA_INJECTION) {
mConnectionHolder.addEventConnectionIfNotPresent(result);
// Add the associated file descriptor to the Looper for polling whenever there is data to
// be injected.
@@ -1880,8 +1917,8 @@
}
ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
- if (mCurrentOperatingMode != NORMAL
- && !isWhiteListedPackage(connection->getPackageName())) {
+ if (mCurrentOperatingMode != NORMAL && mCurrentOperatingMode != REPLAY_DATA_INJECTION &&
+ !isWhiteListedPackage(connection->getPackageName())) {
return INVALID_OPERATION;
}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 78df501..64bc377 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -102,8 +102,7 @@
// Step Detector etc. Typically in this mode, there will be a client (a
// SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps can
// unregister and register for any sensor that supports injection. Registering to sensors
- // that do not support injection will give an error. TODO: Allow exactly one
- // client to inject sensor data at a time.
+ // that do not support injection will give an error.
DATA_INJECTION = 1,
// This mode is used only for testing sensors. Each sensor can be tested in isolation with
// the required sampling_rate and maxReportLatency parameters without having to think about
@@ -116,10 +115,14 @@
// corresponding parameters if the application hasn't unregistered for sensors in the mean
// time. NOTE: Non allowlisted app whose sensors were previously deactivated may still
// receive events if a allowlisted app requests data from the same sensor.
- RESTRICTED = 2
+ RESTRICTED = 2,
+ // Mostly equivalent to DATA_INJECTION with the difference being that the injected data is
+ // delivered to all requesting apps rather than just the package allowed to inject data.
+ // This mode is only allowed to be used on development builds.
+ REPLAY_DATA_INJECTION = 3,
// State Transitions supported.
- // RESTRICTED <--- NORMAL ---> DATA_INJECTION
+ // RESTRICTED <--- NORMAL ---> DATA_INJECTION/REPLAY_DATA_INJECTION
// ---> <---
// Shell commands to switch modes in SensorService.
diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp
index 6bad962..46b4b5b 100644
--- a/services/sensorservice/SensorServiceUtils.cpp
+++ b/services/sensorservice/SensorServiceUtils.cpp
@@ -16,6 +16,7 @@
#include "SensorServiceUtils.h"
+#include <android-base/properties.h>
#include <hardware/sensors.h>
namespace android {
@@ -76,5 +77,10 @@
}
}
+bool isUserBuild() {
+ std::string buildType = android::base::GetProperty("ro.build.type", "user");
+ return "user" == buildType;
+}
+
} // namespace SensorServiceUtil
} // namespace android;
diff --git a/services/sensorservice/SensorServiceUtils.h b/services/sensorservice/SensorServiceUtils.h
index 49457cf..a6e0d6b 100644
--- a/services/sensorservice/SensorServiceUtils.h
+++ b/services/sensorservice/SensorServiceUtils.h
@@ -38,6 +38,11 @@
size_t eventSizeBySensorType(int type);
+/**
+ * Returns true if on a user (production) build.
+ */
+bool isUserBuild();
+
} // namespace SensorServiceUtil
} // namespace android;
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/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index a05d3df..21f5c68 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -180,7 +180,7 @@
for (auto divisor = start; divisor <= end; divisor++) {
const auto fps = mode->getFps() / divisor;
using fps_approx_ops::operator<;
- if (fps < kMinSupportedFrameRate) {
+ if (divisor > 1 && fps < kMinSupportedFrameRate) {
break;
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 14d08f8..4f5842a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -61,7 +61,7 @@
std::chrono::nanoseconds(800us).count();
// The lowest Render Frame Rate that will ever be selected
- static constexpr Fps kMinSupportedFrameRate = 1_Hz;
+ static constexpr Fps kMinSupportedFrameRate = 20_Hz;
class Policy {
static constexpr int kAllowGroupSwitchingDefault = false;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3dd86aa..b930477 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -440,9 +440,6 @@
property_get("debug.sf.treat_170m_as_sRGB", value, "0");
mTreat170mAsSrgb = atoi(value);
- mIgnoreHwcPhysicalDisplayOrientation =
- base::GetBoolProperty("debug.sf.ignore_hwc_physical_display_orientation"s, false);
-
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
// comes online to attempt to read the property. The property is
@@ -2445,8 +2442,7 @@
if (!id) {
return ui::ROTATION_0;
}
- if (!mIgnoreHwcPhysicalDisplayOrientation &&
- getHwComposer().getComposer()->isSupported(
+ if (getHwComposer().getComposer()->isSupported(
Hwc2::Composer::OptionalFeature::PhysicalDisplayOrientation)) {
switch (getHwComposer().getPhysicalDisplayOrientation(*id)) {
case Hwc2::AidlTransform::ROT_90:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 23ac19e..7bb0514 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -311,11 +311,6 @@
// on this behavior to increase contrast for some media sources.
bool mTreat170mAsSrgb = false;
- // Allows to ignore physical orientation provided through hwc API in favour of
- // 'ro.surface_flinger.primary_display_orientation'.
- // TODO(b/246793311): Clean up a temporary property
- bool mIgnoreHwcPhysicalDisplayOrientation = false;
-
protected:
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();
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 a78f42c..06f45f9 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -141,12 +141,6 @@
RefreshRateSelectorTest();
~RefreshRateSelectorTest();
- // Represents the number of refresh rates possible
- // from 1_Hz to 90_hz, including fractional rates.
- static constexpr size_t kTotalRefreshRates120 = 120;
- // Represents the number of refresh rates possible
- // from 1_Hz to 120_hz, including fractional rates.
- static constexpr size_t kTotalRefreshRates216 = 216;
static constexpr DisplayModeId kModeId60{0};
static constexpr DisplayModeId kModeId90{1};
static constexpr DisplayModeId kModeId72{2};
@@ -159,6 +153,9 @@
static constexpr DisplayModeId kModeId30Frac{9};
static constexpr DisplayModeId kModeId60Frac{10};
static constexpr DisplayModeId kModeId35{11};
+ static constexpr DisplayModeId kModeId1{12};
+ static constexpr DisplayModeId kModeId5{13};
+ static constexpr DisplayModeId kModeId10{14};
static inline const ftl::NonNull<DisplayModePtr> kMode60 =
ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
@@ -196,6 +193,12 @@
ftl::as_non_null(createDisplayMode(kModeId24, 24_Hz));
static inline const ftl::NonNull<DisplayModePtr> kMode24Frac =
ftl::as_non_null(createDisplayMode(kModeId24Frac, 23.976_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode1 =
+ ftl::as_non_null(createDisplayMode(kModeId1, 1_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode5 =
+ ftl::as_non_null(createDisplayMode(kModeId5, 5_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode10 =
+ ftl::as_non_null(createDisplayMode(kModeId10, 10_Hz));
// Test configurations.
static inline const DisplayModes kModes_60 = makeModes(kMode60);
@@ -218,6 +221,7 @@
static inline const DisplayModes kModes_25_30_50_60 =
makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50);
static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120);
+ static inline const DisplayModes kModes_1_5_10 = makeModes(kMode1, kMode5, kMode10);
// This is a typical TV configuration.
static inline const DisplayModes kModes_24_25_30_50_60_Frac =
@@ -230,12 +234,6 @@
config.enableFrameRateOverride = GetParam();
return TestableRefreshRateSelector(modes, activeModeId, config);
}
-
- // TODO(b/262783137) Test the complete range of refresh rates
- struct FpsBeginEnd {
- std::vector<std::pair<Fps, Fps>> begin;
- std::vector<std::pair<Fps, Fps>> end;
- };
};
RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -1131,39 +1129,24 @@
const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
RefreshRateOrder::Descending);
- const auto expectedRefreshRates = []() -> FpsBeginEnd {
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
switch (GetParam()) {
case Config::FrameRateOverride::Disabled:
case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
case Config::FrameRateOverride::AppOverride:
- return {.begin{{90_Hz, 90_Hz}, {60_Hz, 60_Hz}, {30_Hz, 30_Hz}}};
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
case Config::FrameRateOverride::Enabled:
- return {.begin{{90_Hz, 90_Hz}, {60_Hz, 60_Hz}, {45_Hz, 90_Hz}, {30_Hz, 30_Hz}},
- .end{{1.022_Hz, 90_Hz},
- {1.016_Hz, 60_Hz},
- {1.011_Hz, 90_Hz},
- {1_Hz, 30_Hz}}};
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
}
}();
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates120, refreshRates.size());
- } else {
- ASSERT_EQ(expectedRefreshRates.begin.size(), refreshRates.size());
- }
-
- for (size_t i = 0; i < expectedRefreshRates.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.begin[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[i].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate, refreshRates[i].frameRateMode.modePtr->getFps());
- }
-
- for (size_t i = 0; i < expectedRefreshRates.end.size(); i++) {
- const size_t refreshRateIndex = refreshRates.size() - expectedRefreshRates.end.size() + i;
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.end[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[refreshRateIndex].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate,
- refreshRates[refreshRateIndex].frameRateMode.modePtr->getFps());
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
@@ -1175,39 +1158,24 @@
const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
RefreshRateOrder::Ascending);
- const auto expectedRefreshRates = []() -> FpsBeginEnd {
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
switch (GetParam()) {
case Config::FrameRateOverride::Disabled:
case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
case Config::FrameRateOverride::AppOverride:
- return {.begin{{30_Hz, 30_Hz}, {60_Hz, 60_Hz}, {90_Hz, 90_Hz}}};
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
case Config::FrameRateOverride::Enabled:
- return {.begin{{1_Hz, 30_Hz},
- {1.011_Hz, 90_Hz},
- {1.016_Hz, 60_Hz},
- {1.022_Hz, 90_Hz}},
- .end{{30_Hz, 30_Hz}, {45_Hz, 90_Hz}, {60_Hz, 60_Hz}, {90_Hz, 90_Hz}}};
+ return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}};
}
}();
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates120, refreshRates.size());
- } else {
- ASSERT_EQ(expectedRefreshRates.begin.size(), refreshRates.size());
- }
-
- for (size_t i = 0; i < expectedRefreshRates.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.begin[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[i].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate, refreshRates[i].frameRateMode.modePtr->getFps());
- }
-
- for (size_t i = 0; i < expectedRefreshRates.end.size(); i++) {
- const size_t refreshRateIndex = refreshRates.size() - expectedRefreshRates.end.size() + i;
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.end[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[refreshRateIndex].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate,
- refreshRates[refreshRateIndex].frameRateMode.modePtr->getFps());
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
@@ -1281,68 +1249,39 @@
auto [refreshRates, signals] = selector.getRankedFrameRates({}, {});
EXPECT_FALSE(signals.powerOnImminent);
- auto expectedRefreshRates = []() -> FpsBeginEnd {
+ auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
switch (GetParam()) {
case Config::FrameRateOverride::Disabled:
case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
case Config::FrameRateOverride::AppOverride:
- return {.begin{{90_Hz, 90_Hz}, {60_Hz, 60_Hz}}};
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}};
case Config::FrameRateOverride::Enabled:
- return {.begin{{90_Hz, 90_Hz},
- {60_Hz, 60_Hz},
- {45_Hz, 90_Hz},
- {30_Hz, 60_Hz},
- {22.5_Hz, 90_Hz},
- {20_Hz, 60_Hz}},
- .end{{1.022_Hz, 90_Hz},
- {1.016_Hz, 60_Hz},
- {1.011_Hz, 90_Hz},
- {1_Hz, 60_Hz}}};
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90},
+ {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
}
}();
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates120, refreshRates.size());
- } else {
- ASSERT_EQ(expectedRefreshRates.begin.size(), refreshRates.size());
- }
-
- for (size_t i = 0; i < expectedRefreshRates.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.begin[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[i].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate, refreshRates[i].frameRateMode.modePtr->getFps());
- }
-
- for (size_t i = 0; i < expectedRefreshRates.end.size(); i++) {
- const size_t refreshRateIndex = refreshRates.size() - expectedRefreshRates.end.size() + i;
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.end[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[refreshRateIndex].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate,
- refreshRates[refreshRateIndex].frameRateMode.modePtr->getFps());
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
std::tie(refreshRates, signals) =
selector.getRankedRefreshRatesAsPair({}, {.powerOnImminent = true});
EXPECT_TRUE(signals.powerOnImminent);
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates120, refreshRates.size());
- } else {
- ASSERT_EQ(expectedRefreshRates.begin.size(), refreshRates.size());
- }
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- for (size_t i = 0; i < expectedRefreshRates.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.begin[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[i].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate, refreshRates[i].frameRateMode.modePtr->getFps());
- }
-
- for (size_t i = 0; i < expectedRefreshRates.end.size(); i++) {
- const size_t refreshRateIndex = refreshRates.size() - expectedRefreshRates.end.size() + i;
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.end[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[refreshRateIndex].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate,
- refreshRates[refreshRateIndex].frameRateMode.modePtr->getFps());
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1355,68 +1294,39 @@
selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = true});
EXPECT_TRUE(signals.powerOnImminent);
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates120, refreshRates.size());
- } else {
- ASSERT_EQ(expectedRefreshRates.begin.size(), refreshRates.size());
- }
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- for (size_t i = 0; i < expectedRefreshRates.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.begin[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[i].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate, refreshRates[i].frameRateMode.modePtr->getFps());
- }
-
- for (size_t i = 0; i < expectedRefreshRates.end.size(); i++) {
- const size_t refreshRateIndex = refreshRates.size() - expectedRefreshRates.end.size() + i;
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.end[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[refreshRateIndex].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate,
- refreshRates[refreshRateIndex].frameRateMode.modePtr->getFps());
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
std::tie(refreshRates, signals) =
selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false});
EXPECT_FALSE(signals.powerOnImminent);
- expectedRefreshRates = []() -> FpsBeginEnd {
+ expectedRefreshRates = []() -> std::vector<FrameRateMode> {
switch (GetParam()) {
case Config::FrameRateOverride::Disabled:
case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
case Config::FrameRateOverride::AppOverride:
- return {.begin{{60_Hz, 60_Hz}, {90_Hz, 90_Hz}}};
+ return {{60_Hz, kMode60}, {90_Hz, kMode90}};
case Config::FrameRateOverride::Enabled:
- return {.begin{{60_Hz, 60_Hz},
- {90_Hz, 90_Hz},
- {45_Hz, 90_Hz},
- {30_Hz, 60_Hz},
- {22.5_Hz, 90_Hz},
- {20_Hz, 60_Hz}},
- .end{{1.034_Hz, 60_Hz},
- {1.046_Hz, 90_Hz},
- {1.052_Hz, 60_Hz},
- {1.058_Hz, 90_Hz}}};
+ return {{60_Hz, kMode60}, {90_Hz, kMode90}, {45_Hz, kMode90},
+ {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
}
}();
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates120, refreshRates.size());
- } else {
- ASSERT_EQ(expectedRefreshRates.begin.size(), refreshRates.size());
- }
-
- for (size_t i = 0; i < expectedRefreshRates.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.begin[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[i].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate, refreshRates[i].frameRateMode.modePtr->getFps());
- }
-
- for (size_t i = 0; i < expectedRefreshRates.end.size(); i++) {
- const size_t refreshRateIndex = refreshRates.size() - expectedRefreshRates.end.size() + i;
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRefreshRates.end[i];
- EXPECT_EQ(expectedRenderRate, refreshRates[refreshRateIndex].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate,
- refreshRates[refreshRateIndex].frameRateMode.modePtr->getFps());
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
@@ -1645,55 +1555,31 @@
lr5.name = "30Hz";
lr5.focused = true;
- auto expectedRanking = []() -> FpsBeginEnd {
+ auto expectedRanking = []() -> std::vector<FrameRateMode> {
switch (GetParam()) {
case Config::FrameRateOverride::Disabled:
case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
case Config::FrameRateOverride::AppOverride:
- return {.begin{{120_Hz, 120_Hz},
- {90_Hz, 90_Hz},
- {72_Hz, 72_Hz},
- {60_Hz, 60_Hz},
- {30_Hz, 30_Hz}}};
+ return {{120_Hz, kMode120},
+ {90_Hz, kMode90},
+ {72_Hz, kMode72},
+ {60_Hz, kMode60},
+ {30_Hz, kMode30}};
case Config::FrameRateOverride::Enabled:
- return {.begin{{120_Hz, 120_Hz},
- {90_Hz, 90_Hz},
- {72_Hz, 72_Hz},
- {60_Hz, 60_Hz},
- {45_Hz, 90_Hz},
- {40_Hz, 120_Hz},
- {36_Hz, 72_Hz},
- {30_Hz, 30_Hz}},
- .end{{1.028_Hz, 72_Hz},
- {1.0256_Hz, 120_Hz},
- {1.022_Hz, 90_Hz},
- {1.016_Hz, 60_Hz},
- {1.014_Hz, 72_Hz},
- {1.011_Hz, 90_Hz},
- {1.008_Hz, 120_Hz},
- {1_Hz, 30_Hz}}};
+ return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
}
}();
auto actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates216, actualRanking.size());
- } else {
- ASSERT_EQ(expectedRanking.begin.size(), actualRanking.size());
- }
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
- for (size_t i = 0; i < expectedRanking.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRanking.begin[i];
- EXPECT_EQ(expectedRenderRate, actualRanking[i].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate, actualRanking[i].frameRateMode.modePtr->getFps());
- }
-
- for (size_t i = 0; i < expectedRanking.end.size(); i++) {
- const size_t refreshRateIndex = actualRanking.size() - expectedRanking.end.size() + i;
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRanking.end[i];
- EXPECT_EQ(expectedRenderRate, actualRanking[refreshRateIndex].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate,
- actualRanking[refreshRateIndex].frameRateMode.modePtr->getFps());
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
lr1.vote = LayerVoteType::Max;
@@ -1711,55 +1597,31 @@
lr5.desiredRefreshRate = 120_Hz;
lr5.name = "120Hz";
- expectedRanking = []() -> FpsBeginEnd {
+ expectedRanking = []() -> std::vector<FrameRateMode> {
switch (GetParam()) {
case Config::FrameRateOverride::Disabled:
case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
case Config::FrameRateOverride::AppOverride:
- return {.begin{{120_Hz, 120_Hz},
- {90_Hz, 90_Hz},
- {72_Hz, 72_Hz},
- {60_Hz, 60_Hz},
- {30_Hz, 30_Hz}}};
+ return {{120_Hz, kMode120},
+ {90_Hz, kMode90},
+ {72_Hz, kMode72},
+ {60_Hz, kMode60},
+ {30_Hz, kMode30}};
case Config::FrameRateOverride::Enabled:
- return {.begin{{120_Hz, 120_Hz},
- {90_Hz, 90_Hz},
- {72_Hz, 72_Hz},
- {60_Hz, 60_Hz},
- {45_Hz, 90_Hz},
- {40_Hz, 120_Hz},
- {36_Hz, 72_Hz},
- {30_Hz, 30_Hz}},
- .end{{1.028_Hz, 72_Hz},
- {1.0256_Hz, 120_Hz},
- {1.022_Hz, 90_Hz},
- {1.016_Hz, 60_Hz},
- {1.014_Hz, 72_Hz},
- {1.011_Hz, 90_Hz},
- {1.008_Hz, 120_Hz},
- {1_Hz, 30_Hz}}};
+ return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
}
}();
actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates216, actualRanking.size());
- } else {
- ASSERT_EQ(expectedRanking.begin.size(), actualRanking.size());
- }
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
- for (size_t i = 0; i < expectedRanking.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRanking.begin[i];
- EXPECT_EQ(expectedRenderRate, actualRanking[i].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate, actualRanking[i].frameRateMode.modePtr->getFps());
- }
-
- for (size_t i = 0; i < expectedRanking.end.size(); i++) {
- const size_t refreshRateIndex = actualRanking.size() - expectedRanking.end.size() + i;
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRanking.end[i];
- EXPECT_EQ(expectedRenderRate, actualRanking[refreshRateIndex].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate,
- actualRanking[refreshRateIndex].frameRateMode.modePtr->getFps());
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
lr1.vote = LayerVoteType::Heuristic;
@@ -1775,55 +1637,31 @@
lr5.desiredRefreshRate = 72_Hz;
lr5.name = "72Hz";
- expectedRanking = []() -> FpsBeginEnd {
+ expectedRanking = []() -> std::vector<FrameRateMode> {
switch (GetParam()) {
case Config::FrameRateOverride::Disabled:
case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
case Config::FrameRateOverride::AppOverride:
- return {.begin{{30_Hz, 30_Hz},
- {60_Hz, 60_Hz},
- {90_Hz, 90_Hz},
- {120_Hz, 120_Hz},
- {72_Hz, 72_Hz}}};
+ return {{30_Hz, kMode30},
+ {60_Hz, kMode60},
+ {90_Hz, kMode90},
+ {120_Hz, kMode120},
+ {72_Hz, kMode72}};
case Config::FrameRateOverride::Enabled:
- return {.begin{{30_Hz, 30_Hz},
- {60_Hz, 60_Hz},
- {90_Hz, 90_Hz},
- {120_Hz, 120_Hz},
- {45_Hz, 90_Hz},
- {40_Hz, 120_Hz},
- {72_Hz, 72_Hz},
- {36_Hz, 72_Hz}},
- .end{{1.074_Hz, 72_Hz},
- {1.081_Hz, 120_Hz},
- {1.084_Hz, 90_Hz},
- {1.00_Hz, 30_Hz},
- {1.008_Hz, 120_Hz},
- {1.011_Hz, 90_Hz},
- {1.014_Hz, 72_Hz},
- {1.016_Hz, 60_Hz}}};
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}, {120_Hz, kMode120},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {72_Hz, kMode72}, {36_Hz, kMode72}};
}
}();
actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates216, actualRanking.size());
- } else {
- ASSERT_EQ(expectedRanking.begin.size(), actualRanking.size());
- }
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
- for (size_t i = 0; i < expectedRanking.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRanking.begin[i];
- EXPECT_EQ(expectedRenderRate, actualRanking[i].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate, actualRanking[i].frameRateMode.modePtr->getFps());
- }
-
- for (size_t i = 0; i < expectedRanking.end.size(); i++) {
- const size_t refreshRateIndex = actualRanking.size() - expectedRanking.end.size() + i;
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRanking.end[i];
- EXPECT_EQ(expectedRenderRate, actualRanking[refreshRateIndex].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate,
- actualRanking[refreshRateIndex].frameRateMode.modePtr->getFps());
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
lr1.desiredRefreshRate = 120_Hz;
@@ -1842,48 +1680,31 @@
lr5.desiredRefreshRate = 120_Hz;
lr5.name = "120Hz-2";
- expectedRanking = []() -> FpsBeginEnd {
+ expectedRanking = []() -> std::vector<FrameRateMode> {
switch (GetParam()) {
case Config::FrameRateOverride::Disabled:
case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
case Config::FrameRateOverride::AppOverride:
- return {.begin{{90_Hz, 90_Hz},
- {60_Hz, 60_Hz},
- {120_Hz, 120_Hz},
- {72_Hz, 72_Hz},
- {30_Hz, 30_Hz}}};
+ return {{90_Hz, kMode90},
+ {60_Hz, kMode60},
+ {120_Hz, kMode120},
+ {72_Hz, kMode72},
+ {30_Hz, kMode30}};
case Config::FrameRateOverride::Enabled:
- return {.begin{{90_Hz, 90_Hz},
- {60_Hz, 60_Hz},
- {120_Hz, 120_Hz},
- {72_Hz, 72_Hz},
- {45_Hz, 90_Hz},
- {40_Hz, 120_Hz},
- {36_Hz, 72_Hz},
- {30_Hz, 30_Hz}},
- .end{}};
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {120_Hz, kMode120}, {72_Hz, kMode72},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
}
}();
actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates216, actualRanking.size());
- } else {
- ASSERT_EQ(expectedRanking.begin.size(), actualRanking.size());
- }
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
- for (size_t i = 0; i < expectedRanking.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRanking.begin[i];
- EXPECT_EQ(expectedRenderRate, actualRanking[i].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate, actualRanking[i].frameRateMode.modePtr->getFps());
- }
-
- for (size_t i = 0; i < expectedRanking.end.size(); i++) {
- const size_t refreshRateIndex = actualRanking.size() - expectedRanking.end.size() + i;
- const auto [expectedRenderRate, expectedRefreshRate] = expectedRanking.end[i];
- EXPECT_EQ(expectedRenderRate, actualRanking[refreshRateIndex].frameRateMode.fps);
- EXPECT_EQ(expectedRefreshRate,
- actualRanking[refreshRateIndex].frameRateMode.modePtr->getFps());
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
@@ -2506,8 +2327,11 @@
}
// b/190578904
-TEST_P(RefreshRateSelectorTest,
- getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_Heuristic) {
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) {
+ if (g_noSlowTests) {
+ GTEST_SKIP();
+ }
+
const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
constexpr int kMaxRefreshRate = 240;
@@ -2532,89 +2356,8 @@
for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
testRefreshRate(refreshRate, LayerVoteType::Heuristic);
- }
-}
-TEST_P(RefreshRateSelectorTest,
- getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_ExplicitDefault) {
- const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
- constexpr int kMaxRefreshRate = 240;
-
- DisplayModes displayModes;
- for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
- const DisplayModeId modeId(fps);
- displayModes.try_emplace(modeId,
- createDisplayMode(modeId,
- Fps::fromValue(static_cast<float>(fps))));
- }
-
- const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate));
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
- layers[0].desiredRefreshRate = fps;
- layers[0].vote = vote;
- EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue())
- << "Failed for " << ftl::enum_string(vote);
- };
-
- for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
- const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
- }
-}
-TEST_P(RefreshRateSelectorTest,
- getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_ExplicitExactOrMultiple) {
- const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
- constexpr int kMaxRefreshRate = 240;
-
- DisplayModes displayModes;
- for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
- const DisplayModeId modeId(fps);
- displayModes.try_emplace(modeId,
- createDisplayMode(modeId,
- Fps::fromValue(static_cast<float>(fps))));
- }
-
- const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate));
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
- layers[0].desiredRefreshRate = fps;
- layers[0].vote = vote;
- EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue())
- << "Failed for " << ftl::enum_string(vote);
- };
-
- for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
- const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
- }
-}
-TEST_P(RefreshRateSelectorTest,
- getBestFrameRateMode_withCloseRefreshRates_LayerVoteType_ExplicitExact) {
- const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
- constexpr int kMaxRefreshRate = 240;
-
- DisplayModes displayModes;
- for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
- const DisplayModeId modeId(fps);
- displayModes.try_emplace(modeId,
- createDisplayMode(modeId,
- Fps::fromValue(static_cast<float>(fps))));
- }
-
- const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate));
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
- layers[0].desiredRefreshRate = fps;
- layers[0].vote = vote;
- EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue())
- << "Failed for " << ftl::enum_string(vote);
- };
-
- for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
- const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
}
}
@@ -3063,57 +2806,30 @@
auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
// [renderRate, refreshRate]
- const auto expected = []() -> FpsBeginEnd {
+ const auto expected = []() -> std::vector<std::pair<Fps, Fps>> {
switch (GetParam()) {
case Config::FrameRateOverride::Disabled:
case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
case Config::FrameRateOverride::AppOverride:
- return {.begin{{30_Hz, 30_Hz},
- {60_Hz, 60_Hz},
- {72_Hz, 72_Hz},
- {90_Hz, 90_Hz},
- {120_Hz, 120_Hz}}};
+ return {{30_Hz, 30_Hz},
+ {60_Hz, 60_Hz},
+ {72_Hz, 72_Hz},
+ {90_Hz, 90_Hz},
+ {120_Hz, 120_Hz}};
case Config::FrameRateOverride::Enabled:
- return {.begin{{1_Hz, 30_Hz},
- {1.008_Hz, 120_Hz},
- {1.011_Hz, 90_Hz},
- {1.014_Hz, 72_Hz},
- {1.016_Hz, 60_Hz},
- {1.022_Hz, 90_Hz},
- {1.0256_Hz, 120_Hz},
- {1.028_Hz, 72_Hz}},
- .end{
- {30_Hz, 30_Hz},
- {36_Hz, 72_Hz},
- {40_Hz, 120_Hz},
- {45_Hz, 90_Hz},
- {60_Hz, 60_Hz},
- {72_Hz, 72_Hz},
- {90_Hz, 90_Hz},
- {120_Hz, 120_Hz},
- }};
+ return {{30_Hz, 30_Hz}, {36_Hz, 72_Hz}, {40_Hz, 120_Hz}, {45_Hz, 90_Hz},
+ {60_Hz, 60_Hz}, {72_Hz, 72_Hz}, {90_Hz, 90_Hz}, {120_Hz, 120_Hz}};
}
}();
const auto& primaryRefreshRates = selector.getPrimaryFrameRates();
- if (GetParam() == Config::FrameRateOverride::Enabled) {
- ASSERT_EQ(kTotalRefreshRates216, primaryRefreshRates.size());
- } else {
- ASSERT_EQ(expected.begin.size(), primaryRefreshRates.size());
- }
+ ASSERT_EQ(expected.size(), primaryRefreshRates.size());
- for (size_t i = 0; i < expected.begin.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expected.begin[i];
+ for (size_t i = 0; i < expected.size(); i++) {
+ const auto [expectedRenderRate, expectedRefreshRate] = expected[i];
EXPECT_EQ(expectedRenderRate, primaryRefreshRates[i].fps);
EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[i].modePtr->getFps());
}
-
- for (size_t i = 0; i < expected.end.size(); i++) {
- const auto [expectedRenderRate, expectedRefreshRate] = expected.end[i];
- const size_t refreshRateIndex = primaryRefreshRates.size() - expected.end.size() + i;
- EXPECT_EQ(expectedRenderRate, primaryRefreshRates[refreshRateIndex].fps);
- EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[refreshRateIndex].modePtr->getFps());
- }
}
TEST_P(RefreshRateSelectorTest, refreshRateIsCappedWithRenderFrameRate) {
@@ -3250,5 +2966,12 @@
EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
}
+TEST_P(RefreshRateSelectorTest, SupportsLowPhysicalRefreshRates) {
+ auto selector = createSelector(kModes_1_5_10, kModeId10);
+
+ EXPECT_EQ(kMode10, selector.getMaxRefreshRateByPolicy());
+ EXPECT_EQ(kMode1, selector.getMinRefreshRateByPolicy());
+}
+
} // namespace
} // namespace android::scheduler