Merge "Skip renderCachedSets if the Output is not enabled" 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/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index 2d1175b..1c875c0 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -23,6 +23,9 @@
// this cannot be the same as the module name.
stem: "rustBinderTestClientBinary",
test_suites: ["general-tests"],
+ data: [
+ ":rustBinderTestService",
+ ],
}
rust_test {
@@ -36,10 +39,6 @@
// this cannot be the same as the module name.
stem: "rustBinderTestServiceBinary",
test_harness: false,
- // TODO(b/164473602): Remove this setting and add the module to `data`
- // attribute of rustBinderTest.
- auto_gen_config: false,
- test_suites: ["general-tests"],
}
cc_test {
@@ -100,7 +99,7 @@
"libbase",
],
static_libs: [
- "libbinder_rs_serialization_test"
+ "libbinder_rs_serialization_test",
],
srcs: [
"serialization.cpp",
@@ -116,8 +115,10 @@
source_stem: "bindings",
cpp_std: "gnu++17",
bindgen_flags: [
- "--allowlist-type", "Transaction",
- "--allowlist-var", "TESTDATA_.*",
+ "--allowlist-type",
+ "Transaction",
+ "--allowlist-var",
+ "TESTDATA_.*",
],
shared_libs: [
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..6262e18 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
@@ -206,7 +210,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 +240,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,
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..d147130 100644
--- a/libs/jpegrecoverymap/jpegr.cpp
+++ b/libs/jpegrecoverymap/jpegr.cpp
@@ -339,6 +339,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 +590,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 +619,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 +645,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 +687,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;
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 2cffde3..8808b55 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);
}
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/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 1f670c8..4dcdd96 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -158,12 +158,15 @@
RequestedLayerState::Changes::VisibleRegion |
RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
}
- if (clientState.what & layer_state_t::eBufferChanged) {
- changes |= RequestedLayerState::Changes::Buffer;
- }
- if (clientState.what & layer_state_t::eSidebandStreamChanged) {
- changes |= RequestedLayerState::Changes::SidebandStream;
- }
+ }
+ if (clientState.what & layer_state_t::eBufferChanged) {
+ barrierProducerId = std::max(bufferData->producerId, barrierProducerId);
+ barrierFrameNumber = std::max(bufferData->frameNumber, barrierFrameNumber);
+ // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
+ changes |= RequestedLayerState::Changes::Buffer;
+ }
+ if (clientState.what & layer_state_t::eSidebandStreamChanged) {
+ changes |= RequestedLayerState::Changes::SidebandStream;
}
if (what & (layer_state_t::eAlphaChanged)) {
if (oldAlpha == 0 || color.a == 0) {
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 216e95f..f15f023 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -111,6 +111,8 @@
ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
uint32_t touchCropId = UNASSIGNED_LAYER_ID;
uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
+ uint64_t barrierFrameNumber = 0;
+ uint32_t barrierProducerId = 0;
// book keeping states
bool handleAlive = true;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3406e92..a538c6d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -167,6 +167,9 @@
mDrawingState.sequence = 0;
mDrawingState.transform.set(0, 0);
mDrawingState.frameNumber = 0;
+ mDrawingState.barrierFrameNumber = 0;
+ mDrawingState.producerId = 0;
+ mDrawingState.barrierProducerId = 0;
mDrawingState.bufferTransform = 0;
mDrawingState.transformToDisplayInverse = false;
mDrawingState.crop.makeInvalid();
@@ -3070,7 +3073,13 @@
}
mDrawingState.producerId = bufferData.producerId;
+ mDrawingState.barrierProducerId =
+ std::max(mDrawingState.producerId, mDrawingState.barrierProducerId);
mDrawingState.frameNumber = frameNumber;
+ mDrawingState.barrierFrameNumber =
+ std::max(mDrawingState.frameNumber, mDrawingState.barrierFrameNumber);
+
+ // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
mDrawingState.buffer = std::move(buffer);
mDrawingState.clientCacheId = bufferData.cachedBuffer;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 70b4e9b..acdd01d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -139,9 +139,16 @@
ui::Dataspace dataspace;
uint64_t frameNumber;
+ // high watermark framenumber to use to check for barriers to protect ourselves
+ // from out of order transactions
+ uint64_t barrierFrameNumber;
ui::Transform transform;
uint32_t producerId = 0;
+ // high watermark producerId to use to check for barriers to protect ourselves
+ // from out of order transactions
+ uint32_t barrierProducerId = 0;
+
uint32_t bufferTransform;
bool transformToDisplayInverse;
Region transparentRegionHint;
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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2f024ce..8394ffb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1149,7 +1149,10 @@
displayIdOpt = getPhysicalDisplayIdLocked(displayToken);
}
- if (!displayIdOpt) {
+ // TODO (b/277364366): Clients should be updated to pass in the display they
+ // want, rather than us picking an arbitrary one (the pacesetter, in this
+ // case).
+ if (displayToken && !displayIdOpt) {
ALOGE("%s: Invalid physical display token %p", __func__, displayToken.get());
return NAME_NOT_FOUND;
}
@@ -4228,7 +4231,7 @@
// The current producerId is already a newer producer than the buffer that has a
// barrier. This means the incoming buffer is older and we can release it here. We
// don't wait on the barrier since we know that's stale information.
- if (layer->getDrawingState().producerId > s.bufferData->producerId) {
+ if (layer->getDrawingState().barrierProducerId > s.bufferData->producerId) {
layer->callReleaseBufferCallback(s.bufferData->releaseBufferListener,
externalTexture->getBuffer(),
s.bufferData->frameNumber,
@@ -4239,7 +4242,7 @@
return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
}
- if (layer->getDrawingState().frameNumber < s.bufferData->barrierFrameNumber) {
+ if (layer->getDrawingState().barrierFrameNumber < s.bufferData->barrierFrameNumber) {
const bool willApplyBarrierFrame =
flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
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/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());