Merge "Remove window infos from InputFlinger dumpsys" into udc-dev
diff --git a/libs/ultrahdr/include/ultrahdr/ultrahdr.h b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
index e87a025..d6153e9 100644
--- a/libs/ultrahdr/include/ultrahdr/ultrahdr.h
+++ b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
@@ -39,10 +39,12 @@
// Target output formats for decoder
typedef enum {
+ ULTRAHDR_OUTPUT_UNSPECIFIED = -1,
ULTRAHDR_OUTPUT_SDR, // SDR in RGBA_8888 color format
ULTRAHDR_OUTPUT_HDR_LINEAR, // HDR in F16 color format (linear)
ULTRAHDR_OUTPUT_HDR_PQ, // HDR in RGBA_1010102 color format (PQ transfer function)
ULTRAHDR_OUTPUT_HDR_HLG, // HDR in RGBA_1010102 color format (HLG transfer function)
+ ULTRAHDR_OUTPUT_MAX = ULTRAHDR_OUTPUT_HDR_HLG,
} ultrahdr_output_format;
/*
diff --git a/libs/ultrahdr/jpegdecoderhelper.cpp b/libs/ultrahdr/jpegdecoderhelper.cpp
index 12217b7..fac90c5 100644
--- a/libs/ultrahdr/jpegdecoderhelper.cpp
+++ b/libs/ultrahdr/jpegdecoderhelper.cpp
@@ -26,6 +26,8 @@
namespace android::ultrahdr {
+#define ALIGNM(x, m) ((((x) + ((m) - 1)) / (m)) * (m))
+
const uint32_t kAPP0Marker = JPEG_APP0; // JFIF
const uint32_t kAPP1Marker = JPEG_APP0 + 1; // EXIF, XMP
const uint32_t kAPP2Marker = JPEG_APP0 + 2; // ICC
@@ -224,7 +226,14 @@
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
+ if (cinfo.comp_info[0].h_samp_factor != 2 ||
+ cinfo.comp_info[1].h_samp_factor != 1 ||
+ cinfo.comp_info[2].h_samp_factor != 1 ||
+ cinfo.comp_info[0].v_samp_factor != 2 ||
+ cinfo.comp_info[1].v_samp_factor != 1 ||
+ cinfo.comp_info[2].v_samp_factor != 1) {
+ return false;
+ }
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);
@@ -342,7 +351,6 @@
}
bool JpegDecoderHelper::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
-
JSAMPROW y[kCompressBatchSize];
JSAMPROW cb[kCompressBatchSize / 2];
JSAMPROW cr[kCompressBatchSize / 2];
@@ -356,6 +364,32 @@
std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
memset(empty.get(), 0, cinfo->image_width);
+ const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
+ bool is_width_aligned = (aligned_width == cinfo->image_width);
+ std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
+ uint8_t* y_plane_intrm = nullptr;
+ uint8_t* u_plane_intrm = nullptr;
+ uint8_t* v_plane_intrm = nullptr;
+ JSAMPROW y_intrm[kCompressBatchSize];
+ JSAMPROW cb_intrm[kCompressBatchSize / 2];
+ JSAMPROW cr_intrm[kCompressBatchSize / 2];
+ JSAMPARRAY planes_intrm[3] {y_intrm, cb_intrm, cr_intrm};
+ if (!is_width_aligned) {
+ size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
+ buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
+ y_plane_intrm = buffer_intrm.get();
+ u_plane_intrm = y_plane_intrm + (aligned_width * kCompressBatchSize);
+ v_plane_intrm = u_plane_intrm + (aligned_width * kCompressBatchSize) / 4;
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ y_intrm[i] = y_plane_intrm + i * aligned_width;
+ }
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ int offset_intrm = i * (aligned_width / 2);
+ cb_intrm[i] = u_plane_intrm + offset_intrm;
+ cr_intrm[i] = v_plane_intrm + offset_intrm;
+ }
+ }
+
while (cinfo->output_scanline < cinfo->image_height) {
for (int i = 0; i < kCompressBatchSize; ++i) {
size_t scanline = cinfo->output_scanline + i;
@@ -377,11 +411,21 @@
}
}
- int processed = jpeg_read_raw_data(cinfo, planes, kCompressBatchSize);
+ int processed = jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
+ kCompressBatchSize);
if (processed != kCompressBatchSize) {
ALOGE("Number of processed lines does not equal input lines.");
return false;
}
+ if (!is_width_aligned) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ memcpy(y[i], y_intrm[i], cinfo->image_width);
+ }
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ memcpy(cb[i], cb_intrm[i], cinfo->image_width / 2);
+ memcpy(cr[i], cr_intrm[i], cinfo->image_width / 2);
+ }
+ }
}
return true;
}
@@ -394,6 +438,21 @@
std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
memset(empty.get(), 0, cinfo->image_width);
+ int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
+ bool is_width_aligned = (aligned_width == cinfo->image_width);
+ std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
+ uint8_t* y_plane_intrm = nullptr;
+ JSAMPROW y_intrm[kCompressBatchSize];
+ JSAMPARRAY planes_intrm[1] {y_intrm};
+ if (!is_width_aligned) {
+ size_t mcu_row_size = aligned_width * kCompressBatchSize;
+ buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
+ y_plane_intrm = buffer_intrm.get();
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ y_intrm[i] = y_plane_intrm + i * aligned_width;
+ }
+ }
+
while (cinfo->output_scanline < cinfo->image_height) {
for (int i = 0; i < kCompressBatchSize; ++i) {
size_t scanline = cinfo->output_scanline + i;
@@ -404,11 +463,17 @@
}
}
- int processed = jpeg_read_raw_data(cinfo, planes, kCompressBatchSize);
+ int processed = jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
+ kCompressBatchSize);
if (processed != kCompressBatchSize / 2) {
ALOGE("Number of processed lines does not equal input lines.");
return false;
}
+ if (!is_width_aligned) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ memcpy(y[i], y_intrm[i], cinfo->image_width);
+ }
+ }
}
return true;
}
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index da25726..5ebca39 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -65,6 +65,13 @@
// Map is quarter res / sixteenth size
static const size_t kMapDimensionScaleFactor = 4;
+
+// Gain Map width is (image_width / kMapDimensionScaleFactor). If we were to
+// compress 420 GainMap in jpeg, then we need at least 2 samples. For Grayscale
+// 1 sample is sufficient. We are using 2 here anyways
+static const int kMinWidth = 2 * kMapDimensionScaleFactor;
+static const int kMinHeight = 2 * kMapDimensionScaleFactor;
+
// JPEG block size.
// JPEG encoding / decoding will require block based DCT transform 16 x 16 for luma,
// and 8 x 8 for chroma.
@@ -105,10 +112,10 @@
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- if (uncompressed_p010_image->width == 0
- || uncompressed_p010_image->height == 0) {
- ALOGE("Image dimensions cannot be zero, image dimensions %dx%d",
- uncompressed_p010_image->width, uncompressed_p010_image->height);
+ if (uncompressed_p010_image->width < kMinWidth
+ || uncompressed_p010_image->height < kMinHeight) {
+ ALOGE("Image dimensions cannot be less than %dx%d, image dimensions %dx%d",
+ kMinWidth, kMinHeight, uncompressed_p010_image->width, uncompressed_p010_image->height);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
@@ -469,12 +476,34 @@
ultrahdr_output_format output_format,
jr_uncompressed_ptr gain_map,
ultrahdr_metadata_ptr metadata) {
- if (compressed_jpegr_image == nullptr || dest == nullptr) {
+ if (compressed_jpegr_image == nullptr || compressed_jpegr_image->data == nullptr) {
+ ALOGE("received nullptr for compressed jpegr image");
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ if (dest == nullptr || dest->data == nullptr) {
+ ALOGE("received nullptr for dest image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
if (max_display_boost < 1.0f) {
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ ALOGE("received bad value for max_display_boost %f", max_display_boost);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (exif != nullptr && exif->data == nullptr) {
+ ALOGE("received nullptr address for exif data");
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (output_format <= ULTRAHDR_OUTPUT_UNSPECIFIED || output_format > ULTRAHDR_OUTPUT_MAX) {
+ ALOGE("received bad value for output format %d", output_format);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (gain_map != nullptr && gain_map->data == nullptr) {
+ ALOGE("received nullptr address for gain map data");
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
if (output_format == ULTRAHDR_OUTPUT_SDR) {
diff --git a/libs/ultrahdr/tests/jpegr_test.cpp b/libs/ultrahdr/tests/jpegr_test.cpp
index ac35887..d482ea1 100644
--- a/libs/ultrahdr/tests/jpegr_test.cpp
+++ b/libs/ultrahdr/tests/jpegr_test.cpp
@@ -89,6 +89,51 @@
return true;
}
+static bool loadP010Image(const char *filename, jr_uncompressed_ptr img,
+ bool isUVContiguous) {
+ int fd = open(filename, O_CLOEXEC);
+ if (fd < 0) {
+ return false;
+ }
+ const int bpp = 2;
+ int lumaStride = img->luma_stride == 0 ? img->width : img->luma_stride;
+ int lumaSize = bpp * lumaStride * img->height;
+ int chromaSize = bpp * (img->height / 2) *
+ (isUVContiguous ? lumaStride : img->chroma_stride);
+ img->data = malloc(lumaSize + (isUVContiguous ? chromaSize : 0));
+ if (img->data == nullptr) {
+ ALOGE("loadP010Image(): failed to allocate memory for luma data.");
+ return false;
+ }
+ uint8_t *mem = static_cast<uint8_t *>(img->data);
+ for (int i = 0; i < img->height; i++) {
+ if (read(fd, mem, img->width * bpp) != img->width * bpp) {
+ close(fd);
+ return false;
+ }
+ mem += lumaStride * bpp;
+ }
+ int chromaStride = lumaStride;
+ if (!isUVContiguous) {
+ img->chroma_data = malloc(chromaSize);
+ if (img->chroma_data == nullptr) {
+ ALOGE("loadP010Image(): failed to allocate memory for chroma data.");
+ return false;
+ }
+ mem = static_cast<uint8_t *>(img->chroma_data);
+ chromaStride = img->chroma_stride;
+ }
+ for (int i = 0; i < img->height / 2; i++) {
+ if (read(fd, mem, img->width * bpp) != img->width * bpp) {
+ close(fd);
+ return false;
+ }
+ mem += chromaStride * bpp;
+ }
+ close(fd);
+ return true;
+}
+
class JpegRTest : public testing::Test {
public:
JpegRTest();
@@ -98,10 +143,11 @@
virtual void SetUp();
virtual void TearDown();
- struct jpegr_uncompressed_struct mRawP010Image;
- struct jpegr_uncompressed_struct mRawP010ImageWithStride;
- struct jpegr_uncompressed_struct mRawYuv420Image;
- struct jpegr_compressed_struct mJpegImage;
+ struct jpegr_uncompressed_struct mRawP010Image{};
+ struct jpegr_uncompressed_struct mRawP010ImageWithStride{};
+ struct jpegr_uncompressed_struct mRawP010ImageWithChromaData{};
+ struct jpegr_uncompressed_struct mRawYuv420Image{};
+ struct jpegr_compressed_struct mJpegImage{};
};
JpegRTest::JpegRTest() {}
@@ -110,7 +156,11 @@
void JpegRTest::SetUp() {}
void JpegRTest::TearDown() {
free(mRawP010Image.data);
+ free(mRawP010Image.chroma_data);
free(mRawP010ImageWithStride.data);
+ free(mRawP010ImageWithStride.chroma_data);
+ free(mRawP010ImageWithChromaData.data);
+ free(mRawP010ImageWithChromaData.chroma_data);
free(mRawYuv420Image.data);
free(mJpegImage.data);
}
@@ -286,6 +336,8 @@
&mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad chroma stride";
+ mRawP010ImageWithStride.chroma_data = nullptr;
+
free(jpegR.data);
}
@@ -734,6 +786,7 @@
EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
&mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
&jpegR)) << "fail, API allows bad chroma stride";
+ mRawP010ImageWithStride.chroma_data = nullptr;
// bad compressed image
EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
@@ -769,6 +822,45 @@
free(jpegR.data);
}
+/* Test Decode API invalid arguments */
+TEST_F(JpegRTest, decodeAPIForInvalidArgs) {
+ int ret;
+
+ // we are not really compressing anything so lets keep allocs to a minimum
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = 16 * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+
+ // we are not really decoding anything so lets keep allocs to a minimum
+ mRawP010Image.data = malloc(16);
+
+ JpegR jpegRCodec;
+
+ // test jpegr image
+ EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
+ nullptr, &mRawP010Image)) << "fail, API allows nullptr for jpegr img";
+
+ // test dest image
+ EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
+ &jpegR, nullptr)) << "fail, API allows nullptr for dest";
+
+ // test max display boost
+ EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
+ &jpegR, &mRawP010Image, 0.5)) << "fail, API allows invalid max display boost";
+
+ // test output format
+ EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
+ &jpegR, &mRawP010Image, 0.5, nullptr,
+ static_cast<ultrahdr_output_format>(-1))) << "fail, API allows invalid output format";
+
+ EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
+ &jpegR, &mRawP010Image, 0.5, nullptr,
+ static_cast<ultrahdr_output_format>(ULTRAHDR_OUTPUT_MAX + 1)))
+ << "fail, API allows invalid output format";
+
+ free(jpegR.data);
+}
+
TEST_F(JpegRTest, writeXmpThenRead) {
ultrahdr_metadata_struct metadata_expected;
metadata_expected.version = "1.0";
@@ -792,6 +884,81 @@
EXPECT_FLOAT_EQ(metadata_expected.minContentBoost, metadata_read.minContentBoost);
}
+/* Test Encode API-0 */
+TEST_F(JpegRTest, encodeFromP010) {
+ int ret;
+
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ // Load input files.
+ if (!loadP010Image(RAW_P010_IMAGE, &mRawP010Image, true)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+
+ JpegR jpegRCodec;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = jpegRCodec.encodeJPEGR(
+ &mRawP010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY,
+ nullptr);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+
+ mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
+ mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
+ mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH + 128;
+ mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ // Load input files.
+ if (!loadP010Image(RAW_P010_IMAGE, &mRawP010ImageWithStride, true)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+
+ jpegr_compressed_struct jpegRWithStride;
+ jpegRWithStride.maxLength = jpegR.length;
+ jpegRWithStride.data = malloc(jpegRWithStride.maxLength);
+ ret = jpegRCodec.encodeJPEGR(
+ &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegRWithStride,
+ DEFAULT_JPEG_QUALITY, nullptr);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ ASSERT_EQ(jpegR.length, jpegRWithStride.length)
+ << "Same input is yielding different output";
+ ASSERT_EQ(0, memcmp(jpegR.data, jpegRWithStride.data, jpegR.length))
+ << "Same input is yielding different output";
+
+ mRawP010ImageWithChromaData.width = TEST_IMAGE_WIDTH;
+ mRawP010ImageWithChromaData.height = TEST_IMAGE_HEIGHT;
+ mRawP010ImageWithChromaData.luma_stride = TEST_IMAGE_WIDTH + 64;
+ mRawP010ImageWithChromaData.chroma_stride = TEST_IMAGE_WIDTH + 256;
+ mRawP010ImageWithChromaData.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ // Load input files.
+ if (!loadP010Image(RAW_P010_IMAGE, &mRawP010ImageWithChromaData, false)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ jpegr_compressed_struct jpegRWithChromaData;
+ jpegRWithChromaData.maxLength = jpegR.length;
+ jpegRWithChromaData.data = malloc(jpegRWithChromaData.maxLength);
+ ret = jpegRCodec.encodeJPEGR(
+ &mRawP010ImageWithChromaData, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ &jpegRWithChromaData, DEFAULT_JPEG_QUALITY, nullptr);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ ASSERT_EQ(jpegR.length, jpegRWithChromaData.length)
+ << "Same input is yielding different output";
+ ASSERT_EQ(0, memcmp(jpegR.data, jpegRWithChromaData.data, jpegR.length))
+ << "Same input is yielding different output";
+
+ free(jpegR.data);
+ free(jpegRWithStride.data);
+ free(jpegRWithChromaData.data);
+}
+
/* Test Encode API-0 and decode */
TEST_F(JpegRTest, encodeFromP010ThenDecode) {
int ret;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 0a64a1c..c6b93a5 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -151,21 +151,22 @@
return;
}
std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
- std::vector<std::unique_ptr<InputMapper>> mappers;
-
- mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+ mDevices.insert(
+ {eventHubId,
+ std::make_pair<std::unique_ptr<InputDeviceContext>,
+ std::vector<std::unique_ptr<InputMapper>>>(std::move(contextPtr), {})});
}
-void InputDevice::addEventHubDevice(int32_t eventHubId,
- const InputReaderConfiguration& readerConfig) {
- if (mDevices.find(eventHubId) != mDevices.end()) {
- return;
- }
- std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
- std::vector<std::unique_ptr<InputMapper>> mappers = createMappers(*contextPtr, readerConfig);
-
- // insert the context into the devices set
- mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+void InputDevice::populateMappers(int32_t eventHubId,
+ const InputReaderConfiguration& readerConfig) {
+ auto targetDevice = mDevices.find(eventHubId);
+ LOG_ALWAYS_FATAL_IF(targetDevice == mDevices.end(),
+ "InputDevice::populateMappers(): missing device with eventHubId %d ",
+ eventHubId);
+ // create and add mappers to device
+ InputDeviceContext& context = *targetDevice->second.first;
+ std::vector<std::unique_ptr<InputMapper>> mappers = createMappers(context, readerConfig);
+ targetDevice->second.second = std::move(mappers);
// Must change generation to flag this device as changed
bumpGeneration();
}
@@ -440,29 +441,29 @@
}
std::vector<std::unique_ptr<InputMapper>> InputDevice::createMappers(
- InputDeviceContext& contextPtr, const InputReaderConfiguration& readerConfig) {
- ftl::Flags<InputDeviceClass> classes = contextPtr.getDeviceClasses();
+ InputDeviceContext& context, const InputReaderConfiguration& readerConfig) {
+ ftl::Flags<InputDeviceClass> classes = context.getDeviceClasses();
std::vector<std::unique_ptr<InputMapper>> mappers;
// Switch-like devices.
if (classes.test(InputDeviceClass::SWITCH)) {
- mappers.push_back(createInputMapper<SwitchInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<SwitchInputMapper>(context, readerConfig));
}
// Scroll wheel-like devices.
if (classes.test(InputDeviceClass::ROTARY_ENCODER)) {
- mappers.push_back(createInputMapper<RotaryEncoderInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<RotaryEncoderInputMapper>(context, readerConfig));
}
// Vibrator-like devices.
if (classes.test(InputDeviceClass::VIBRATOR)) {
- mappers.push_back(createInputMapper<VibratorInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<VibratorInputMapper>(context, readerConfig));
}
// Battery-like devices or light-containing devices.
// PeripheralController will be created with associated EventHub device.
if (classes.test(InputDeviceClass::BATTERY) || classes.test(InputDeviceClass::LIGHT)) {
- mController = std::make_unique<PeripheralController>(contextPtr);
+ mController = std::make_unique<PeripheralController>(context);
}
// Keyboard-like devices.
@@ -482,13 +483,13 @@
}
if (keyboardSource != 0) {
- mappers.push_back(createInputMapper<KeyboardInputMapper>(contextPtr, readerConfig,
+ mappers.push_back(createInputMapper<KeyboardInputMapper>(context, readerConfig,
keyboardSource, keyboardType));
}
// Cursor-like devices.
if (classes.test(InputDeviceClass::CURSOR)) {
- mappers.push_back(createInputMapper<CursorInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<CursorInputMapper>(context, readerConfig));
}
// Touchscreens and touchpad devices.
@@ -496,31 +497,31 @@
sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true);
// TODO(b/272518665): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) touchpads, or
// at least load this setting from the IDC file.
- const InputDeviceIdentifier identifier = contextPtr.getDeviceIdentifier();
+ const InputDeviceIdentifier identifier = context.getDeviceIdentifier();
const bool isSonyDualShock4Touchpad = identifier.vendor == 0x054c &&
(identifier.product == 0x05c4 || identifier.product == 0x09cc);
if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
- mappers.push_back(createInputMapper<TouchpadInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<TouchpadInputMapper>(context, readerConfig));
} else if (classes.test(InputDeviceClass::TOUCH_MT)) {
- mappers.push_back(std::make_unique<MultiTouchInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<MultiTouchInputMapper>(context, readerConfig));
} else if (classes.test(InputDeviceClass::TOUCH)) {
- mappers.push_back(std::make_unique<SingleTouchInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<SingleTouchInputMapper>(context, readerConfig));
}
// Joystick-like devices.
if (classes.test(InputDeviceClass::JOYSTICK)) {
- mappers.push_back(createInputMapper<JoystickInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<JoystickInputMapper>(context, readerConfig));
}
// Motion sensor enabled devices.
if (classes.test(InputDeviceClass::SENSOR)) {
- mappers.push_back(createInputMapper<SensorInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<SensorInputMapper>(context, readerConfig));
}
// External stylus-like devices.
if (classes.test(InputDeviceClass::EXTERNAL_STYLUS)) {
- mappers.push_back(createInputMapper<ExternalStylusInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<ExternalStylusInputMapper>(context, readerConfig));
}
return mappers;
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index ea95f78..8a33dff 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -334,7 +334,9 @@
device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
identifier);
}
- device->addEventHubDevice(eventHubId, mConfig);
+ device->addEmptyEventHubDevice(eventHubId);
+ auto unused = device->configure(systemTime(SYSTEM_TIME_MONOTONIC), mConfig, /*changes=*/{});
+ device->populateMappers(eventHubId, mConfig);
return device;
}
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 0b8a608..1729d46 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -81,7 +81,7 @@
void dump(std::string& dump, const std::string& eventHubDevStr);
void addEmptyEventHubDevice(int32_t eventHubId);
- void addEventHubDevice(int32_t eventHubId, const InputReaderConfiguration& readerConfig);
+ void populateMappers(int32_t eventHubId, const InputReaderConfiguration& readerConfig);
void removeEventHubDevice(int32_t eventHubId);
[[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
const InputReaderConfiguration& readerConfig,
@@ -203,7 +203,7 @@
int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
std::vector<std::unique_ptr<InputMapper>> createMappers(
- InputDeviceContext& contextPtr, const InputReaderConfiguration& readerConfig);
+ InputDeviceContext& context, const InputReaderConfiguration& readerConfig);
PropertyMap mConfiguration;
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index f300ee1..1d788df 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -27,8 +27,6 @@
friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
Args... args);
- explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
~MultiTouchInputMapper() override;
@@ -41,6 +39,8 @@
bool hasStylus() const override;
private:
+ explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
// simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
// mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
// It is used to simulate stylus events for debugging and testing on a device that does not
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index dac53cf..7726bfb 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -27,8 +27,6 @@
friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
Args... args);
- explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
~SingleTouchInputMapper() override;
@@ -42,6 +40,8 @@
private:
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
+ explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
};
} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index bfb371f..c045e15 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2584,7 +2584,10 @@
mFakeEventHub->addDevice(TEST_EVENTHUB_ID, "Test EventHub device", InputDeviceClass::BATTERY);
InputDevice device(mReader->getContext(), /*id=*/1, /*generation=*/2, /*identifier=*/{});
- device.addEventHubDevice(TEST_EVENTHUB_ID, mFakePolicy->getReaderConfiguration());
+ device.addEmptyEventHubDevice(TEST_EVENTHUB_ID);
+ auto unused = device.configure(systemTime(SYSTEM_TIME_MONOTONIC),
+ mFakePolicy->getReaderConfiguration(), /*changes=*/{});
+ device.populateMappers(TEST_EVENTHUB_ID, mFakePolicy->getReaderConfiguration());
device.removeEventHubDevice(TEST_EVENTHUB_ID);
std::string dumpStr, eventHubDevStr;
device.dump(dumpStr, eventHubDevStr);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 38590e6..f7596e2 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -877,6 +877,7 @@
// TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through
// CompositionEngine to create a single path for composing layers.
void updateSnapshot(bool updateGeometry);
+ void updateChildrenSnapshots(bool updateGeometry);
void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
std::unordered_set<Layer*>& visited);
@@ -1134,8 +1135,6 @@
bool hasSomethingToDraw() const { return hasEffect() || hasBufferOrSidebandStream(); }
- void updateChildrenSnapshots(bool updateGeometry);
-
// Fills the provided vector with the currently available JankData and removes the processed
// JankData from the pending list.
void transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles,
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index d606cff..51d4ff8 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -116,6 +116,8 @@
mLayer->setChildrenDrawingParent(mLayer);
}
}
+ mLayer->updateSnapshot(/*updateGeometry=*/true);
+ mLayer->updateChildrenSnapshots(/*updateGeometry=*/true);
}
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b1d4b3c..2ac1db9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2547,7 +2547,7 @@
}
updateCursorAsync();
- updateInputFlinger(vsyncId);
+ updateInputFlinger(vsyncId, frameTime);
if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and tracing should only be enabled for debugging.
@@ -3740,7 +3740,7 @@
doCommitTransactions();
}
-void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId) {
+void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) {
if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) {
return;
}
@@ -3752,8 +3752,6 @@
if (mUpdateInputInfo) {
mUpdateInputInfo = false;
updateWindowInfo = true;
- mLastInputFlingerUpdateVsyncId = vsyncId;
- mLastInputFlingerUpdateTimestamp = systemTime();
buildWindowInfos(windowInfos, displayInfos);
}
@@ -3775,17 +3773,17 @@
inputWindowCommands =
std::move(mInputWindowCommands),
inputFlinger = mInputFlinger, this,
- visibleWindowsChanged]() {
+ visibleWindowsChanged, vsyncId, frameTime]() {
ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
if (updateWindowInfo) {
mWindowInfosListenerInvoker
- ->windowInfosChanged(std::move(windowInfos), std::move(displayInfos),
+ ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
+ std::move(displayInfos),
+ vsyncId.value, frameTime.ns()},
std::move(
inputWindowCommands.windowInfosReportedListeners),
/* forceImmediateCall= */ visibleWindowsChanged ||
- !inputWindowCommands.focusRequests.empty(),
- mLastInputFlingerUpdateVsyncId,
- mLastInputFlingerUpdateTimestamp);
+ !inputWindowCommands.focusRequests.empty());
} else {
// If there are listeners but no changes to input windows, call the listeners
// immediately.
@@ -6152,27 +6150,14 @@
result.append("\n");
result.append("Window Infos:\n");
- StringAppendF(&result, " input flinger update vsync id: %" PRId64 "\n",
- mLastInputFlingerUpdateVsyncId.value);
- StringAppendF(&result, " input flinger update timestamp (ns): %" PRId64 "\n",
- mLastInputFlingerUpdateTimestamp);
+ auto windowInfosDebug = mWindowInfosListenerInvoker->getDebugInfo();
+ StringAppendF(&result, " max send vsync id: %" PRId64 "\n",
+ windowInfosDebug.maxSendDelayVsyncId.value);
+ StringAppendF(&result, " max send delay (ns): %" PRId64 " ns\n",
+ windowInfosDebug.maxSendDelayDuration);
+ StringAppendF(&result, " unsent messages: %" PRIu32 "\n",
+ windowInfosDebug.pendingMessageCount);
result.append("\n");
-
- if (int64_t unsentVsyncId = mWindowInfosListenerInvoker->getUnsentMessageVsyncId().value;
- unsentVsyncId != -1) {
- StringAppendF(&result, " unsent input flinger update vsync id: %" PRId64 "\n",
- unsentVsyncId);
- StringAppendF(&result, " unsent input flinger update timestamp (ns): %" PRId64 "\n",
- mWindowInfosListenerInvoker->getUnsentMessageTimestamp());
- result.append("\n");
- }
-
- if (uint32_t pendingMessages = mWindowInfosListenerInvoker->getPendingMessageCount();
- pendingMessages != 0) {
- StringAppendF(&result, " pending input flinger calls: %" PRIu32 "\n",
- mWindowInfosListenerInvoker->getPendingMessageCount());
- result.append("\n");
- }
}
mat4 SurfaceFlinger::calculateColorMatrix(float saturation) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e2691ab..0bc506f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -722,7 +722,7 @@
void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
- void updateInputFlinger(VsyncId);
+ void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime);
void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
std::vector<gui::DisplayInfo>& outDisplayInfos);
@@ -1259,9 +1259,6 @@
VsyncId mLastCommittedVsyncId;
- VsyncId mLastInputFlingerUpdateVsyncId;
- nsecs_t mLastInputFlingerUpdateTimestamp;
-
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
std::atomic<uint32_t> mFrameMissedCount = 0;
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 2b62638..20699ef 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -16,8 +16,11 @@
#include <ftl/small_vector.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/TraceUtils.h>
#include <gui/WindowInfosUpdate.h>
+#include <scheduler/Time.h>
+#include "BackgroundExecutor.h"
#include "WindowInfosListenerInvoker.h"
namespace android {
@@ -26,7 +29,7 @@
using gui::IWindowInfosListener;
using gui::WindowInfo;
-using WindowInfosListenerVector = ftl::SmallVector<const sp<IWindowInfosListener>, 3>;
+using WindowInfosListenerVector = ftl::SmallVector<const sp<gui::IWindowInfosListener>, 3>;
struct WindowInfosReportedListenerInvoker : gui::BnWindowInfosReportedListener,
IBinder::DeathRecipient {
@@ -86,45 +89,19 @@
}
void WindowInfosListenerInvoker::windowInfosChanged(
- std::vector<WindowInfo> windowInfos, std::vector<DisplayInfo> displayInfos,
- WindowInfosReportedListenerSet reportedListeners, bool forceImmediateCall, VsyncId vsyncId,
- nsecs_t timestamp) {
- reportedListeners.insert(sp<WindowInfosListenerInvoker>::fromExisting(this));
- auto callListeners = [this, windowInfos = std::move(windowInfos),
- displayInfos = std::move(displayInfos), vsyncId,
- timestamp](WindowInfosReportedListenerSet reportedListeners) mutable {
- WindowInfosListenerVector windowInfosListeners;
- {
- std::scoped_lock lock(mListenersMutex);
- for (const auto& [_, listener] : mWindowInfosListeners) {
- windowInfosListeners.push_back(listener);
- }
- }
-
- auto reportedInvoker =
- sp<WindowInfosReportedListenerInvoker>::make(windowInfosListeners,
- std::move(reportedListeners));
-
- gui::WindowInfosUpdate update(std::move(windowInfos), std::move(displayInfos),
- vsyncId.value, timestamp);
-
- for (const auto& listener : windowInfosListeners) {
- sp<IBinder> asBinder = IInterface::asBinder(listener);
-
- // linkToDeath is used here to ensure that the windowInfosReportedListeners
- // are called even if one of the windowInfosListeners dies before
- // calling onWindowInfosReported.
- asBinder->linkToDeath(reportedInvoker);
-
- auto status = listener->onWindowInfosChanged(update, reportedInvoker);
- if (!status.isOk()) {
- reportedInvoker->onWindowInfosReported();
- }
- }
- };
-
+ gui::WindowInfosUpdate update, WindowInfosReportedListenerSet reportedListeners,
+ bool forceImmediateCall) {
+ WindowInfosListenerVector listeners;
{
- std::scoped_lock lock(mMessagesMutex);
+ std::scoped_lock lock{mMessagesMutex};
+
+ if (!mDelayInfo) {
+ mDelayInfo = DelayInfo{
+ .vsyncId = update.vsyncId,
+ .frameTime = update.timestamp,
+ };
+ }
+
// If there are unacked messages and this isn't a forced call, then return immediately.
// If a forced window infos change doesn't happen first, the update will be sent after
// the WindowInfosReportedListeners are called. If a forced window infos change happens or
@@ -132,44 +109,87 @@
// will be dropped and the listeners will only be called with the latest info. This is done
// to reduce the amount of binder memory used.
if (mActiveMessageCount > 0 && !forceImmediateCall) {
- mWindowInfosChangedDelayed = std::move(callListeners);
- mUnsentVsyncId = vsyncId;
- mUnsentTimestamp = timestamp;
- mReportedListenersDelayed.merge(reportedListeners);
+ mDelayedUpdate = std::move(update);
+ mReportedListeners.merge(reportedListeners);
return;
}
- mWindowInfosChangedDelayed = nullptr;
- mUnsentVsyncId = {-1};
- mUnsentTimestamp = -1;
- reportedListeners.merge(mReportedListenersDelayed);
+ if (mDelayedUpdate) {
+ mDelayedUpdate.reset();
+ }
+
+ {
+ std::scoped_lock lock{mListenersMutex};
+ for (const auto& [_, listener] : mWindowInfosListeners) {
+ listeners.push_back(listener);
+ }
+ }
+ if (CC_UNLIKELY(listeners.empty())) {
+ mReportedListeners.merge(reportedListeners);
+ mDelayInfo.reset();
+ return;
+ }
+
+ reportedListeners.insert(sp<WindowInfosListenerInvoker>::fromExisting(this));
+ reportedListeners.merge(mReportedListeners);
+ mReportedListeners.clear();
+
mActiveMessageCount++;
+ updateMaxSendDelay();
+ mDelayInfo.reset();
}
- callListeners(std::move(reportedListeners));
+
+ auto reportedInvoker =
+ sp<WindowInfosReportedListenerInvoker>::make(listeners, std::move(reportedListeners));
+
+ for (const auto& listener : listeners) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+
+ // linkToDeath is used here to ensure that the windowInfosReportedListeners
+ // are called even if one of the windowInfosListeners dies before
+ // calling onWindowInfosReported.
+ asBinder->linkToDeath(reportedInvoker);
+
+ auto status = listener->onWindowInfosChanged(update, reportedInvoker);
+ if (!status.isOk()) {
+ reportedInvoker->onWindowInfosReported();
+ }
+ }
}
binder::Status WindowInfosListenerInvoker::onWindowInfosReported() {
- std::function<void(WindowInfosReportedListenerSet)> callListeners;
- WindowInfosReportedListenerSet reportedListeners;
-
- {
- std::scoped_lock lock{mMessagesMutex};
- mActiveMessageCount--;
- if (!mWindowInfosChangedDelayed || mActiveMessageCount > 0) {
- return binder::Status::ok();
+ BackgroundExecutor::getInstance().sendCallbacks({[this]() {
+ gui::WindowInfosUpdate update;
+ {
+ std::scoped_lock lock{mMessagesMutex};
+ mActiveMessageCount--;
+ if (!mDelayedUpdate || mActiveMessageCount > 0) {
+ return;
+ }
+ update = std::move(*mDelayedUpdate);
+ mDelayedUpdate.reset();
}
-
- mActiveMessageCount++;
- callListeners = std::move(mWindowInfosChangedDelayed);
- mWindowInfosChangedDelayed = nullptr;
- mUnsentVsyncId = {-1};
- mUnsentTimestamp = -1;
- reportedListeners = std::move(mReportedListenersDelayed);
- mReportedListenersDelayed.clear();
- }
-
- callListeners(std::move(reportedListeners));
+ windowInfosChanged(std::move(update), {}, false);
+ }});
return binder::Status::ok();
}
+WindowInfosListenerInvoker::DebugInfo WindowInfosListenerInvoker::getDebugInfo() {
+ std::scoped_lock lock{mMessagesMutex};
+ updateMaxSendDelay();
+ mDebugInfo.pendingMessageCount = mActiveMessageCount;
+ return mDebugInfo;
+}
+
+void WindowInfosListenerInvoker::updateMaxSendDelay() {
+ if (!mDelayInfo) {
+ return;
+ }
+ nsecs_t delay = TimePoint::now().ns() - mDelayInfo->frameTime;
+ if (delay > mDebugInfo.maxSendDelayDuration) {
+ mDebugInfo.maxSendDelayDuration = delay;
+ mDebugInfo.maxSendDelayVsyncId = VsyncId{mDelayInfo->vsyncId};
+ }
+}
+
} // namespace android
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index e35d056..bc465a3 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -16,6 +16,7 @@
#pragma once
+#include <optional>
#include <unordered_set>
#include <android/gui/BnWindowInfosReportedListener.h>
@@ -40,26 +41,18 @@
void addWindowInfosListener(sp<gui::IWindowInfosListener>);
void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
- void windowInfosChanged(std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>,
+ void windowInfosChanged(gui::WindowInfosUpdate update,
WindowInfosReportedListenerSet windowInfosReportedListeners,
- bool forceImmediateCall, VsyncId vsyncId, nsecs_t timestamp);
+ bool forceImmediateCall);
binder::Status onWindowInfosReported() override;
- VsyncId getUnsentMessageVsyncId() {
- std::scoped_lock lock(mMessagesMutex);
- return mUnsentVsyncId;
- }
-
- nsecs_t getUnsentMessageTimestamp() {
- std::scoped_lock lock(mMessagesMutex);
- return mUnsentTimestamp;
- }
-
- uint32_t getPendingMessageCount() {
- std::scoped_lock lock(mMessagesMutex);
- return mActiveMessageCount;
- }
+ struct DebugInfo {
+ VsyncId maxSendDelayVsyncId;
+ nsecs_t maxSendDelayDuration;
+ uint32_t pendingMessageCount;
+ };
+ DebugInfo getDebugInfo();
protected:
void binderDied(const wp<IBinder>& who) override;
@@ -73,11 +66,16 @@
std::mutex mMessagesMutex;
uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0;
- std::function<void(WindowInfosReportedListenerSet)> mWindowInfosChangedDelayed
- GUARDED_BY(mMessagesMutex);
- VsyncId mUnsentVsyncId GUARDED_BY(mMessagesMutex) = {-1};
- nsecs_t mUnsentTimestamp GUARDED_BY(mMessagesMutex) = -1;
- WindowInfosReportedListenerSet mReportedListenersDelayed;
+ std::optional<gui::WindowInfosUpdate> mDelayedUpdate GUARDED_BY(mMessagesMutex);
+ WindowInfosReportedListenerSet mReportedListeners;
+
+ DebugInfo mDebugInfo GUARDED_BY(mMessagesMutex);
+ struct DelayInfo {
+ int64_t vsyncId;
+ nsecs_t frameTime;
+ };
+ std::optional<DelayInfo> mDelayInfo GUARDED_BY(mMessagesMutex);
+ void updateMaxSendDelay() REQUIRES(mMessagesMutex);
};
} // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index da5ec48..4d03be0 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -590,7 +590,7 @@
mFlinger->binderDied(display);
mFlinger->onFirstRef();
- mFlinger->updateInputFlinger(VsyncId{0});
+ mFlinger->updateInputFlinger(VsyncId{}, TimePoint{});
mFlinger->updateCursorAsync();
mutableScheduler().setVsyncConfig({.sfOffset = mFdp.ConsumeIntegral<nsecs_t>(),
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 881b362..db81bad 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -139,6 +139,7 @@
"VSyncReactorTest.cpp",
"VsyncConfigurationTest.cpp",
"VsyncScheduleTest.cpp",
+ "WindowInfosListenerInvokerTest.cpp",
],
}
diff --git a/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
new file mode 100644
index 0000000..af4971b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
@@ -0,0 +1,244 @@
+#include <android/gui/BnWindowInfosListener.h>
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/WindowInfosUpdate.h>
+#include <condition_variable>
+
+#include "BackgroundExecutor.h"
+#include "WindowInfosListenerInvoker.h"
+#include "android/gui/IWindowInfosReportedListener.h"
+
+namespace android {
+
+class WindowInfosListenerInvokerTest : public testing::Test {
+protected:
+ WindowInfosListenerInvokerTest() : mInvoker(sp<WindowInfosListenerInvoker>::make()) {}
+
+ ~WindowInfosListenerInvokerTest() {
+ std::mutex mutex;
+ std::condition_variable cv;
+ bool flushComplete = false;
+ // Flush the BackgroundExecutor thread to ensure any scheduled tasks are complete.
+ // Otherwise, references those tasks hold may go out of scope before they are done
+ // executing.
+ BackgroundExecutor::getInstance().sendCallbacks({[&]() {
+ std::scoped_lock lock{mutex};
+ flushComplete = true;
+ cv.notify_one();
+ }});
+ std::unique_lock<std::mutex> lock{mutex};
+ cv.wait(lock, [&]() { return flushComplete; });
+ }
+
+ sp<WindowInfosListenerInvoker> mInvoker;
+};
+
+using WindowInfosUpdateConsumer = std::function<void(const gui::WindowInfosUpdate&,
+ const sp<gui::IWindowInfosReportedListener>&)>;
+
+class Listener : public gui::BnWindowInfosListener {
+public:
+ Listener(WindowInfosUpdateConsumer consumer) : mConsumer(std::move(consumer)) {}
+
+ binder::Status onWindowInfosChanged(
+ const gui::WindowInfosUpdate& update,
+ const sp<gui::IWindowInfosReportedListener>& reportedListener) override {
+ mConsumer(update, reportedListener);
+ return binder::Status::ok();
+ }
+
+private:
+ WindowInfosUpdateConsumer mConsumer;
+};
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged calls a single window infos listener.
+TEST_F(WindowInfosListenerInvokerTest, callsSingleListener) {
+ std::mutex mutex;
+ std::condition_variable cv;
+
+ int callCount = 0;
+
+ mInvoker->addWindowInfosListener(
+ sp<Listener>::make([&](const gui::WindowInfosUpdate&,
+ const sp<gui::IWindowInfosReportedListener>& reportedListener) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
+
+ reportedListener->onWindowInfosReported();
+ }));
+
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[this]() { mInvoker->windowInfosChanged({}, {}, false); }});
+
+ std::unique_lock<std::mutex> lock{mutex};
+ cv.wait(lock, [&]() { return callCount == 1; });
+ EXPECT_EQ(callCount, 1);
+}
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged calls multiple window infos listeners.
+TEST_F(WindowInfosListenerInvokerTest, callsMultipleListeners) {
+ std::mutex mutex;
+ std::condition_variable cv;
+
+ int callCount = 0;
+ const int expectedCallCount = 3;
+
+ for (int i = 0; i < expectedCallCount; i++) {
+ mInvoker->addWindowInfosListener(sp<Listener>::make(
+ [&](const gui::WindowInfosUpdate&,
+ const sp<gui::IWindowInfosReportedListener>& reportedListener) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ if (callCount == expectedCallCount) {
+ cv.notify_one();
+ }
+
+ reportedListener->onWindowInfosReported();
+ }));
+ }
+
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[&]() { mInvoker->windowInfosChanged({}, {}, false); }});
+
+ std::unique_lock<std::mutex> lock{mutex};
+ cv.wait(lock, [&]() { return callCount == expectedCallCount; });
+ EXPECT_EQ(callCount, expectedCallCount);
+}
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged delays sending a second message until
+// after the WindowInfosReportedListener is called.
+TEST_F(WindowInfosListenerInvokerTest, delaysUnackedCall) {
+ std::mutex mutex;
+ std::condition_variable cv;
+
+ int callCount = 0;
+
+ // Simulate a slow ack by not calling the WindowInfosReportedListener.
+ mInvoker->addWindowInfosListener(sp<Listener>::make(
+ [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
+ }));
+
+ BackgroundExecutor::getInstance().sendCallbacks({[&]() {
+ mInvoker->windowInfosChanged({}, {}, false);
+ mInvoker->windowInfosChanged({}, {}, false);
+ }});
+
+ {
+ std::unique_lock lock{mutex};
+ cv.wait(lock, [&]() { return callCount == 1; });
+ }
+ EXPECT_EQ(callCount, 1);
+
+ // Ack the first message.
+ mInvoker->onWindowInfosReported();
+
+ {
+ std::unique_lock lock{mutex};
+ cv.wait(lock, [&]() { return callCount == 2; });
+ }
+ EXPECT_EQ(callCount, 2);
+}
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged immediately sends a second message when
+// forceImmediateCall is true.
+TEST_F(WindowInfosListenerInvokerTest, sendsForcedMessage) {
+ std::mutex mutex;
+ std::condition_variable cv;
+
+ int callCount = 0;
+ const int expectedCallCount = 2;
+
+ // Simulate a slow ack by not calling the WindowInfosReportedListener.
+ mInvoker->addWindowInfosListener(sp<Listener>::make(
+ [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ if (callCount == expectedCallCount) {
+ cv.notify_one();
+ }
+ }));
+
+ BackgroundExecutor::getInstance().sendCallbacks({[&]() {
+ mInvoker->windowInfosChanged({}, {}, false);
+ mInvoker->windowInfosChanged({}, {}, true);
+ }});
+
+ {
+ std::unique_lock lock{mutex};
+ cv.wait(lock, [&]() { return callCount == expectedCallCount; });
+ }
+ EXPECT_EQ(callCount, expectedCallCount);
+}
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged skips old messages when more than one
+// message is delayed.
+TEST_F(WindowInfosListenerInvokerTest, skipsDelayedMessage) {
+ std::mutex mutex;
+ std::condition_variable cv;
+
+ int64_t lastUpdateId = -1;
+
+ // Simulate a slow ack by not calling the WindowInfosReportedListener.
+ mInvoker->addWindowInfosListener(
+ sp<Listener>::make([&](const gui::WindowInfosUpdate& update,
+ const sp<gui::IWindowInfosReportedListener>&) {
+ std::scoped_lock lock{mutex};
+ lastUpdateId = update.vsyncId;
+ cv.notify_one();
+ }));
+
+ BackgroundExecutor::getInstance().sendCallbacks({[&]() {
+ mInvoker->windowInfosChanged({{}, {}, /* vsyncId= */ 1, 0}, {}, false);
+ mInvoker->windowInfosChanged({{}, {}, /* vsyncId= */ 2, 0}, {}, false);
+ mInvoker->windowInfosChanged({{}, {}, /* vsyncId= */ 3, 0}, {}, false);
+ }});
+
+ {
+ std::unique_lock lock{mutex};
+ cv.wait(lock, [&]() { return lastUpdateId == 1; });
+ }
+ EXPECT_EQ(lastUpdateId, 1);
+
+ // Ack the first message. The third update should be sent.
+ mInvoker->onWindowInfosReported();
+
+ {
+ std::unique_lock lock{mutex};
+ cv.wait(lock, [&]() { return lastUpdateId == 3; });
+ }
+ EXPECT_EQ(lastUpdateId, 3);
+}
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged immediately calls listener after a call
+// where no listeners were configured.
+TEST_F(WindowInfosListenerInvokerTest, noListeners) {
+ std::mutex mutex;
+ std::condition_variable cv;
+
+ int callCount = 0;
+
+ // Test that calling windowInfosChanged without any listeners doesn't cause the next call to be
+ // delayed.
+ BackgroundExecutor::getInstance().sendCallbacks({[&]() {
+ mInvoker->windowInfosChanged({}, {}, false);
+ mInvoker->addWindowInfosListener(sp<Listener>::make(
+ [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
+ }));
+ mInvoker->windowInfosChanged({}, {}, false);
+ }});
+
+ {
+ std::unique_lock lock{mutex};
+ cv.wait(lock, [&]() { return callCount == 1; });
+ }
+ EXPECT_EQ(callCount, 1);
+}
+
+} // namespace android