Merge "Expand fatal log for HOVER_MOVE" into udc-dev
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 92b1677..f1d8c72 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -540,6 +540,7 @@
# Run atrace with the categories written in a file
service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categories
+ user root
disabled
oneshot
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 043a7f1..8ca927e 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2206,6 +2206,16 @@
continue;
}
+ // Skip cached processes.
+ if (IsCached(pid)) {
+ // For consistency, the header and footer to this message match those
+ // dumped by debuggerd in the success case.
+ dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid);
+ dprintf(fd, "Dump skipped for cached process.\n");
+ dprintf(fd, "---- end %d ----", pid);
+ continue;
+ }
+
const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
std::string exe;
if (!android::base::Readlink(link_name, &exe)) {
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index a80da4e..d0030dd 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -8,6 +8,7 @@
socket dumpstate stream 0660 shell log
disabled
oneshot
+ user root
# 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
+ user root
# bugreportd starts dumpstate binder service and makes it wait for a listener to connect.
service bugreportd /system/bin/dumpstate -w
class main
disabled
oneshot
+ user root
diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc
index 5b08c77..525f0c8 100644
--- a/cmds/installd/installd.rc
+++ b/cmds/installd/installd.rc
@@ -1,6 +1,7 @@
service installd /system/bin/installd
class main
+ user root
capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL SETGID SETUID SYS_ADMIN
on early-boot
diff --git a/include/input/RingBuffer.h b/include/input/RingBuffer.h
index 67984b7..37fe5af 100644
--- a/include/input/RingBuffer.h
+++ b/include/input/RingBuffer.h
@@ -103,6 +103,11 @@
iterator end() { return {*this, mSize}; }
const_iterator end() const { return {*this, mSize}; }
+ reference front() { return mBuffer[mBegin]; }
+ const_reference front() const { return mBuffer[mBegin]; }
+ reference back() { return mBuffer[bufferIndex(mSize - 1)]; }
+ const_reference back() const { return mBuffer[bufferIndex(mSize - 1)]; }
+
reference operator[](size_type i) { return mBuffer[bufferIndex(i)]; }
const_reference operator[](size_type i) const { return mBuffer[bufferIndex(i)]; }
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 851b407..9282856 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -553,7 +553,7 @@
socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
if (!socket_fd.ok()) {
int savedErrno = errno;
- ALOGE("Could not create socket: %s", strerror(savedErrno));
+ ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
return -savedErrno;
}
if (0 != TEMP_FAILURE_RETRY(bind(socket_fd.get(), addr.addr(), addr.addrSize()))) {
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 067ce17..97cb810 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -16,6 +16,7 @@
#include <set>
#include <android-base/file.h>
+#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -210,3 +211,18 @@
return cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" ||
cmdline == "usap64" || cmdline == "webview_zygote";
}
+
+bool IsCached(int pid) {
+ std::string oom_score_adj;
+ if (!android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/oom_score_adj",
+ pid),
+ &oom_score_adj)) {
+ return false;
+ }
+ int32_t oom_score_adj_value;
+ if (!android::base::ParseInt(android::base::Trim(oom_score_adj), &oom_score_adj_value)) {
+ return false;
+ }
+ // An OOM score greater than 900 indicates a cached process.
+ return oom_score_adj_value >= 900;
+}
diff --git a/libs/dumputils/include/dumputils/dump_utils.h b/libs/dumputils/include/dumputils/dump_utils.h
index 7c5329d..f973d9f 100644
--- a/libs/dumputils/include/dumputils/dump_utils.h
+++ b/libs/dumputils/include/dumputils/dump_utils.h
@@ -25,4 +25,6 @@
bool IsZygote(int pid);
+bool IsCached(int pid);
+
#endif // DUMPUTILS_H_
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index b18bf5b..a5cf8d6 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -793,11 +793,15 @@
return result;
}
- std::vector<CancelBufferInput> cancelBufferInputs(numBufferRequested);
+ std::vector<CancelBufferInput> cancelBufferInputs;
+ cancelBufferInputs.reserve(numBufferRequested);
std::vector<status_t> cancelBufferOutputs;
for (size_t i = 0; i < numBufferRequested; i++) {
- cancelBufferInputs[i].slot = dequeueOutput[i].slot;
- cancelBufferInputs[i].fence = dequeueOutput[i].fence;
+ if (dequeueOutput[i].result >= 0) {
+ CancelBufferInput& input = cancelBufferInputs.emplace_back();
+ input.slot = dequeueOutput[i].slot;
+ input.fence = dequeueOutput[i].fence;
+ }
}
for (const auto& output : dequeueOutput) {
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 9e8c65c..9a9bd4a 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -628,7 +628,7 @@
// Expect no crash for overflow.
injectTap(12, 24);
- fgSurface->expectTap(6, 12);
+ bgSurface->expectTap(12, 24);
}
// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
@@ -1235,32 +1235,6 @@
surface->expectKey(AKEYCODE_V);
}
-/**
- * When multiple DisplayDevices are mapped to the same layerStack, use the configuration for the
- * display that can receive input.
- */
-TEST_F(MultiDisplayTests, many_to_one_display_mapping) {
- ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
- createDisplay(1000, 1000, false /*isSecure*/, layerStack, false /*receivesInput*/,
- 100 /*offsetX*/, 100 /*offsetY*/);
- createDisplay(1000, 1000, false /*isSecure*/, layerStack, true /*receivesInput*/,
- 200 /*offsetX*/, 200 /*offsetY*/);
- createDisplay(1000, 1000, false /*isSecure*/, layerStack, false /*receivesInput*/,
- 300 /*offsetX*/, 300 /*offsetY*/);
- std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
- surface->showAt(10, 10);
-
- // Input injection happens in logical display coordinates.
- injectTapOnDisplay(11, 11, layerStack.id);
- // Expect that the display transform for the display that receives input was used.
- surface->expectTapInDisplayCoordinates(211, 211);
-
- surface->requestFocus(layerStack.id);
- surface->assertFocusChange(true);
- injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
-}
-
TEST_F(MultiDisplayTests, drop_input_for_secure_layer_on_nonsecure_display) {
ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
createDisplay(1000, 1000, false /*isSecure*/, layerStack);
diff --git a/libs/input/tests/RingBuffer_test.cpp b/libs/input/tests/RingBuffer_test.cpp
index 8a6ef4c..a2ef658 100644
--- a/libs/input/tests/RingBuffer_test.cpp
+++ b/libs/input/tests/RingBuffer_test.cpp
@@ -118,6 +118,21 @@
EXPECT_EQ(0u, d.capacity());
}
+TEST(RingBufferTest, FrontBackAccess) {
+ RingBuffer<int> buffer(/*capacity=*/2);
+ buffer.pushBack(1);
+ EXPECT_EQ(1, buffer.front());
+ EXPECT_EQ(1, buffer.back());
+
+ buffer.pushFront(0);
+ EXPECT_EQ(0, buffer.front());
+ EXPECT_EQ(1, buffer.back());
+
+ buffer.pushFront(-1);
+ EXPECT_EQ(-1, buffer.front());
+ EXPECT_EQ(0, buffer.back());
+}
+
TEST(RingBufferTest, Subscripting) {
RingBuffer<int> buffer(/*capacity=*/2);
buffer.pushBack(1);
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
index 1ab1dd7..afec065 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
@@ -19,6 +19,10 @@
#include "jpegrerrorcode.h"
+#ifndef FLT_MAX
+#define FLT_MAX 0x1.fffffep127f
+#endif
+
namespace android::jpegrecoverymap {
// Color gamuts for image data
@@ -59,12 +63,26 @@
struct jpegr_uncompressed_struct {
// Pointer to the data location.
void* data;
- // Width of the recovery map or image in pixels.
+ // Width of the recovery map or the luma plane of the image in pixels.
int width;
- // Height of the recovery map or image in pixels.
+ // Height of the recovery map or the luma plane of the image in pixels.
int height;
// Color gamut.
jpegr_color_gamut colorGamut;
+
+ // Values below are optional
+ // Pointer to chroma data, if it's NULL, chroma plane is considered to be immediately
+ // following after the luma plane.
+ // Note: currently this feature is only supported for P010 image (HDR input).
+ void* chroma_data = nullptr;
+ // Strides of Y plane in number of pixels, using 0 to present uninitialized, must be
+ // larger than or equal to luma width.
+ // Note: currently this feature is only supported for P010 image (HDR input).
+ int luma_stride = 0;
+ // Strides of UV plane in number of pixels, using 0 to present uninitialized, must be
+ // larger than or equal to chroma width.
+ // Note: currently this feature is only supported for P010 image (HDR input).
+ int chroma_stride = 0;
};
/*
@@ -206,7 +224,8 @@
*
* @param compressed_jpegr_image compressed JPEGR image.
* @param dest destination of the uncompressed JPEGR image.
- * @param max_display_boost (optional) the maximum available boost supported by a display
+ * @param max_display_boost (optional) the maximum available boost supported by a display,
+ * the value must be greater than or equal to 1.0.
* @param exif destination of the decoded EXIF metadata. The default value is NULL where the
decoder will do nothing about it. If configured not NULL the decoder will write
EXIF data into this structure. The format is defined in {@code jpegr_exif_struct}
@@ -235,7 +254,7 @@
*/
status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
jr_uncompressed_ptr dest,
- float max_display_boost = -1.0f,
+ float max_display_boost = FLT_MAX,
jr_exif_ptr exif = nullptr,
jpegr_output_format output_format = JPEGR_OUTPUT_HDR_LINEAR,
jr_uncompressed_ptr recovery_map = nullptr,
@@ -358,6 +377,16 @@
*/
status_t toneMap(jr_uncompressed_ptr src,
jr_uncompressed_ptr dest);
+
+ /*
+ * This method will check the validity of the input images.
+ *
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @return NO_ERROR if the input images are valid, error code is not valid.
+ */
+ status_t areInputImagesValid(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image);
};
} // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
index f730343..159aaa8 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
@@ -46,6 +46,8 @@
ERROR_JPEGR_CALCULATION_ERROR = JPEGR_RUNTIME_ERROR_BASE - 3,
ERROR_JPEGR_METADATA_ERROR = JPEGR_RUNTIME_ERROR_BASE - 4,
ERROR_JPEGR_TONEMAP_ERROR = JPEGR_RUNTIME_ERROR_BASE - 5,
+
+ ERROR_JPEGR_UNSUPPORTED_FEATURE = -20000,
};
} // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index 67d2a6a..a32b291 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -116,11 +116,17 @@
}
inline uint16_t floatToHalf(float f) {
- uint32_t x = *((uint32_t*)&f);
- uint16_t h = ((x >> 16) & 0x8000)
- | ((((x & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
- | ((x >> 13) & 0x03ff);
- return h;
+ // round-to-nearest-even: add last bit after truncated mantissa
+ const uint32_t b = *((uint32_t*)&f) + 0x00001000;
+
+ const uint32_t e = (b & 0x7F800000) >> 23; // exponent
+ const uint32_t m = b & 0x007FFFFF; // mantissa
+
+ // sign : normalized : denormalized : saturate
+ return (b & 0x80000000) >> 16
+ | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13)
+ | ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1)
+ | (e > 143) * 0x7FFF;
}
constexpr size_t kRecoveryFactorPrecision = 10;
@@ -361,6 +367,8 @@
* luminances in linear space, and the hdr ratio to encode against.
*/
uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata);
+uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata,
+ float log2MinContentBoost, float log2MaxContentBoost);
/*
* Calculates the linear luminance in nits after applying the given recovery
diff --git a/libs/jpegrecoverymap/jpegr.cpp b/libs/jpegrecoverymap/jpegr.cpp
index e395d51..cdf685e 100644
--- a/libs/jpegrecoverymap/jpegr.cpp
+++ b/libs/jpegrecoverymap/jpegr.cpp
@@ -86,6 +86,54 @@
return cpuCoreCount;
}
+status_t JpegR::areInputImagesValid(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image) {
+ if (uncompressed_p010_image == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ if (uncompressed_p010_image->width % kJpegBlock != 0
+ || uncompressed_p010_image->height % 2 != 0) {
+ ALOGE("Image size can not be handled: %dx%d.",
+ uncompressed_p010_image->width, uncompressed_p010_image->height);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (uncompressed_p010_image->luma_stride != 0
+ && uncompressed_p010_image->luma_stride < uncompressed_p010_image->width) {
+ ALOGE("Image stride can not be smaller than width, stride=%d, width=%d",
+ uncompressed_p010_image->luma_stride, uncompressed_p010_image->width);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (uncompressed_yuv_420_image == nullptr) {
+ return NO_ERROR;
+ }
+
+ if (uncompressed_yuv_420_image->luma_stride != 0) {
+ ALOGE("Stride is not supported for YUV420 image");
+ return ERROR_JPEGR_UNSUPPORTED_FEATURE;
+ }
+
+ if (uncompressed_yuv_420_image->chroma_data != nullptr) {
+ ALOGE("Pointer to chroma plane is not supported for YUV420 image, chroma data must"
+ "be immediately after the luma data.");
+ return ERROR_JPEGR_UNSUPPORTED_FEATURE;
+ }
+
+ if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
+ || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
+ ALOGE("Image resolutions mismatch: P010: %dx%d, YUV420: %dx%d",
+ uncompressed_p010_image->width,
+ uncompressed_p010_image->height,
+ uncompressed_yuv_420_image->width,
+ uncompressed_yuv_420_image->height);
+ return ERROR_JPEGR_RESOLUTION_MISMATCH;
+ }
+
+ return NO_ERROR;
+}
+
/* Encode API-0 */
status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jpegr_transfer_function hdr_tf,
@@ -100,11 +148,9 @@
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- if (uncompressed_p010_image->width % kJpegBlock != 0
- || uncompressed_p010_image->height % 2 != 0) {
- ALOGE("Image size can not be handled: %dx%d",
- uncompressed_p010_image->width, uncompressed_p010_image->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ if (status_t ret = areInputImagesValid(
+ uncompressed_p010_image, /* uncompressed_yuv_420_image */ nullptr) != NO_ERROR) {
+ return ret;
}
jpegr_metadata_struct metadata;
@@ -164,16 +210,9 @@
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
- || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
- return ERROR_JPEGR_RESOLUTION_MISMATCH;
- }
-
- if (uncompressed_p010_image->width % kJpegBlock != 0
- || uncompressed_p010_image->height % 2 != 0) {
- ALOGE("Image size can not be handled: %dx%d",
- uncompressed_p010_image->width, uncompressed_p010_image->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ if (status_t ret = areInputImagesValid(
+ uncompressed_p010_image, uncompressed_yuv_420_image) != NO_ERROR) {
+ return ret;
}
jpegr_metadata_struct metadata;
@@ -223,16 +262,9 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
- || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
- return ERROR_JPEGR_RESOLUTION_MISMATCH;
- }
-
- if (uncompressed_p010_image->width % kJpegBlock != 0
- || uncompressed_p010_image->height % 2 != 0) {
- ALOGE("Image size can not be handled: %dx%d",
- uncompressed_p010_image->width, uncompressed_p010_image->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ if (status_t ret = areInputImagesValid(
+ uncompressed_p010_image, uncompressed_yuv_420_image) != NO_ERROR) {
+ return ret;
}
jpegr_metadata_struct metadata;
@@ -266,11 +298,9 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- if (uncompressed_p010_image->width % kJpegBlock != 0
- || uncompressed_p010_image->height % 2 != 0) {
- ALOGE("Image size can not be handled: %dx%d",
- uncompressed_p010_image->width, uncompressed_p010_image->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ if (status_t ret = areInputImagesValid(
+ uncompressed_p010_image, /* uncompressed_yuv_420_image */ nullptr) != NO_ERROR) {
+ return ret;
}
JpegDecoderHelper jpeg_decoder;
@@ -339,6 +369,10 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
+ if (max_display_boost < 1.0f) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
if (output_format == JPEGR_OUTPUT_SDR) {
JpegDecoderHelper jpeg_decoder;
if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
@@ -586,6 +620,8 @@
metadata->maxContentBoost = hdr_white_nits / kSdrWhiteNits;
metadata->minContentBoost = 1.0f;
+ float log2MinBoost = log2(metadata->minContentBoost);
+ float log2MaxBoost = log2(metadata->maxContentBoost);
ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
@@ -613,7 +649,8 @@
std::function<void()> generateMap = [uncompressed_yuv_420_image, uncompressed_p010_image,
metadata, dest, hdrInvOetf, hdrGamutConversionFn,
- luminanceFn, hdr_white_nits, &jobQueue]() -> void {
+ luminanceFn, hdr_white_nits, log2MinBoost, log2MaxBoost,
+ &jobQueue]() -> void {
size_t rowStart, rowEnd;
size_t dest_map_width = uncompressed_yuv_420_image->width / kMapDimensionScaleFactor;
size_t dest_map_stride = dest->width;
@@ -638,7 +675,7 @@
size_t pixel_idx = x + y * dest_map_stride;
reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
- encodeRecovery(sdr_y_nits, hdr_y_nits, metadata);
+ encodeRecovery(sdr_y_nits, hdr_y_nits, metadata, log2MinBoost, log2MaxBoost);
}
}
}
@@ -680,9 +717,7 @@
dest->width = uncompressed_yuv_420_image->width;
dest->height = uncompressed_yuv_420_image->height;
ShepardsIDW idwTable(kMapDimensionScaleFactor);
- float display_boost = max_display_boost > 0 ?
- std::min(max_display_boost, metadata->maxContentBoost)
- : metadata->maxContentBoost;
+ float display_boost = std::min(max_display_boost, metadata->maxContentBoost);
RecoveryLUT recoveryLUT(metadata, display_boost);
JobQueue jobQueue;
@@ -993,25 +1028,43 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
+ size_t src_luma_stride = src->luma_stride;
+ size_t src_chroma_stride = src->chroma_stride;
+ uint16_t* src_luma_data = reinterpret_cast<uint16_t*>(src->data);
+ uint16_t* src_chroma_data = reinterpret_cast<uint16_t*>(src->chroma_data);
+
+ if (src_chroma_data == nullptr) {
+ src_chroma_data = &reinterpret_cast<uint16_t*>(src->data)[src->luma_stride * src->height];
+ }
+ if (src_luma_stride == 0) {
+ src_luma_stride = src->width;
+ }
+ if (src_chroma_stride == 0) {
+ src_chroma_stride = src_luma_stride;
+ }
+
dest->width = src->width;
dest->height = src->height;
- size_t pixel_count = src->width * src->height;
+ size_t dest_luma_pixel_count = dest->width * dest->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);
+ size_t src_y_idx = y * src_luma_stride + x;
+ size_t src_u_idx = (y >> 1) * src_chroma_stride + (x & ~0x1);
+ size_t src_v_idx = src_u_idx + 1;
- 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;
+ uint16_t y_uint = src_luma_data[src_y_idx] >> 6;
+ uint16_t u_uint = src_chroma_data[src_u_idx] >> 6;
+ uint16_t v_uint = src_chroma_data[src_v_idx] >> 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];
+ size_t dest_y_idx = x + y * dest->width;
+ size_t dest_uv_idx = x / 2 + (y / 2) * (dest->width / 2);
+
+ uint8_t* y = &reinterpret_cast<uint8_t*>(dest->data)[dest_y_idx];
+ uint8_t* u = &reinterpret_cast<uint8_t*>(dest->data)[dest_luma_pixel_count + dest_uv_idx];
+ uint8_t* v = &reinterpret_cast<uint8_t*>(
+ dest->data)[dest_luma_pixel_count * 5 / 4 + dest_uv_idx];
*y = static_cast<uint8_t>((y_uint >> 2) & 0xff);
*u = static_cast<uint8_t>((u_uint >> 2) & 0xff);
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 2cffde3..ce6fc8f 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -443,6 +443,12 @@
////////////////////////////////////////////////////////////////////////////////
// Recovery map calculations
uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata) {
+ return encodeRecovery(y_sdr, y_hdr, metadata,
+ log2(metadata->minContentBoost), log2(metadata->maxContentBoost));
+}
+
+uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata,
+ float log2MinContentBoost, float log2MaxContentBoost) {
float gain = 1.0f;
if (y_sdr > 0.0f) {
gain = y_hdr / y_sdr;
@@ -451,8 +457,8 @@
if (gain < metadata->minContentBoost) gain = metadata->minContentBoost;
if (gain > metadata->maxContentBoost) gain = metadata->maxContentBoost;
- return static_cast<uint8_t>((log2(gain) - log2(metadata->minContentBoost))
- / (log2(metadata->maxContentBoost) - log2(metadata->minContentBoost))
+ return static_cast<uint8_t>((log2(gain) - log2MinContentBoost)
+ / (log2MaxContentBoost - log2MinContentBoost)
* 255.0f);
}
@@ -493,17 +499,28 @@
}
Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
- size_t pixel_count = image->width * image->height;
+ size_t luma_stride = image->luma_stride;
+ size_t chroma_stride = image->chroma_stride;
+ uint16_t* luma_data = reinterpret_cast<uint16_t*>(image->data);
+ uint16_t* chroma_data = reinterpret_cast<uint16_t*>(image->chroma_data);
- size_t pixel_y_idx = x + y * image->width;
- size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2);
+ if (luma_stride == 0) {
+ luma_stride = image->width;
+ }
+ if (chroma_stride == 0) {
+ chroma_stride = luma_stride;
+ }
+ if (chroma_data == nullptr) {
+ chroma_data = &reinterpret_cast<uint16_t*>(image->data)[image->luma_stride * image->height];
+ }
- uint16_t y_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_y_idx]
- >> 6;
- uint16_t u_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2]
- >> 6;
- uint16_t v_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2 + 1]
- >> 6;
+ size_t pixel_y_idx = y * luma_stride + x;
+ size_t pixel_u_idx = (y >> 1) * chroma_stride + (x & ~0x1);
+ size_t pixel_v_idx = pixel_u_idx + 1;
+
+ uint16_t y_uint = luma_data[pixel_y_idx] >> 6;
+ uint16_t u_uint = chroma_data[pixel_u_idx] >> 6;
+ uint16_t v_uint = chroma_data[pixel_v_idx] >> 6;
// Conversions include taking narrow-range into account.
return {{{ (static_cast<float>(y_uint) - 64.0f) / 876.0f,
diff --git a/libs/jpegrecoverymap/tests/data/raw_p010_image_with_stride.p010 b/libs/jpegrecoverymap/tests/data/raw_p010_image_with_stride.p010
new file mode 100644
index 0000000..e7a5dc8
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/raw_p010_image_with_stride.p010
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/jpegr_test.cpp b/libs/jpegrecoverymap/tests/jpegr_test.cpp
index 7c669ab..229d7dc 100644
--- a/libs/jpegrecoverymap/tests/jpegr_test.cpp
+++ b/libs/jpegrecoverymap/tests/jpegr_test.cpp
@@ -24,10 +24,12 @@
#include <utils/Log.h>
#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
+#define RAW_P010_IMAGE_WITH_STRIDE "/sdcard/Documents/raw_p010_image_with_stride.p010"
#define RAW_YUV420_IMAGE "/sdcard/Documents/raw_yuv420_image.yuv420"
#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg"
#define TEST_IMAGE_WIDTH 1280
#define TEST_IMAGE_HEIGHT 720
+#define TEST_IMAGE_STRIDE 1288
#define DEFAULT_JPEG_QUALITY 90
#define SAVE_ENCODING_RESULT true
@@ -97,6 +99,7 @@
virtual void TearDown();
struct jpegr_uncompressed_struct mRawP010Image;
+ struct jpegr_uncompressed_struct mRawP010ImageWithStride;
struct jpegr_uncompressed_struct mRawYuv420Image;
struct jpegr_compressed_struct mJpegImage;
};
@@ -107,6 +110,7 @@
void JpegRTest::SetUp() {}
void JpegRTest::TearDown() {
free(mRawP010Image.data);
+ free(mRawP010ImageWithStride.data);
free(mRawYuv420Image.data);
free(mJpegImage.data);
}
@@ -249,6 +253,61 @@
free(decodedJpegR.data);
}
+/* Test Encode API-0 (with stride) and decode */
+TEST_F(JpegRTest, encodeFromP010WithStrideThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE_WITH_STRIDE, mRawP010ImageWithStride.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE_WITH_STRIDE << " failed";
+ }
+ mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
+ mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
+ mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
+ mRawP010ImageWithStride.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ 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(
+ &mRawP010ImageWithStride, 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_p010_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 * 8;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = jpegRCodec.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_p010_input.rgb";
+ 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(JpegRTest, encodeFromRawHdrAndSdrThenDecode) {
int ret;
diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
index 5ef79e9..2369a7e 100644
--- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -952,6 +952,27 @@
| static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff)) << 20);
}
+TEST_F(RecoveryMapMathTest, ColorToRgbaF16) {
+ EXPECT_EQ(colorToRgbaF16(RgbBlack()), ((uint64_t) 0x3C00) << 48);
+ EXPECT_EQ(colorToRgbaF16(RgbWhite()), 0x3C003C003C003C00);
+ EXPECT_EQ(colorToRgbaF16(RgbRed()), (((uint64_t) 0x3C00) << 48) | ((uint64_t) 0x3C00));
+ EXPECT_EQ(colorToRgbaF16(RgbGreen()), (((uint64_t) 0x3C00) << 48) | (((uint64_t) 0x3C00) << 16));
+ EXPECT_EQ(colorToRgbaF16(RgbBlue()), (((uint64_t) 0x3C00) << 48) | (((uint64_t) 0x3C00) << 32));
+
+ Color e_gamma = {{{ 0.1f, 0.2f, 0.3f }}};
+ EXPECT_EQ(colorToRgbaF16(e_gamma), 0x3C0034CD32662E66);
+}
+
+TEST_F(RecoveryMapMathTest, Float32ToFloat16) {
+ EXPECT_EQ(floatToHalf(0.1f), 0x2E66);
+ EXPECT_EQ(floatToHalf(0.0f), 0x0);
+ EXPECT_EQ(floatToHalf(1.0f), 0x3C00);
+ EXPECT_EQ(floatToHalf(-1.0f), 0xBC00);
+ EXPECT_EQ(floatToHalf(0x1.fffffep127f), 0x7FFF); // float max
+ EXPECT_EQ(floatToHalf(-0x1.fffffep127f), 0xFFFF); // float min
+ EXPECT_EQ(floatToHalf(0x1.0p-126f), 0x0); // float zero
+}
+
TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgb) {
EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance),
0.0f);
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 495334e..f0b1072 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -27,6 +27,14 @@
"name": "libinputservice_test"
},
{
+ "name": "libgui_test",
+ "options": [
+ {
+ "native-test-flag": "--gtest_filter=\"InputSurfacesTest*:MultiDisplayTests*\""
+ }
+ ]
+ },
+ {
"name": "CtsHardwareTestCases",
"options": [
{
@@ -123,6 +131,14 @@
"name": "libinputservice_test"
},
{
+ "name": "libgui_test",
+ "options": [
+ {
+ "native-test-flag": "--gtest_filter=\"InputSurfacesTest*:MultiDisplayTests*\""
+ }
+ ]
+ },
+ {
"name": "CtsHardwareTestCases",
"options": [
{
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index eb99438..c19737d 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1855,6 +1855,27 @@
}
}
+ if (!mCurrentRawState.rawPointerData.hoveringIdBits.isEmpty() &&
+ mCurrentRawState.rawPointerData.touchingIdBits.isEmpty() &&
+ mDeviceMode != DeviceMode::UNSCALED) {
+ // We have hovering pointers, and there are no touching pointers.
+ bool hoveringPointersInFrame = false;
+ auto hoveringIds = mCurrentRawState.rawPointerData.hoveringIdBits;
+ while (!hoveringIds.isEmpty()) {
+ uint32_t id = hoveringIds.clearFirstMarkedBit();
+ const auto& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
+ if (isPointInsidePhysicalFrame(pointer.x, pointer.y)) {
+ hoveringPointersInFrame = true;
+ break;
+ }
+ }
+ if (!hoveringPointersInFrame) {
+ // All hovering pointers are outside the physical frame.
+ outConsumed = true;
+ return out;
+ }
+ }
+
if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() &&
!mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
// Pointer just went down. Check for virtual key press or off-screen touches.
@@ -1865,7 +1886,7 @@
if (!isPointInsidePhysicalFrame(pointer.x, pointer.y) &&
mDeviceMode != DeviceMode::UNSCALED) {
// If exactly one pointer went down, check for virtual key hit.
- // Otherwise we will drop the entire stroke.
+ // Otherwise, we will drop the entire stroke.
if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
if (virtualKey) {
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index c5fd5f5..e829692 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -41,6 +41,12 @@
bool isHovering() const;
bool hasStylus() const;
bool hasButtonTouch() const;
+
+ /*
+ * Returns the number of touches reported by the device through its BTN_TOOL_FINGER and
+ * BTN_TOOL_*TAP "buttons". Note that this count includes touches reported with their
+ * ABS_MT_TOOL_TYPE set to MT_TOOL_PALM.
+ */
int getTouchCount() const;
private:
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index e89262a..8841b6e 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -81,11 +81,16 @@
}
schs.fingers.clear();
+ size_t numPalms = 0;
for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
+ if (!slot.isInUse()) {
+ continue;
+ }
// Some touchpads continue to report contacts even after they've identified them as palms.
// We want to exclude these contacts from the HardwareStates.
- if (!slot.isInUse() || slot.getToolType() == ToolType::PALM) {
+ if (slot.getToolType() == ToolType::PALM) {
+ numPalms++;
continue;
}
@@ -103,7 +108,7 @@
}
schs.state.fingers = schs.fingers.data();
schs.state.finger_cnt = schs.fingers.size();
- schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount();
+ schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount() - numPalms;
return schs;
}
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 3e97241..19d46c8 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -13,11 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <gestures/HardwareStateConverter.h>
+
+#include <memory>
#include <EventHub.h>
-#include <gestures/HardwareStateConverter.h>
#include <gtest/gtest.h>
#include <linux/input-event-codes.h>
+#include <utils/StrongPointer.h>
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
@@ -28,38 +31,37 @@
namespace android {
class HardwareStateConverterTest : public testing::Test {
+public:
+ HardwareStateConverterTest()
+ : mFakeEventHub(std::make_shared<FakeEventHub>()),
+ mFakePolicy(sp<FakeInputReaderPolicy>::make()),
+ mReader(mFakeEventHub, mFakePolicy, mFakeListener),
+ mDevice(newDevice()),
+ mDeviceContext(*mDevice, EVENTHUB_ID) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, 7, 0, 0, 0);
+ mConverter = std::make_unique<HardwareStateConverter>(mDeviceContext);
+ }
+
protected:
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
static constexpr int32_t EVENTHUB_ID = 1;
- void SetUp() {
- mFakeEventHub = std::make_unique<FakeEventHub>();
- mFakePolicy = sp<FakeInputReaderPolicy>::make();
- mFakeListener = std::make_unique<TestInputListener>();
- mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
- *mFakeListener);
- mDevice = newDevice();
-
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, 7, 0, 0, 0);
- }
-
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,
+ std::make_shared<InputDevice>(mReader.getContext(), DEVICE_ID, /*generation=*/2,
identifier);
- mReader->pushNextDevice(device);
+ mReader.pushNextDevice(device);
mFakeEventHub->addDevice(EVENTHUB_ID, identifier.name, InputDeviceClass::TOUCHPAD,
identifier.bus);
- mReader->loopOnce();
+ mReader.loopOnce();
return device;
}
- void processAxis(HardwareStateConverter& conv, nsecs_t when, int32_t type, int32_t code,
- int32_t value) {
+ void processAxis(nsecs_t when, int32_t type, int32_t code, int32_t value) {
RawEvent event;
event.when = when;
event.readTime = READ_TIME;
@@ -67,12 +69,11 @@
event.type = type;
event.code = code;
event.value = value;
- std::optional<SelfContainedHardwareState> schs = conv.processRawEvent(&event);
+ std::optional<SelfContainedHardwareState> schs = mConverter->processRawEvent(&event);
EXPECT_FALSE(schs.has_value());
}
- std::optional<SelfContainedHardwareState> processSync(HardwareStateConverter& conv,
- nsecs_t when) {
+ std::optional<SelfContainedHardwareState> processSync(nsecs_t when) {
RawEvent event;
event.when = when;
event.readTime = READ_TIME;
@@ -80,37 +81,37 @@
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
- return conv.processRawEvent(&event);
+ return mConverter->processRawEvent(&event);
}
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
- std::unique_ptr<TestInputListener> mFakeListener;
- std::unique_ptr<InstrumentedInputReader> mReader;
+ TestInputListener mFakeListener;
+ InstrumentedInputReader mReader;
std::shared_ptr<InputDevice> mDevice;
+ InputDeviceContext mDeviceContext;
+ std::unique_ptr<HardwareStateConverter> mConverter;
};
TEST_F(HardwareStateConverterTest, OneFinger) {
const nsecs_t time = 1500000000;
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- HardwareStateConverter conv(deviceContext);
- processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
- processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
- processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MAJOR, 5);
- processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MINOR, 4);
- processAxis(conv, time, EV_ABS, ABS_MT_PRESSURE, 42);
- processAxis(conv, time, EV_ABS, ABS_MT_ORIENTATION, 2);
+ processAxis(time, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(time, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(time, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(time, EV_ABS, ABS_MT_TOUCH_MAJOR, 5);
+ processAxis(time, EV_ABS, ABS_MT_TOUCH_MINOR, 4);
+ processAxis(time, EV_ABS, ABS_MT_PRESSURE, 42);
+ processAxis(time, EV_ABS, ABS_MT_ORIENTATION, 2);
- processAxis(conv, time, EV_ABS, ABS_X, 50);
- processAxis(conv, time, EV_ABS, ABS_Y, 100);
- processAxis(conv, time, EV_ABS, ABS_PRESSURE, 42);
+ processAxis(time, EV_ABS, ABS_X, 50);
+ processAxis(time, EV_ABS, ABS_Y, 100);
+ processAxis(time, EV_ABS, ABS_PRESSURE, 42);
- processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
- processAxis(conv, time, EV_KEY, BTN_TOOL_FINGER, 1);
- std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+ processAxis(time, EV_KEY, BTN_TOUCH, 1);
+ processAxis(time, EV_KEY, BTN_TOOL_FINGER, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(time);
ASSERT_TRUE(schs.has_value());
const HardwareState& state = schs->state;
@@ -138,35 +139,31 @@
}
TEST_F(HardwareStateConverterTest, TwoFingers) {
- const nsecs_t time = ARBITRARY_TIME;
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- HardwareStateConverter conv(deviceContext);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, 5);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, 4);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, 42);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, 2);
- processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
- processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
- processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MAJOR, 5);
- processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MINOR, 4);
- processAxis(conv, time, EV_ABS, ABS_MT_PRESSURE, 42);
- processAxis(conv, time, EV_ABS, ABS_MT_ORIENTATION, 2);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 456);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, -20);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 40);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, 8);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, 7);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, 21);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, 1);
- processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 1);
- processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 456);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, -20);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 40);
- processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MAJOR, 8);
- processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MINOR, 7);
- processAxis(conv, time, EV_ABS, ABS_MT_PRESSURE, 21);
- processAxis(conv, time, EV_ABS, ABS_MT_ORIENTATION, 1);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_X, 50);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_Y, 100);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_PRESSURE, 42);
- processAxis(conv, time, EV_ABS, ABS_X, 50);
- processAxis(conv, time, EV_ABS, ABS_Y, 100);
- processAxis(conv, time, EV_ABS, ABS_PRESSURE, 42);
-
- processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
- processAxis(conv, time, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
- std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
ASSERT_TRUE(schs.has_value());
ASSERT_EQ(2, schs->state.finger_cnt);
@@ -192,59 +189,58 @@
}
TEST_F(HardwareStateConverterTest, OnePalm) {
- const nsecs_t time = ARBITRARY_TIME;
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- HardwareStateConverter conv(deviceContext);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
- processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
- processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
- processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
-
- processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
- std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(0, schs->state.touch_cnt);
EXPECT_EQ(0, schs->state.finger_cnt);
}
TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalm) {
- const nsecs_t time = ARBITRARY_TIME;
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- HardwareStateConverter conv(deviceContext);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
- processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
- processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
- processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1);
- processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
-
- std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+ std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(1, schs->state.touch_cnt);
EXPECT_EQ(1, schs->state.finger_cnt);
- processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 51);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 99);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 51);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 99);
- schs = processSync(conv, time);
+ schs = processSync(ARBITRARY_TIME);
ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(0, schs->state.touch_cnt);
ASSERT_EQ(0, schs->state.finger_cnt);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 53);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 97);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 53);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 97);
- schs = processSync(conv, time);
+ schs = processSync(ARBITRARY_TIME);
ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(0, schs->state.touch_cnt);
EXPECT_EQ(0, schs->state.finger_cnt);
- processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 55);
- processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 95);
- schs = processSync(conv, time);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 55);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 95);
+ schs = processSync(ARBITRARY_TIME);
ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(1, schs->state.touch_cnt);
ASSERT_EQ(1, schs->state.finger_cnt);
const FingerState& newFinger = schs->state.fingers[0];
EXPECT_EQ(123, newFinger.tracking_id);
@@ -253,25 +249,16 @@
}
TEST_F(HardwareStateConverterTest, ButtonPressed) {
- const nsecs_t time = ARBITRARY_TIME;
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- HardwareStateConverter conv(deviceContext);
-
- processAxis(conv, time, EV_KEY, BTN_LEFT, 1);
- std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
ASSERT_TRUE(schs.has_value());
EXPECT_EQ(GESTURES_BUTTON_LEFT, schs->state.buttons_down);
}
TEST_F(HardwareStateConverterTest, MscTimestamp) {
- const nsecs_t time = ARBITRARY_TIME;
- mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- HardwareStateConverter conv(deviceContext);
-
- processAxis(conv, time, EV_MSC, MSC_TIMESTAMP, 1200000);
- std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+ processAxis(ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1200000);
+ std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
ASSERT_TRUE(schs.has_value());
EXPECT_NEAR(1.2, schs->state.msc_timestamp, EPSILON);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 2223b35..fb082da 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6735,6 +6735,54 @@
ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mDevice->getSources());
}
+TEST_F(SingleTouchInputMapperTest, HoverEventsOutsidePhysicalFrameAreIgnored) {
+ // Initialize the device without setting device source to touch navigation.
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
+
+ // Set a physical frame in the display viewport.
+ auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ viewport->physicalLeft = 0;
+ viewport->physicalTop = 0;
+ viewport->physicalRight = DISPLAY_WIDTH / 2;
+ viewport->physicalBottom = DISPLAY_HEIGHT / 2;
+ mFakePolicy->updateViewport(*viewport);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ // Hovering inside the physical frame produces events.
+ processKey(mapper, BTN_TOOL_PEN, 1);
+ processMove(mapper, RAW_X_MIN + 1, RAW_Y_MIN + 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)));
+
+ // Leaving the physical frame ends the hovering gesture.
+ processMove(mapper, RAW_X_MAX - 1, RAW_Y_MAX - 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)));
+
+ // Moving outside the physical frame does not produce events.
+ processMove(mapper, RAW_X_MAX - 2, RAW_Y_MAX - 2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Re-entering the physical frame produces events.
+ processMove(mapper, RAW_X_MIN, RAW_Y_MIN);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)));
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index e720af5..d64231f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1574,9 +1574,10 @@
}
void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
- if (mPlanner) {
- mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime,
- getState().usesDeviceComposition || getSkipColorTransform());
+ const auto& outputState = getState();
+ if (mPlanner && outputState.isEnabled) {
+ mPlanner->renderCachedSets(outputState, refreshArgs.scheduledFrameTime,
+ outputState.usesDeviceComposition || getSkipColorTransform());
}
}
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index c30465f..5913d4b 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -30,7 +30,7 @@
auto lhsLayer = lhs.first->getLayer();
auto rhsLayer = rhs.first->getLayer();
if (lhsLayer->layerStack.id != rhsLayer->layerStack.id) {
- return lhsLayer->layerStack.id > rhsLayer->layerStack.id;
+ return lhsLayer->layerStack.id < rhsLayer->layerStack.id;
}
if (lhsLayer->z != rhsLayer->z) {
return lhsLayer->z < rhsLayer->z;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 25cbe7a..ce7d37e 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -817,7 +817,8 @@
snapshot.frameRateSelectionPriority = requested.frameRateSelectionPriority;
}
- if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content)) {
+ if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content) ||
+ snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
snapshot.color.rgb = requested.getColor().rgb;
snapshot.isColorspaceAgnostic = requested.colorSpaceAgnostic;
snapshot.backgroundBlurRadius = args.supportsBlur
@@ -1069,6 +1070,10 @@
// touches from going outside the cloned area.
if (path.isClone()) {
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE;
+ // Cloned layers shouldn't handle watch outside since their z order is not determined by
+ // WM or the client.
+ snapshot.inputInfo.inputConfig.clear(gui::WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH);
+
mNeedsTouchableRegionCrop.insert(path);
}
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 4dcdd96..b397b82 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -32,10 +32,6 @@
using namespace ftl::flag_operators;
namespace {
-std::string layerIdToString(uint32_t layerId) {
- return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId);
-}
-
std::string layerIdsToString(const std::vector<uint32_t>& layerIds) {
std::stringstream stream;
stream << "{";
@@ -326,9 +322,13 @@
std::string RequestedLayerState::getDebugString() const {
std::stringstream debug;
- debug << "RequestedLayerState{" << name << " parent=" << layerIdToString(parentId)
- << " relativeParent=" << layerIdToString(relativeParentId)
- << " mirrorId=" << layerIdsToString(mirrorIds) << " handle=" << handleAlive << " z=" << z;
+ debug << "RequestedLayerState{" << name;
+ if (parentId != UNASSIGNED_LAYER_ID) debug << " parentId=" << parentId;
+ if (relativeParentId != UNASSIGNED_LAYER_ID) debug << " relativeParentId=" << relativeParentId;
+ if (!mirrorIds.empty()) debug << " mirrorId=" << layerIdsToString(mirrorIds);
+ if (!handleAlive) debug << " !handle";
+ if (z != 0) debug << " z=" << z;
+ if (layerStack.id != 0) debug << " layerStack=" << layerStack.id;
return debug.str();
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 5a90d58..bae3739 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -78,17 +78,16 @@
.count();
}
-bool LayerInfo::isFrequent(nsecs_t now) const {
- using fps_approx_ops::operator>=;
+LayerInfo::Frequent LayerInfo::isFrequent(nsecs_t now) const {
// If we know nothing about this layer (e.g. after touch event),
// we consider it as frequent as it might be the start of an animation.
if (mFrameTimes.size() < kFrequentLayerWindowSize) {
- return true;
+ return {/* isFrequent */ true, /* clearHistory */ false, /* isConclusive */ true};
}
// Non-active layers are also infrequent
if (mLastUpdatedTime < getActiveLayerThreshold(now)) {
- return false;
+ return {/* isFrequent */ false, /* clearHistory */ false, /* isConclusive */ true};
}
// We check whether we can classify this layer as frequent or infrequent:
@@ -111,12 +110,20 @@
}
if (isFrequent || isInfrequent) {
- return isFrequent;
+ // If the layer was previously inconclusive, we clear
+ // the history as indeterminate layers changed to frequent,
+ // and we should not look at the stale data.
+ return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true};
}
// If we can't determine whether the layer is frequent or not, we return
- // the last known classification.
- return !mLastRefreshRate.infrequent;
+ // the last known classification and mark the layer frequency as inconclusive.
+ isFrequent = !mLastRefreshRate.infrequent;
+
+ // If the layer was previously tagged as animating, we clear
+ // the history as it is likely the layer just changed its behavior,
+ // and we should not look at stale data.
+ return {isFrequent, isFrequent && mLastRefreshRate.animating, /* isConclusive */ false};
}
Fps LayerInfo::getFps(nsecs_t now) const {
@@ -273,19 +280,18 @@
return {LayerHistory::LayerVoteType::Max, Fps()};
}
- if (!isFrequent(now)) {
+ const LayerInfo::Frequent frequent = isFrequent(now);
+ mIsFrequencyConclusive = frequent.isConclusive;
+ if (!frequent.isFrequent) {
ATRACE_FORMAT_INSTANT("infrequent");
ALOGV("%s is infrequent", mName.c_str());
mLastRefreshRate.infrequent = true;
- // Infrequent layers vote for mininal refresh rate for
+ // Infrequent layers vote for minimal refresh rate for
// battery saving purposes and also to prevent b/135718869.
return {LayerHistory::LayerVoteType::Min, Fps()};
}
- // If the layer was previously tagged as animating or infrequent, we clear
- // the history as it is likely the layer just changed its behavior
- // and we should not look at stale data
- if (mLastRefreshRate.animating || mLastRefreshRate.infrequent) {
+ if (frequent.clearHistory) {
clearHistory(now);
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index a3523ac..c5a6057 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -181,6 +181,7 @@
mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
mLastRefreshRate = {};
mRefreshRateHistory.clear();
+ mIsFrequencyConclusive = true;
}
void clearHistory(nsecs_t now) {
@@ -251,7 +252,15 @@
static constexpr float MARGIN_CONSISTENT_FPS = 1.0;
};
- bool isFrequent(nsecs_t now) const;
+ // Represents whether we were able to determine either layer is frequent or infrequent
+ bool mIsFrequencyConclusive = true;
+ struct Frequent {
+ bool isFrequent;
+ bool clearHistory;
+ // Represents whether we were able to determine isFrequent conclusively
+ bool isConclusive;
+ };
+ Frequent isFrequent(nsecs_t now) const;
bool isAnimating(nsecs_t now) const;
bool hasEnoughDataForHeuristic() const;
std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateSelector&, nsecs_t now);
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index 57752b7..155a275 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -7,6 +7,14 @@
"name": "libcompositionengine_test"
},
{
+ "name": "libgui_test",
+ "options": [
+ {
+ "native-test-flag": "--gtest_filter=\"InputSurfacesTest*:MultiDisplayTests*\""
+ }
+ ]
+ },
+ {
"name": "libscheduler_test"
}
],
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index ddf3363..88d39db 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -662,10 +662,9 @@
mLifecycleManager.commitChanges();
LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
UPDATE_AND_VERIFY(hierarchyBuilder);
- std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 21};
+ std::vector<uint32_t> expectedTraversalPath = {2, 21, 1, 11};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
- expectedTraversalPath = {1, 11, 2, 21};
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
expectedTraversalPath = {};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
@@ -678,8 +677,8 @@
setLayerStack(3, 1);
UPDATE_AND_VERIFY(hierarchyBuilder);
- std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2,
- 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3,
+ 1, 11, 111, 12, 121, 122, 1221, 13, 2};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
expected = {};
@@ -693,7 +692,7 @@
setLayerStack(3, 1);
UPDATE_AND_VERIFY(hierarchyBuilder);
- std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
expected = {};
@@ -710,8 +709,8 @@
createRootLayer(4);
UPDATE_AND_VERIFY(hierarchyBuilder);
- std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2, 4,
- 1, 11, 111, 12, 121, 122, 1221, 13, 2, 4};
+ std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 4, 3,
+ 1, 11, 111, 12, 121, 122, 1221, 13, 2, 4};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
expected = {};
@@ -729,7 +728,7 @@
destroyLayerHandle(1);
UPDATE_AND_VERIFY(hierarchyBuilder);
- std::vector<uint32_t> expected = {3, 2, 2};
+ std::vector<uint32_t> expected = {2, 3, 2};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
expected = {11, 111, 12, 121, 122, 1221, 13};
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 79cfd6a..4301186 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -297,6 +297,17 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setBackgroundBlurRadius(uint32_t id, uint32_t backgroundBlurRadius) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.backgroundBlurRadius = backgroundBlurRadius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index b767276..85d86a7 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -84,7 +84,7 @@
auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
const auto& infos = history().mActiveLayerInfos;
return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
- return pair.second.second->isFrequent(now);
+ return pair.second.second->isFrequent(now).isFrequent;
});
}
@@ -95,6 +95,13 @@
});
}
+ auto clearLayerHistoryCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isFrequent(now).clearHistory;
+ });
+ }
+
void setDefaultLayerVote(Layer* layer,
LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
auto [found, layerPair] = history().findLayer(layer->getSequence());
@@ -764,6 +771,7 @@
time += std::chrono::nanoseconds(3s).count();
history().record(layer->getSequence(), layer->getLayerProps(), time, time,
LayerHistory::LayerUpdateType::Buffer);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
@@ -778,6 +786,7 @@
time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
history().record(layer->getSequence(), layer->getLayerProps(), time, time,
LayerHistory::LayerUpdateType::Buffer);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -787,6 +796,7 @@
// posting another buffer should keep the layer infrequent
history().record(layer->getSequence(), layer->getLayerProps(), time, time,
LayerHistory::LayerUpdateType::Buffer);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -798,6 +808,7 @@
LayerHistory::LayerUpdateType::Buffer);
history().record(layer->getSequence(), layer->getLayerProps(), time, time,
LayerHistory::LayerUpdateType::Buffer);
+ EXPECT_EQ(1, clearLayerHistoryCount(time));
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -808,6 +819,7 @@
time += std::chrono::nanoseconds(3s).count();
history().record(layer->getSequence(), layer->getLayerProps(), time, time,
LayerHistory::LayerUpdateType::Buffer);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -818,6 +830,64 @@
time += (60_Hz).getPeriodNsecs();
history().record(layer->getSequence(), layer->getLayerProps(), time, time,
LayerHistory::LayerUpdateType::Buffer);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, inconclusiveLayerBecomingFrequent) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // Fill up the window with frequent updates
+ for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += (60_Hz).getPeriodNsecs();
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ }
+
+ // posting infrequent buffers after long inactivity should make the layer
+ // inconclusive but frequent.
+ time += std::chrono::nanoseconds(3s).count();
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting more buffers should make the layer frequent and switch the refresh rate to max
+ // by clearing the history
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ EXPECT_EQ(1, clearLayerHistoryCount(time));
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 5a066a6..b8a7446 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -79,6 +79,7 @@
.displays = mFrontEndDisplayInfos,
.displayChanges = hasDisplayChanges,
.globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
.supportedLayerGenericMetadata = {},
.genericLayerMetadataKeyMap = {}};
actualBuilder.update(args);
@@ -333,6 +334,19 @@
EXPECT_EQ(getSnapshot({.id = 111})->inputInfo.touchableRegion.bounds(), modifiedTouchCrop);
}
+TEST_F(LayerSnapshotTest, blurUpdatesWhenAlphaChanges) {
+ static constexpr int blurRadius = 42;
+ setBackgroundBlurRadius(1221, blurRadius);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->backgroundBlurRadius, blurRadius);
+
+ static constexpr float alpha = 0.5;
+ setAlpha(12, alpha);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->backgroundBlurRadius, blurRadius * alpha);
+}
+
// Display Mirroring Tests
// tree with 3 levels of children
// ROOT (DISPLAY 0)
@@ -352,7 +366,7 @@
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
- std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3, 1, 11, 111, 13, 2};
UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
}
@@ -371,8 +385,8 @@
createDisplayMirrorLayer(4, ui::LayerStack::fromValue(0));
setLayerStack(4, 4);
- std::vector<uint32_t> expected = {4, 1, 11, 111, 13, 2, 3, 1, 11,
- 111, 13, 2, 1, 11, 111, 13, 2};
+ std::vector<uint32_t> expected = {1, 11, 111, 13, 2, 3, 1, 11, 111,
+ 13, 2, 4, 1, 11, 111, 13, 2};
UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootId = 3})->outputFilter.layerStack.id, 3u);
EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootId = 4})->outputFilter.layerStack.id, 4u);
@@ -395,7 +409,7 @@
setCrop(111, Rect{200, 200});
Region touch{Rect{0, 0, 1000, 1000}};
setTouchableRegion(111, touch);
- std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 13, 2};
+ std::vector<uint32_t> expected = {1, 11, 111, 13, 2, 3, 1, 11, 111, 13, 2};
UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
EXPECT_TRUE(getSnapshot({.id = 111})->inputInfo.touchableRegion.hasSameRects(touch));
Region touchCroppedByMirrorRoot{Rect{0, 0, 50, 50}};
@@ -407,7 +421,7 @@
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
- std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3, 1, 11, 111, 13, 2};
UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
destroyLayerHandle(3);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
@@ -417,8 +431,8 @@
size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size();
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
- std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2,
- 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3,
+ 1, 11, 111, 12, 121, 122, 1221, 13, 2};
UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
destroyLayerHandle(3);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);