Merge "Add nullptr check for captureListener in captureScreenCommon"
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 2ae61b9..cc038ae 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -136,6 +136,7 @@
updatableViaApex = manifestInstance.updatableViaApex();
return false; // break (libvintf uses opposite convention)
});
+ if (updatableViaApex.has_value()) return true; // break (found match)
return false; // continue
});
@@ -154,7 +155,7 @@
manifestInstance.interface() + "/" + manifestInstance.instance();
instances.push_back(aname);
}
- return false; // continue
+ return true; // continue (libvintf uses opposite convention)
});
return false; // continue
});
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 5d5a75e..0fd8d8e 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -14,13 +14,15 @@
* limitations under the License.
*/
+#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <android/os/BnServiceCallback.h>
#include <binder/Binder.h>
-#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include <cutils/android_filesystem_config.h>
-#include <gtest/gtest.h>
#include <gmock/gmock.h>
+#include <gtest/gtest.h>
#include "Access.h"
#include "ServiceManager.h"
@@ -75,6 +77,11 @@
return sm;
}
+static bool isCuttlefish() {
+ return android::base::StartsWith(android::base::GetProperty("ro.product.vendor.device", ""),
+ "vsoc_");
+}
+
TEST(AddService, HappyHappy) {
auto sm = getPermissiveServiceManager();
EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
@@ -306,6 +313,49 @@
EXPECT_THAT(out, ElementsAre("sa"));
}
+TEST(Vintf, UpdatableViaApex) {
+ if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+
+ auto sm = getPermissiveServiceManager();
+ std::optional<std::string> updatableViaApex;
+ EXPECT_TRUE(sm->updatableViaApex("android.hardware.camera.provider.ICameraProvider/internal/0",
+ &updatableViaApex)
+ .isOk());
+ EXPECT_EQ(std::make_optional<std::string>("com.google.emulated.camera.provider.hal"),
+ updatableViaApex);
+}
+
+TEST(Vintf, UpdatableViaApex_InvalidNameReturnsNullOpt) {
+ if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+
+ auto sm = getPermissiveServiceManager();
+ std::optional<std::string> updatableViaApex;
+ EXPECT_TRUE(sm->updatableViaApex("android.hardware.camera.provider.ICameraProvider",
+ &updatableViaApex)
+ .isOk()); // missing instance name
+ EXPECT_EQ(std::nullopt, updatableViaApex);
+}
+
+TEST(Vintf, GetUpdatableNames) {
+ if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+
+ auto sm = getPermissiveServiceManager();
+ std::vector<std::string> names;
+ EXPECT_TRUE(sm->getUpdatableNames("com.google.emulated.camera.provider.hal", &names).isOk());
+ EXPECT_EQ(std::vector<
+ std::string>{"android.hardware.camera.provider.ICameraProvider/internal/0"},
+ names);
+}
+
+TEST(Vintf, GetUpdatableNames_InvalidApexNameReturnsEmpty) {
+ if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+
+ auto sm = getPermissiveServiceManager();
+ std::vector<std::string> names;
+ EXPECT_TRUE(sm->getUpdatableNames("non.existing.apex.name", &names).isOk());
+ EXPECT_EQ(std::vector<std::string>{}, names);
+}
+
class CallbackHistorian : public BnServiceCallback {
Status onRegistration(const std::string& name, const sp<IBinder>& binder) override {
registrations.push_back(name);
diff --git a/include/input/Input.h b/include/input/Input.h
index dd74a51..d298d81 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -209,6 +209,8 @@
bool isFromSource(uint32_t source, uint32_t test);
+bool isStylusToolType(uint32_t toolType);
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 07d0a65..4b07608 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -966,7 +966,15 @@
}
}
+void Parcel::setEnforceNoDataAvail(bool enforceNoDataAvail) {
+ mEnforceNoDataAvail = enforceNoDataAvail;
+}
+
binder::Status Parcel::enforceNoDataAvail() const {
+ if (!mEnforceNoDataAvail) {
+ return binder::Status::ok();
+ }
+
const auto n = dataAvail();
if (n == 0) {
return binder::Status::ok();
@@ -3077,6 +3085,7 @@
mAllowFds = true;
mDeallocZero = false;
mOwner = nullptr;
+ mEnforceNoDataAvail = true;
}
void Parcel::scanForFds() const {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 399667d..0820cd1 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -531,56 +531,35 @@
LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server.");
- RpcTransportFd transportFd(unique_fd(TEMP_FAILURE_RETRY(
- socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))));
- if (!transportFd.fd.ok()) {
+ unique_fd socket_fd(TEMP_FAILURE_RETRY(
+ 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));
return -savedErrno;
}
-
- if (0 != TEMP_FAILURE_RETRY(bind(transportFd.fd.get(), addr.addr(), addr.addrSize()))) {
+ if (0 != TEMP_FAILURE_RETRY(bind(socket_fd.get(), addr.addr(), addr.addrSize()))) {
int savedErrno = errno;
ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
return -savedErrno;
}
- // Right now, we create all threads at once, making accept4 slow. To avoid hanging the client,
- // the backlog is increased to a large number.
- // TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced
- // to 1.
- if (0 != TEMP_FAILURE_RETRY(listen(transportFd.fd.get(), 50 /*backlog*/))) {
- int savedErrno = errno;
- ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return -savedErrno;
- }
-
- LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str());
-
- if (status_t status = setupExternalServer(std::move(transportFd.fd)); status != OK) {
- ALOGE("Another thread has set up server while calling setupSocketServer. Race?");
- return status;
- }
- return OK;
+ return setupRawSocketServer(std::move(socket_fd));
}
-status_t RpcServer::setupRawSocketServer(base::unique_fd socket_fd) {
- RpcTransportFd transportFd(std::move(socket_fd));
- if (!transportFd.fd.ok()) {
- int savedErrno = errno;
- ALOGE("Could not get initialized Unix socket: %s", strerror(savedErrno));
- return -savedErrno;
- }
+status_t RpcServer::setupRawSocketServer(unique_fd socket_fd) {
+ LOG_ALWAYS_FATAL_IF(!socket_fd.ok(), "Socket must be setup to listen.");
+
// Right now, we create all threads at once, making accept4 slow. To avoid hanging the client,
// the backlog is increased to a large number.
// TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced
// to 1.
- if (0 != TEMP_FAILURE_RETRY(listen(transportFd.fd.get(), 50 /*backlog*/))) {
+ if (0 != TEMP_FAILURE_RETRY(listen(socket_fd.get(), 50 /*backlog*/))) {
int savedErrno = errno;
ALOGE("Could not listen initialized Unix socket: %s", strerror(savedErrno));
return -savedErrno;
}
- if (status_t status = setupExternalServer(std::move(transportFd.fd)); status != OK) {
+ if (status_t status = setupExternalServer(std::move(socket_fd)); status != OK) {
ALOGE("Another thread has set up server while calling setupSocketServer. Race?");
return status;
}
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 6de6ce8..f730acb 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -150,6 +150,9 @@
// Returns Status(EX_BAD_PARCELABLE) when the Parcel is not consumed.
binder::Status enforceNoDataAvail() const;
+ // This Api is used by fuzzers to skip dataAvail checks.
+ void setEnforceNoDataAvail(bool enforceNoDataAvail);
+
void freeData();
size_t objectsCount() const;
@@ -1329,6 +1332,9 @@
// data to be overridden with zero when deallocated
mutable bool mDeallocZero;
+ // Set this to false to skip dataAvail checks.
+ bool mEnforceNoDataAvail;
+
release_func mOwner;
size_t mReserved;
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index ae07aee..9edb3b6 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -94,6 +94,10 @@
void (*readyCallback)(void* param), void* param) {
auto server = RpcServer::make();
auto fd = unique_fd(android_get_control_socket(name));
+ if (!fd.ok()) {
+ LOG(ERROR) << "Failed to get fd for the socket:" << name;
+ return false;
+ }
if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
<< " error: " << statusToString(status).c_str();
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index c320e8d..8693022 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -129,7 +129,13 @@
}
T* array;
- if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
+ if (!allocator(arrayData, length, &array)) {
+ if (length < 0) {
+ return STATUS_UNEXPECTED_NULL;
+ } else {
+ return STATUS_NO_MEMORY;
+ }
+ }
if (length <= 0) return STATUS_OK;
if (array == nullptr) return STATUS_NO_MEMORY;
@@ -157,7 +163,13 @@
}
char16_t* array;
- if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
+ if (!allocator(arrayData, length, &array)) {
+ if (length < 0) {
+ return STATUS_UNEXPECTED_NULL;
+ } else {
+ return STATUS_NO_MEMORY;
+ }
+ }
if (length <= 0) return STATUS_OK;
if (array == nullptr) return STATUS_NO_MEMORY;
@@ -204,7 +216,13 @@
return status;
}
- if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
+ if (!allocator(arrayData, length)) {
+ if (length < 0) {
+ return STATUS_UNEXPECTED_NULL;
+ } else {
+ return STATUS_NO_MEMORY;
+ }
+ }
if (length <= 0) return STATUS_OK;
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 4e10fa9..ca2cedc 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -502,7 +502,7 @@
let instances = binder::get_declared_instances("android.hardware.light.ILights")
.expect("Could not get declared instances");
- let expected_defaults = if has_lights { 1 } else { 0 };
+ let expected_defaults = usize::from(has_lights);
assert_eq!(expected_defaults, instances.iter().filter(|i| i.as_str() == "default").count());
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 03e4a23..a999d59 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -723,5 +723,7 @@
"smoreland@google.com",
"waghpawan@google.com",
],
+ // Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer
+ hotlists: ["4637097"],
},
}
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 25f6096..86461c8 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -18,6 +18,7 @@
#include <fuzzbinder/random_parcel.h>
#include <android-base/logging.h>
+#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
namespace android {
@@ -30,10 +31,17 @@
.extraFds = {},
};
+ if (provider.ConsumeBool()) {
+ // set calling uid
+ IPCThreadState::self()->restoreCallingIdentity(provider.ConsumeIntegral<int64_t>());
+ }
+
while (provider.remaining_bytes() > 0) {
uint32_t code = provider.ConsumeIntegral<uint32_t>();
uint32_t flags = provider.ConsumeIntegral<uint32_t>();
Parcel data;
+ // for increased fuzz coverage
+ data.setEnforceNoDataAvail(provider.ConsumeBool());
sp<IBinder> target = options.extraBinders.at(
provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1));
@@ -50,6 +58,8 @@
fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options);
Parcel reply;
+ // for increased fuzz coverage
+ reply.setEnforceNoDataAvail(provider.ConsumeBool());
(void)target->transact(code, data, &reply, flags);
// feed back in binders and fds that are returned from the service, so that
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 924e65e..95962af 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -24,10 +24,31 @@
#include <binder/Parcel.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/LayerState.h>
+#include <gui/SurfaceControl.h>
#include <private/gui/ParcelUtils.h>
#include <system/window.h>
#include <utils/Errors.h>
+#define CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD) \
+ { \
+ if ((OTHER.what & CHANGE_FLAG) && (FIELD != OTHER.FIELD)) { \
+ DIFF_RESULT |= CHANGE_FLAG; \
+ } \
+ }
+
+#define CHECK_DIFF2(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1, FIELD2) \
+ { \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1) \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD2) \
+ }
+
+#define CHECK_DIFF3(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1, FIELD2, FIELD3) \
+ { \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1) \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD2) \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD3) \
+ }
+
namespace android {
using gui::FocusRequest;
@@ -626,6 +647,74 @@
}
}
+uint64_t layer_state_t::diff(const layer_state_t& other) const {
+ uint64_t diff = 0;
+ CHECK_DIFF2(diff, ePositionChanged, other, x, y);
+ if (other.what & eLayerChanged) {
+ diff |= eLayerChanged;
+ diff &= ~eRelativeLayerChanged;
+ }
+ CHECK_DIFF(diff, eAlphaChanged, other, color.a);
+ CHECK_DIFF(diff, eMatrixChanged, other, matrix);
+ if (other.what & eTransparentRegionChanged &&
+ (!transparentRegion.hasSameRects(other.transparentRegion))) {
+ diff |= eTransparentRegionChanged;
+ }
+ if (other.what & eFlagsChanged) {
+ uint64_t changedFlags = (flags & other.mask) ^ (other.flags & other.mask);
+ if (changedFlags) diff |= eFlagsChanged;
+ }
+ CHECK_DIFF(diff, eLayerStackChanged, other, layerStack);
+ CHECK_DIFF(diff, eCornerRadiusChanged, other, cornerRadius);
+ CHECK_DIFF(diff, eBackgroundBlurRadiusChanged, other, backgroundBlurRadius);
+ if (other.what & eBlurRegionsChanged) diff |= eBlurRegionsChanged;
+ if (other.what & eRelativeLayerChanged) {
+ diff |= eRelativeLayerChanged;
+ diff &= ~eLayerChanged;
+ }
+ if (other.what & eReparent &&
+ !SurfaceControl::isSameSurface(parentSurfaceControlForChild,
+ other.parentSurfaceControlForChild)) {
+ diff |= eReparent;
+ }
+ CHECK_DIFF(diff, eBufferTransformChanged, other, bufferTransform);
+ CHECK_DIFF(diff, eTransformToDisplayInverseChanged, other, transformToDisplayInverse);
+ CHECK_DIFF(diff, eCropChanged, other, crop);
+ if (other.what & eBufferChanged) diff |= eBufferChanged;
+ CHECK_DIFF(diff, eDataspaceChanged, other, dataspace);
+ CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata);
+ if (other.what & eSurfaceDamageRegionChanged &&
+ (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) {
+ diff |= eSurfaceDamageRegionChanged;
+ }
+ CHECK_DIFF(diff, eApiChanged, other, api);
+ if (other.what & eSidebandStreamChanged) diff |= eSidebandStreamChanged;
+ CHECK_DIFF(diff, eApiChanged, other, api);
+ CHECK_DIFF(diff, eColorTransformChanged, other, colorTransform);
+ if (other.what & eHasListenerCallbacksChanged) diff |= eHasListenerCallbacksChanged;
+ if (other.what & eInputInfoChanged) diff |= eInputInfoChanged;
+ CHECK_DIFF3(diff, eBackgroundColorChanged, other, color.rgb, bgColorAlpha, bgColorDataspace);
+ if (other.what & eMetadataChanged) diff |= eMetadataChanged;
+ CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius);
+ CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor);
+ CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility);
+ CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority);
+ CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
+ changeFrameRateStrategy);
+ CHECK_DIFF(diff, eFixedTransformHintChanged, other, fixedTransformHint);
+ CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh);
+ CHECK_DIFF(diff, eTrustedOverlayChanged, other, isTrustedOverlay);
+ CHECK_DIFF(diff, eStretchChanged, other, stretchEffect);
+ CHECK_DIFF(diff, eBufferCropChanged, other, bufferCrop);
+ CHECK_DIFF(diff, eDestinationFrameChanged, other, destinationFrame);
+ if (other.what & eProducerDisconnect) diff |= eProducerDisconnect;
+ CHECK_DIFF(diff, eDropInputModeChanged, other, dropInputMode);
+ CHECK_DIFF(diff, eColorChanged, other, color.rgb);
+ CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic);
+ CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
+ return diff;
+}
+
bool layer_state_t::hasBufferChanges() const {
return what & layer_state_t::eBufferChanged;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index d18d22d..1e43700 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1250,6 +1250,7 @@
if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) ||
(mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
(mask & layer_state_t::eEnableBackpressure) ||
+ (mask & layer_state_t::eIgnoreDestinationFrame) ||
(mask & layer_state_t::eLayerIsDisplayDecoration)) {
s->what |= layer_state_t::eFlagsChanged;
}
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 09f171d..6ec6bd7 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -201,7 +201,32 @@
void merge(const layer_state_t& other);
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+ // Compares two layer_state_t structs and returns a set of change flags describing all the
+ // states that are different.
+ uint64_t diff(const layer_state_t& other) const;
bool hasBufferChanges() const;
+
+ // Changes to the tree structure.
+ static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eLayerChanged |
+ layer_state_t::eRelativeLayerChanged | layer_state_t::eReparent |
+ layer_state_t::eBackgroundColorChanged;
+ // Content updates.
+ static constexpr uint64_t CONTENT_CHANGES = layer_state_t::eAlphaChanged |
+ layer_state_t::eTransparentRegionChanged | layer_state_t::eShadowRadiusChanged |
+ layer_state_t::eRenderBorderChanged | layer_state_t::eColorChanged |
+ layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
+ layer_state_t::eApiChanged | layer_state_t::eSidebandStreamChanged |
+ layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eBackgroundColorChanged | layer_state_t::eColorSpaceAgnosticChanged |
+ layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged |
+ layer_state_t::eAutoRefreshChanged | layer_state_t::eStretchChanged;
+ // Changes to content or children size.
+ static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::ePositionChanged |
+ layer_state_t::eMatrixChanged | layer_state_t::eTransparentRegionChanged |
+ layer_state_t::eBufferCropChanged | layer_state_t::eBufferTransformChanged |
+ layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
+ layer_state_t::eDestinationFrameChanged;
+
bool hasValidBuffer() const;
void sanitize(int32_t permissions);
@@ -212,6 +237,11 @@
float dsdy{0};
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+ inline bool operator==(const matrix22_t& other) const {
+ return std::tie(dsdx, dtdx, dtdy, dsdy) ==
+ std::tie(other.dsdx, other.dtdx, other.dtdy, other.dsdy);
+ }
+ inline bool operator!=(const matrix22_t& other) const { return !(*this == other); }
};
sp<IBinder> surface;
int32_t layerId;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index cf5a7e7..3685f54 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -238,6 +238,10 @@
return (source & test) == test;
}
+bool isStylusToolType(uint32_t toolType) {
+ return toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || toolType == AMOTION_EVENT_TOOL_TYPE_ERASER;
+}
+
VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
event.getSource(), event.getDisplayId()},
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index 8e9b07b..e76bc20 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -23,6 +23,9 @@
namespace android::recoverymap {
+////////////////////////////////////////////////////////////////////////////////
+// Framework
+
const float kSdrWhiteNits = 100.0f;
struct Color {
@@ -40,36 +43,142 @@
};
};
-/*
- * Convert from OETF'd bt.2100 RGB to YUV, according to BT.2100
- */
-Color bt2100RgbToYuv(Color e);
+inline Color operator+=(Color& lhs, const Color& rhs) {
+ lhs.r += rhs.r;
+ lhs.g += rhs.g;
+ lhs.b += rhs.b;
+ return lhs;
+}
+inline Color operator-=(Color& lhs, const Color& rhs) {
+ lhs.r -= rhs.r;
+ lhs.g -= rhs.g;
+ lhs.b -= rhs.b;
+ return lhs;
+}
+
+inline Color operator+(const Color& lhs, const Color& rhs) {
+ Color temp = lhs;
+ return temp += rhs;
+}
+inline Color operator-(const Color& lhs, const Color& rhs) {
+ Color temp = lhs;
+ return temp -= rhs;
+}
+
+inline Color operator+=(Color& lhs, const float rhs) {
+ lhs.r += rhs;
+ lhs.g += rhs;
+ lhs.b += rhs;
+ return lhs;
+}
+inline Color operator-=(Color& lhs, const float rhs) {
+ lhs.r -= rhs;
+ lhs.g -= rhs;
+ lhs.b -= rhs;
+ return lhs;
+}
+inline Color operator*=(Color& lhs, const float rhs) {
+ lhs.r *= rhs;
+ lhs.g *= rhs;
+ lhs.b *= rhs;
+ return lhs;
+}
+inline Color operator/=(Color& lhs, const float rhs) {
+ lhs.r /= rhs;
+ lhs.g /= rhs;
+ lhs.b /= rhs;
+ return lhs;
+}
+
+inline Color operator+(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp += rhs;
+}
+inline Color operator-(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp -= rhs;
+}
+inline Color operator*(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp *= rhs;
+}
+inline Color operator/(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp /= rhs;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// sRGB transformations
/*
- * Convert srgb YUV to RGB, according to ECMA TR/98.
+ * Calculate the luminance of a linear RGB sRGB pixel, according to IEC 61966-2-1.
*/
-Color srgbYuvToRgb(Color e);
+float srgbLuminance(Color e);
/*
- * TODO: better source for srgb transfer function
- * Convert from srgb to linear, according to https://en.wikipedia.org/wiki/SRGB.
+ * Convert from OETF'd srgb YUV to RGB, according to ECMA TR/98.
+ */
+Color srgbYuvToRgb(Color e_gamma);
+
+/*
+ * Convert from OETF'd srgb RGB to YUV, according to ECMA TR/98.
+ */
+Color srgbRgbToYuv(Color e_gamma);
+
+/*
+ * Convert from srgb to linear, according to IEC 61966-2-1.
+ *
* [0.0, 1.0] range in and out.
*/
-float srgbInvOetf(float e);
-Color srgbInvOetf(Color e);
+float srgbInvOetf(float e_gamma);
+Color srgbInvOetf(Color e_gamma);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Display-P3 transformations
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// BT.2100 transformations - according to ITU-R BT.2100-2
/*
- * Convert from HLG to scene luminance in nits, according to BT.2100.
+ * Calculate the luminance of a linear RGB BT.2100 pixel.
*/
-float hlgInvOetf(float e);
+float bt2100Luminance(Color e);
/*
- * Convert from scene luminance in nits to HLG, according to BT.2100.
+ * Convert from OETF'd BT.2100 RGB to YUV.
*/
-float hlgOetf(float e);
+Color bt2100RgbToYuv(Color e_gamma);
+
+/*
+ * Convert from OETF'd BT.2100 YUV to RGB.
+ */
+Color bt2100YuvToRgb(Color e_gamma);
+
+/*
+ * Convert from scene luminance in nits to HLG.
+ */
Color hlgOetf(Color e);
/*
+ * Convert from HLG to scene luminance in nits.
+ */
+Color hlgInvOetf(Color e_gamma);
+
+////////////////////////////////////////////////////////////////////////////////
+// Color space conversions
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Recovery map calculations
+
+/*
* Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR
* luminances in linear space, and the hdr ratio to encode against.
*/
@@ -87,6 +196,11 @@
Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
/*
+ * Helper for sampling from images.
+ */
+Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
+
+/*
* Sample the recovery value for the map from a given x,y coordinate on a scale
* that is map scale factor larger than the map size.
*/
@@ -98,8 +212,13 @@
*
* Expect narrow-range image data for P010.
*/
-float sampleYuv420Y(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
-float sampleP010Y(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+Color sampleYuv420(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+
+/*
+ * Sample the image Y value at the provided location, with a weighting based on nearby pixels
+ * and the map scale factor. Assumes narrow-range image data for P010.
+ */
+Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index bd16a68..86eb8a8 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -301,31 +301,38 @@
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
- uint16_t yp_hdr_max = 0;
+ float hdr_y_nits_max = 0.0f;
for (size_t y = 0; y < image_height; ++y) {
for (size_t x = 0; x < image_width; ++x) {
- size_t pixel_idx = x + y * image_width;
- uint16_t yp_hdr = reinterpret_cast<uint8_t*>(uncompressed_yuv_420_image->data)[pixel_idx];
- if (yp_hdr > yp_hdr_max) {
- yp_hdr_max = yp_hdr;
+ Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y);
+ Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
+ Color hdr_rgb = hlgInvOetf(hdr_rgb_gamma);
+ float hdr_y_nits = bt2100Luminance(hdr_rgb);
+
+ if (hdr_y_nits > hdr_y_nits_max) {
+ hdr_y_nits_max = hdr_y_nits;
}
}
}
- float y_hdr_max_nits = hlgInvOetf(yp_hdr_max);
- hdr_ratio = y_hdr_max_nits / kSdrWhiteNits;
+ hdr_ratio = hdr_y_nits_max / kSdrWhiteNits;
for (size_t y = 0; y < map_height; ++y) {
for (size_t x = 0; x < map_width; ++x) {
- float yp_sdr = sampleYuv420Y(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y);
- float yp_hdr = sampleP010Y(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
+ Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image,
+ kMapDimensionScaleFactor, x, y);
+ Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
+ Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
+ float sdr_y_nits = srgbLuminance(sdr_rgb);
- float y_sdr_nits = srgbInvOetf(yp_sdr);
- float y_hdr_nits = hlgInvOetf(yp_hdr);
+ Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
+ Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
+ Color hdr_rgb = hlgInvOetf(hdr_rgb_gamma);
+ float hdr_y_nits = bt2100Luminance(hdr_rgb);
size_t pixel_idx = x + y * map_width;
reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
- encodeRecovery(y_sdr_nits, y_hdr_nits, hdr_ratio);
+ encodeRecovery(sdr_y_nits, hdr_y_nits, hdr_ratio);
}
}
@@ -367,7 +374,8 @@
Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio);
Color rgbp_hdr = hlgOetf(rgb_hdr);
- Color ypuv_hdr = bt2100RgbToYuv(rgbp_hdr);
+ // TODO: actually just leave in RGB and convert to RGBA1010102 instead.
+ Color ypuv_hdr = srgbRgbToYuv(rgbp_hdr);
reinterpret_cast<uint16_t*>(dest->data)[pixel_y_idx] = ypuv_hdr.r;
reinterpret_cast<uint16_t*>(dest->data)[pixel_count + pixel_uv_idx] = ypuv_hdr.g;
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 3e110bc..0d3319f 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -20,43 +20,103 @@
namespace android::recoverymap {
-static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f;
-static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f;
+////////////////////////////////////////////////////////////////////////////////
+// sRGB transformations
-Color bt2100RgbToYuv(Color e) {
- float yp = kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b;
- return {{{yp, (e.b - yp) / kBt2100Cb, (e.r - yp) / kBt2100Cr }}};
+static const float kSrgbR = 0.299f, kSrgbG = 0.587f, kSrgbB = 0.114f;
+
+float srgbLuminance(Color e) {
+ return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b;
}
static const float kSrgbRCr = 1.402f, kSrgbGCb = 0.34414f, kSrgbGCr = 0.71414f, kSrgbBCb = 1.772f;
-Color srgbYuvToRgb(Color e) {
- return {{{ e.y + kSrgbRCr * e.v, e.y - kSrgbGCb * e.u - kSrgbGCr * e.v, e.y + kSrgbBCb * e.u }}};
+Color srgbYuvToRgb(Color e_gamma) {
+ return {{{ e_gamma.y + kSrgbRCr * e_gamma.v,
+ e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v,
+ e_gamma.y + kSrgbBCb * e_gamma.u }}};
}
-float srgbInvOetf(float e) {
- if (e <= 0.04045f) {
- return e / 12.92f;
+static const float kSrgbUR = -0.1687f, kSrgbUG = -0.3313f, kSrgbUB = 0.5f;
+static const float kSrgbVR = 0.5f, kSrgbVG = -0.4187f, kSrgbVB = -0.0813f;
+
+Color srgbRgbToYuv(Color e_gamma) {
+ return {{{ kSrgbR * e_gamma.r + kSrgbG * e_gamma.g + kSrgbB * e_gamma.b,
+ kSrgbUR * e_gamma.r + kSrgbUG * e_gamma.g + kSrgbUB * e_gamma.b,
+ kSrgbVR * e_gamma.r + kSrgbVG * e_gamma.g + kSrgbVB * e_gamma.b }}};
+}
+
+float srgbInvOetf(float e_gamma) {
+ if (e_gamma <= 0.04045f) {
+ return e_gamma / 12.92f;
} else {
- return pow((e + 0.055f) / 1.055f, 2.4);
+ return pow((e_gamma + 0.055f) / 1.055f, 2.4);
}
}
-Color srgbInvOetf(Color e) {
- return {{{ srgbInvOetf(e.r), srgbInvOetf(e.g), srgbInvOetf(e.b) }}};
+Color srgbInvOetf(Color e_gamma) {
+ return {{{ srgbInvOetf(e_gamma.r),
+ srgbInvOetf(e_gamma.g),
+ srgbInvOetf(e_gamma.b) }}};
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Display-P3 transformations
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// BT.2100 transformations - according to ITU-R BT.2100-2
+
+static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f;
+
+float bt2100Luminance(Color e) {
+ return kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b;
+}
+
+static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f;
+
+Color bt2100RgbToYuv(Color e_gamma) {
+ float y_gamma = bt2100Luminance(e_gamma);
+ return {{{ y_gamma,
+ (e_gamma.b - y_gamma) / kBt2100Cb,
+ (e_gamma.r - y_gamma) / kBt2100Cr }}};
+}
+
+// Derived from the reverse of bt2100RgbToYuv. The derivation for R and B are
+// pretty straight forward; we just reverse the formulas for U and V above. But
+// deriving the formula for G is a bit more complicated:
+//
+// Start with equation for luminance:
+// Y = kBt2100R * R + kBt2100G * G + kBt2100B * B
+// Solve for G:
+// G = (Y - kBt2100R * R - kBt2100B * B) / kBt2100B
+// Substitute equations for R and B in terms YUV:
+// G = (Y - kBt2100R * (Y + kBt2100Cr * V) - kBt2100B * (Y + kBt2100Cb * U)) / kBt2100B
+// Simplify:
+// G = Y * ((1 - kBt2100R - kBt2100B) / kBt2100G)
+// + U * (kBt2100B * kBt2100Cb / kBt2100G)
+// + V * (kBt2100R * kBt2100Cr / kBt2100G)
+//
+// We then get the following coeficients for calculating G from YUV:
+//
+// Coef for Y = (1 - kBt2100R - kBt2100B) / kBt2100G = 1
+// Coef for U = kBt2100B * kBt2100Cb / kBt2100G = kBt2100GCb = ~0.1645
+// Coef for V = kBt2100R * kBt2100Cr / kBt2100G = kBt2100GCr = ~0.5713
+
+static const float kBt2100GCb = kBt2100B * kBt2100Cb / kBt2100G;
+static const float kBt2100GCr = kBt2100R * kBt2100Cr / kBt2100G;
+
+Color bt2100YuvToRgb(Color e_gamma) {
+ return {{{ e_gamma.y + kBt2100Cr * e_gamma.v,
+ e_gamma.y - kBt2100GCb * e_gamma.u - kBt2100GCr * e_gamma.v,
+ e_gamma.y + kBt2100Cb * e_gamma.u }}};
}
static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073;
-float hlgInvOetf(float e) {
- if (e <= 0.5f) {
- return pow(e, 2.0f) / 3.0f;
- } else {
- return (exp((e - kHlgC) / kHlgA) + kHlgB) / 12.0f;
- }
-}
-
-float hlgOetf(float e) {
+static float hlgOetf(float e) {
if (e <= 1.0f/12.0f) {
return sqrt(3.0f * e);
} else {
@@ -68,7 +128,29 @@
return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}};
}
-uint8_t EncodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) {
+float hlgInvOetf(float e_gamma) {
+ if (e_gamma <= 0.5f) {
+ return pow(e_gamma, 2.0f) / 3.0f;
+ } else {
+ return (exp((e_gamma - kHlgC) / kHlgA) + kHlgB) / 12.0f;
+ }
+}
+
+Color hlgInvOetf(Color e_gamma) {
+ return {{{ hlgInvOetf(e_gamma.r),
+ hlgInvOetf(e_gamma.g),
+ hlgInvOetf(e_gamma.b) }}};
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Color conversions
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Recovery map calculations
+
+uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) {
float gain = 1.0f;
if (y_sdr > 0.0f) {
gain = y_hdr / y_sdr;
@@ -80,8 +162,14 @@
return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f + 127.5f);
}
-float applyRecovery(float y_sdr, float recovery, float hdr_ratio) {
- return exp2(log2(y_sdr) + recovery * log2(hdr_ratio));
+static float applyRecovery(float e, float recovery, float hdr_ratio) {
+ return exp2(log2(e) + recovery * log2(hdr_ratio));
+}
+
+Color applyRecovery(Color e, float recovery, float hdr_ratio) {
+ return {{{ applyRecovery(e.r, recovery, hdr_ratio),
+ applyRecovery(e.g, recovery, hdr_ratio),
+ applyRecovery(e.b, recovery, hdr_ratio) }}};
}
// TODO: do we need something more clever for filtering either the map or images
@@ -131,39 +219,42 @@
(static_cast<float>(v_uint) - 128.0f) / 255.0f }}};
}
-typedef float (*sampleComponentFn)(jr_uncompressed_ptr, size_t, size_t);
+Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
+ size_t pixel_count = image->width * image->height;
-static float sampleComponent(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y,
- sampleComponentFn sample_fn) {
- float e = 0.0f;
+ size_t pixel_y_idx = x + y * image->width;
+ size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2);
+
+ uint16_t y_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_y_idx];
+ uint16_t u_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2];
+ uint16_t v_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2 + 1];
+
+ // Conversions include taking narrow-range into account.
+ return {{{ static_cast<float>(y_uint) / 940.0f,
+ (static_cast<float>(u_uint) - 64.0f) / 940.0f - 0.5f,
+ (static_cast<float>(v_uint) - 64.0f) / 940.0f - 0.5f }}};
+}
+
+typedef Color (*getPixelFn)(jr_uncompressed_ptr, size_t, size_t);
+
+static Color samplePixels(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y,
+ getPixelFn get_pixel_fn) {
+ Color e = {{{ 0.0f, 0.0f, 0.0f }}};
for (size_t dy = 0; dy < map_scale_factor; ++dy) {
for (size_t dx = 0; dx < map_scale_factor; ++dx) {
- e += sample_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy);
+ e += get_pixel_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy);
}
}
return e / static_cast<float>(map_scale_factor * map_scale_factor);
}
-static float getYuv420Y(jr_uncompressed_ptr image, size_t x, size_t y) {
- size_t pixel_idx = x + y * image->width;
- uint8_t y_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_idx];
- return static_cast<float>(y_uint) / 255.0f;
+Color sampleYuv420(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
+ return samplePixels(image, map_scale_factor, x, y, getYuv420Pixel);
}
-
-float sampleYuv420Y(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
- return sampleComponent(image, map_scale_factor, x, y, getYuv420Y);
+Color sampleP010(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
+ return samplePixels(image, map_scale_factor, x, y, getP010Pixel);
}
-static float getP010Y(jr_uncompressed_ptr image, size_t x, size_t y) {
- size_t pixel_idx = x + y * image->width;
- uint8_t y_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_idx];
- // Expecting narrow range input
- return (static_cast<float>(y_uint) - 64.0f) / 960.0f;
-}
-
-float sampleP010Y(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
- return sampleComponent(image, map_scale_factor, x, y, getP010Y);
-}
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
index 41af991..8f37954 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -27,7 +27,15 @@
srcs: [
"recoverymap_test.cpp",
],
+ shared_libs: [
+ "libimage_io",
+ "libjpeg",
+ "liblog",
+ ],
static_libs: [
+ "libgtest",
+ "libjpegdecoder",
+ "libjpegencoder",
"libjpegrecoverymap",
],
}
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
index c436138..226056b 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -14,9 +14,33 @@
* limitations under the License.
*/
+#include <gtest/gtest.h>
#include <jpegrecoverymap/recoverymap.h>
-namespace android {
+namespace android::recoverymap {
-// Add new tests here.
-} // namespace android
+class RecoveryMapTest : public testing::Test {
+public:
+ RecoveryMapTest();
+ ~RecoveryMapTest();
+protected:
+ virtual void SetUp();
+ virtual void TearDown();
+};
+
+RecoveryMapTest::RecoveryMapTest() {}
+RecoveryMapTest::~RecoveryMapTest() {}
+
+void RecoveryMapTest::SetUp() {}
+void RecoveryMapTest::TearDown() {}
+
+TEST_F(RecoveryMapTest, build) {
+ // Force all of the recovery map lib to be linked by calling all public functions.
+ RecoveryMap recovery_map;
+ recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, 0, nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, nullptr);
+ recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false);
+}
+
+} // namespace android::recoverymap
diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
index beec2e1..ddd5146 100644
--- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp
+++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
@@ -25,8 +25,7 @@
for (size_t i = 0; i < args.pointerCount; i++) {
// Make sure we are canceling stylus pointers
const int32_t toolType = args.pointerProperties[i].toolType;
- if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
- toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+ if (isStylusToolType(toolType)) {
hasStylus = true;
}
if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 4a0f2ec..b3b4fb1 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -41,6 +41,7 @@
"include-filter": "android.view.cts.input",
"include-filter": "android.view.cts.MotionEventTest",
"include-filter": "android.view.cts.PointerCaptureTest",
+ "include-filter": "android.view.cts.TooltipTest",
"include-filter": "android.view.cts.VerifyInputEventTest"
}
]
@@ -128,6 +129,7 @@
{
"include-filter": "android.view.cts.MotionEventTest",
"include-filter": "android.view.cts.PointerCaptureTest",
+ "include-filter": "android.view.cts.TooltipTest",
"include-filter": "android.view.cts.VerifyInputEventTest"
}
]
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index ec41025..c170b81 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -99,14 +99,17 @@
}
static int getLinuxToolCode(int toolType) {
- if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
- return BTN_TOOL_PEN;
+ switch (toolType) {
+ case AMOTION_EVENT_TOOL_TYPE_STYLUS:
+ return BTN_TOOL_PEN;
+ case AMOTION_EVENT_TOOL_TYPE_ERASER:
+ return BTN_TOOL_RUBBER;
+ case AMOTION_EVENT_TOOL_TYPE_FINGER:
+ return BTN_TOOL_FINGER;
+ default:
+ ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType);
+ return BTN_TOOL_FINGER;
}
- if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
- return BTN_TOOL_FINGER;
- }
- ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType);
- return BTN_TOOL_FINGER;
}
static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
@@ -195,7 +198,7 @@
static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) {
std::set<int32_t> stylusPointerIds;
for (uint32_t i = 0; i < args.pointerCount; i++) {
- if (args.pointerProperties[i].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+ if (isStylusToolType(args.pointerProperties[i].toolType)) {
stylusPointerIds.insert(args.pointerProperties[i].id);
}
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 7b7c42a..eb938c8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -481,8 +481,7 @@
bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) {
return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) &&
- (entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
- entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER);
+ isStylusToolType(entry.pointerProperties[pointerIndex].toolType);
}
// Determines if the given window can be targeted as InputTarget::Flags::FOREGROUND.
@@ -1770,15 +1769,16 @@
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%" PRId32
", policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, "
"metaState=0x%x, buttonState=0x%x,"
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
- prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
- entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
- entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
- entry.xPrecision, entry.yPrecision, entry.downTime);
+ prefix, entry.eventTime, entry.deviceId,
+ inputEventSourceToString(entry.source).c_str(), entry.displayId, entry.policyFlags,
+ MotionEvent::actionToString(entry.action).c_str(), entry.actionButton, entry.flags,
+ entry.metaState, entry.buttonState, entry.edgeFlags, entry.xPrecision,
+ entry.yPrecision, entry.downTime);
for (uint32_t i = 0; i < entry.pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%d, "
@@ -2503,6 +2503,10 @@
}
}
+ if (tempTouchState.windows.empty()) {
+ mTouchStatesByDisplay.erase(displayId);
+ }
+
// Update hover state.
mLastHoverWindowHandle = newHoverWindowHandle;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index ee7da93..114e0bf 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -148,7 +148,8 @@
std::string TouchState::dump() const {
std::string out;
- out += StringPrintf("deviceId=%d, source=0x%08x\n", deviceId, source);
+ out += StringPrintf("deviceId=%d, source=%s\n", deviceId,
+ inputEventSourceToString(source).c_str());
if (!windows.empty()) {
out += " Windows:\n";
for (size_t i = 0; i < windows.size(); i++) {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index f8b1b3f..f04a646 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -66,8 +66,7 @@
return false;
}
const auto actionIndex = MotionEvent::getActionIndex(motionArgs.action);
- return motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
- motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER;
+ return isStylusToolType(motionArgs.pointerProperties[actionIndex].toolType);
}
// --- InputReader ---
diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h
index 8d14d3c..ff15e0c 100644
--- a/services/inputflinger/reader/include/StylusState.h
+++ b/services/inputflinger/reader/include/StylusState.h
@@ -24,27 +24,19 @@
struct StylusState {
/* Time the stylus event was received. */
- nsecs_t when;
- /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */
- float pressure;
+ nsecs_t when{};
+ /*
+ * Pressure as reported by the stylus if supported, normalized to the range [0, 1.0].
+ * The presence of a pressure value indicates that the stylus is able to tell whether it is
+ * touching the display.
+ */
+ std::optional<float> pressure{};
/* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */
- uint32_t buttons;
+ uint32_t buttons{};
/* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */
- int32_t toolType;
+ int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN};
- void copyFrom(const StylusState& other) {
- when = other.when;
- pressure = other.pressure;
- buttons = other.buttons;
- toolType = other.toolType;
- }
-
- void clear() {
- when = LLONG_MAX;
- pressure = 0.f;
- buttons = 0;
- toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
- }
+ void clear() { *this = StylusState{}; }
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 5657d61..a1a2af9 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -300,10 +300,11 @@
mCursorScrollAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- const nsecs_t eventTime =
+ const auto [eventTime, readTime] =
applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(),
- rawEvent->when, mLastEventTime);
- out += sync(eventTime, rawEvent->readTime);
+ rawEvent->when, rawEvent->readTime,
+ mLastEventTime);
+ out += sync(eventTime, readTime);
mLastEventTime = eventTime;
}
return out;
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 56fc5fa..2809939 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -32,8 +32,10 @@
void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f,
- 0.0f);
+ if (mRawPressureAxis.valid) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f);
+ }
}
void ExternalStylusInputMapper::dump(std::string& dump) {
@@ -79,13 +81,12 @@
mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
}
- int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
if (mRawPressureAxis.valid) {
- mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue;
- } else if (mTouchButtonAccumulator.isToolActive()) {
- mStylusState.pressure = 1.0f;
- } else {
- mStylusState.pressure = 0.0f;
+ auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure());
+ mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) /
+ static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue);
+ } else if (mTouchButtonAccumulator.hasButtonTouch()) {
+ mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f;
}
mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 844afe0..8e3539c 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -19,6 +19,7 @@
#include "InputMapper.h"
#include "InputDevice.h"
+#include "input/PrintTools.h"
namespace android {
@@ -129,7 +130,7 @@
void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) {
dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when);
- dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure);
+ dump += StringPrintf(INDENT4 "Pressure: %s\n", toString(state.pressure).c_str());
dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons);
dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType);
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 8e757a5..b193dff 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -119,6 +119,18 @@
if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
}
+ } else if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS && !mStylusMtToolSeen) {
+ mStylusMtToolSeen = true;
+ // The multi-touch device produced a stylus event with MT_TOOL_PEN. Dynamically
+ // re-configure this input device so that we add SOURCE_STYLUS if we haven't already.
+ // This is to cover the case where we cannot reliably detect whether a multi-touch
+ // device will ever produce stylus events when it is initially being configured.
+ if (!isFromSource(mSource, AINPUT_SOURCE_STYLUS)) {
+ // Add the stylus source immediately so that it is included in any events generated
+ // before we have a chance to re-configure the device.
+ mSource |= AINPUT_SOURCE_STYLUS;
+ bumpGeneration();
+ }
}
if (shouldSimulateStylusWithTouch() &&
outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
@@ -200,7 +212,7 @@
}
bool MultiTouchInputMapper::hasStylus() const {
- return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() ||
+ return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() ||
shouldSimulateStylusWithTouch();
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index ddf9e80..5f8bccf 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -50,6 +50,8 @@
// Specifies the pointer id bits that are in use, and their associated tracking id.
BitSet32 mPointerIdBits;
int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
+
+ bool mStylusMtToolSeen{false};
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 0b7ff84..d8a4d34 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -110,10 +110,11 @@
// coordinates result in extremely large instantaneous velocities, which can negatively impact
// user experience. To avoid this, we augment the timestamps so that subsequent event timestamps
// differ by at least a minimum delta value.
-static nsecs_t applyBluetoothTimestampSmoothening(const InputDeviceIdentifier& identifier,
- nsecs_t currentEventTime, nsecs_t lastEventTime) {
+static std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+ const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
+ nsecs_t lastEventTime) {
if (identifier.bus != BUS_BLUETOOTH) {
- return currentEventTime;
+ return {currentEventTime, readTime};
}
// Assume the fastest rate at which a Bluetooth touch device can report input events is one
@@ -123,8 +124,14 @@
// We define a maximum smoothing time delta so that we don't generate events too far into the
// future.
constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
- return std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
- currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
+ const nsecs_t smoothenedEventTime =
+ std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
+ currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
+ // If we are modifying the event time, treat this event as a synthetically generated event for
+ // latency tracking purposes and use the event time as the read time (zero read latency).
+ const nsecs_t smoothenedReadTime =
+ smoothenedEventTime != currentEventTime ? currentEventTime : readTime;
+ return {smoothenedEventTime, smoothenedReadTime};
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index bf73ce5..5be694f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -21,6 +21,7 @@
#include "TouchInputMapper.h"
#include <ftl/enum.h>
+#include <input/PrintTools.h>
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
@@ -31,13 +32,6 @@
// --- Constants ---
-// Maximum amount of latency to add to touch events while waiting for data from an
-// external stylus.
-static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
-
-// Maximum amount of time to wait on touch data before pushing out new pressure data.
-static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
-
// Artificial latency on synthetic events created from stylus data without corresponding touch
// data.
static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
@@ -258,9 +252,12 @@
dump += INDENT3 "Stylus Fusion:\n";
dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n",
toString(mExternalStylusConnected));
- dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId);
+ dump += StringPrintf(INDENT4 "Fused External Stylus Pointer ID: %s\n",
+ toString(mFusedStylusPointerId).c_str());
dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
mExternalStylusFusionTimeout);
+ dump += StringPrintf(INDENT4 " External Stylus Buttons Applied: 0x%08x",
+ mExternalStylusButtonsApplied);
dump += INDENT3 "External Stylus State:\n";
dumpStylusState(dump, mExternalStylusState);
@@ -1417,9 +1414,10 @@
void TouchInputMapper::resetExternalStylus() {
mExternalStylusState.clear();
- mExternalStylusId = -1;
+ mFusedStylusPointerId.reset();
mExternalStylusFusionTimeout = LLONG_MAX;
mExternalStylusDataPending = false;
+ mExternalStylusButtonsApplied = 0;
}
void TouchInputMapper::clearStylusDataPendingFlags() {
@@ -1469,8 +1467,9 @@
const RawState& last =
mRawStatesPending.size() == 1 ? mCurrentRawState : mRawStatesPending.rbegin()[1];
- next.when = applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), when,
- last.when);
+ std::tie(next.when, next.readTime) =
+ applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), when,
+ readTime, last.when);
// Assign pointer ids.
if (!mHavePointerIds) {
@@ -1600,8 +1599,7 @@
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
- if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
- pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+ if (isStylusToolType(pointer.toolType)) {
mCurrentCookedState.stylusIdBits.markBit(id);
} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER ||
pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
@@ -1614,8 +1612,7 @@
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
- if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
- pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+ if (isStylusToolType(pointer.toolType)) {
mCurrentCookedState.stylusIdBits.markBit(id);
}
}
@@ -1693,29 +1690,41 @@
}
void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
- if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
- mCurrentRawState.buttonState |= mExternalStylusState.buttons;
+ if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus()) {
+ // If any of the external buttons are already pressed by the touch device, ignore them.
+ const int32_t pressedButtons = ~mCurrentRawState.buttonState & mExternalStylusState.buttons;
+ const int32_t releasedButtons =
+ mExternalStylusButtonsApplied & ~mExternalStylusState.buttons;
+
+ mCurrentRawState.buttonState |= pressedButtons;
+ mCurrentRawState.buttonState &= ~releasedButtons;
+
+ mExternalStylusButtonsApplied |= pressedButtons;
+ mExternalStylusButtonsApplied &= ~releasedButtons;
}
}
void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) {
CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData;
const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData;
+ if (!mFusedStylusPointerId || !currentPointerData.isTouching(*mFusedStylusPointerId)) {
+ return;
+ }
- if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) {
- float pressure = mExternalStylusState.pressure;
- if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) {
- const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId);
- pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
- }
- PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId);
- coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ float pressure = lastPointerData.isTouching(*mFusedStylusPointerId)
+ ? lastPointerData.pointerCoordsForId(*mFusedStylusPointerId)
+ .getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)
+ : 0.f;
+ if (mExternalStylusState.pressure && *mExternalStylusState.pressure > 0.f) {
+ pressure = *mExternalStylusState.pressure;
+ }
+ PointerCoords& coords = currentPointerData.editPointerCoordsWithId(*mFusedStylusPointerId);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
PointerProperties& properties =
- currentPointerData.editPointerPropertiesWithId(mExternalStylusId);
- if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- properties.toolType = mExternalStylusState.toolType;
- }
+ currentPointerData.editPointerPropertiesWithId(*mFusedStylusPointerId);
+ properties.toolType = mExternalStylusState.toolType;
}
}
@@ -1724,34 +1733,48 @@
return false;
}
+ // Check if the stylus pointer has gone up.
+ if (mFusedStylusPointerId &&
+ !state.rawPointerData.touchingIdBits.hasBit(*mFusedStylusPointerId)) {
+ ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up");
+ mFusedStylusPointerId.reset();
+ return false;
+ }
+
const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&
state.rawPointerData.pointerCount != 0;
- if (initialDown) {
- if (mExternalStylusState.pressure != 0.0f) {
- ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion");
- mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit();
- } else if (timeout) {
- ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus.");
- resetExternalStylus();
- } else {
- if (mExternalStylusFusionTimeout == LLONG_MAX) {
- mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
- }
- ALOGD_IF(DEBUG_STYLUS_FUSION,
- "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)",
- mExternalStylusFusionTimeout);
- getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
- return true;
- }
+ if (!initialDown) {
+ return false;
}
- // Check if the stylus pointer has gone up.
- if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) {
- ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up");
- mExternalStylusId = -1;
+ if (!mExternalStylusState.pressure) {
+ ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus does not support pressure, no pointer fusion needed");
+ return false;
}
- return false;
+ if (*mExternalStylusState.pressure != 0.0f) {
+ ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion");
+ mFusedStylusPointerId = state.rawPointerData.touchingIdBits.firstMarkedBit();
+ return false;
+ }
+
+ if (timeout) {
+ ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus.");
+ mFusedStylusPointerId.reset();
+ mExternalStylusFusionTimeout = LLONG_MAX;
+ return false;
+ }
+
+ // We are waiting for the external stylus to report a pressure value. Withhold touches from
+ // being processed until we either get pressure data or timeout.
+ if (mExternalStylusFusionTimeout == LLONG_MAX) {
+ mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
+ }
+ ALOGD_IF(DEBUG_STYLUS_FUSION,
+ "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)",
+ mExternalStylusFusionTimeout);
+ getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+ return true;
}
std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) {
@@ -1763,7 +1786,7 @@
out += dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
}
} else if (mDeviceMode == DeviceMode::DIRECT) {
- if (mExternalStylusFusionTimeout < when) {
+ if (mExternalStylusFusionTimeout <= when) {
out += processRawTouches(true /*timeout*/);
} else if (mExternalStylusFusionTimeout != LLONG_MAX) {
getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
@@ -1774,11 +1797,14 @@
std::list<NotifyArgs> TouchInputMapper::updateExternalStylusState(const StylusState& state) {
std::list<NotifyArgs> out;
- mExternalStylusState.copyFrom(state);
- if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) {
- // We're either in the middle of a fused stream of data or we're waiting on data before
- // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus
- // data.
+ const bool buttonsChanged = mExternalStylusState.buttons != state.buttons;
+ mExternalStylusState = state;
+ if (mFusedStylusPointerId || mExternalStylusFusionTimeout != LLONG_MAX || buttonsChanged) {
+ // The following three cases are handled here:
+ // - We're in the middle of a fused stream of data;
+ // - We're waiting on external stylus data before dispatching the initial down; or
+ // - Only the button state, which is not reported through a specific pointer, has changed.
+ // Go ahead and dispatch now that we have fresh stylus data.
mExternalStylusDataPending = true;
out += processRawTouches(false /*timeout*/);
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index c20f28b..788ec58 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -27,6 +27,13 @@
namespace android {
+// Maximum amount of latency to add to touch events while waiting for data from an
+// external stylus.
+static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
+
+// Maximum amount of time to wait on touch data before pushing out new pressure data.
+static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
+
/* Raw axis information from the driver. */
struct RawPointerAxes {
RawAbsoluteAxisInfo x{};
@@ -355,9 +362,14 @@
// State provided by an external stylus
StylusState mExternalStylusState;
- int64_t mExternalStylusId;
+ // If an external stylus is capable of reporting pointer-specific data like pressure, we will
+ // attempt to fuse the pointer data reported by the stylus to the first touch pointer. This is
+ // the id of the pointer to which the external stylus data is fused.
+ std::optional<uint32_t> mFusedStylusPointerId;
nsecs_t mExternalStylusFusionTimeout;
bool mExternalStylusDataPending;
+ // A subset of the buttons in mCurrentRawState that came from an external stylus.
+ int32_t mExternalStylusButtonsApplied;
// True if we sent a HOVER_ENTER event.
bool mSentHoverEnter;
@@ -783,6 +795,8 @@
[[nodiscard]] std::list<NotifyArgs> abortPointerSimple(nsecs_t when, nsecs_t readTime,
uint32_t policyFlags);
+ // Attempts to assign a pointer id to the external stylus. Returns true if the state should be
+ // withheld from further processing while waiting for data from the stylus.
bool assignExternalStylusId(const RawState& state, bool timeout);
void applyExternalStylusButtonState(nsecs_t when);
void applyExternalStylusTouchState(nsecs_t when);
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index b0cef67..8746729 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -24,12 +24,11 @@
// --- MultiTouchMotionAccumulator ---
MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
- : mCurrentSlot(-1), mUsingSlotsProtocol(false), mHaveStylus(false) {}
+ : mCurrentSlot(-1), mUsingSlotsProtocol(false) {}
void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
bool usingSlotsProtocol) {
mUsingSlotsProtocol = usingSlotsProtocol;
- mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
mSlots = std::vector<Slot>(slotCount);
mCurrentSlot = -1;
@@ -146,10 +145,6 @@
}
}
-bool MultiTouchMotionAccumulator::hasStylus() const {
- return mHaveStylus;
-}
-
void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
if (!slot.mInUse) {
ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index 625a00f..62bc780 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -75,7 +75,6 @@
void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
void process(const RawEvent* rawEvent);
void finishSync();
- bool hasStylus() const;
inline size_t getSlotCount() const { return mSlots.size(); }
inline const Slot& getSlot(size_t index) const {
@@ -87,7 +86,6 @@
int32_t mCurrentSlot;
std::vector<Slot> mSlots;
bool mUsingSlotsProtocol;
- bool mHaveStylus;
void resetSlots();
void warnIfNotInUse(const RawEvent& event, const Slot& slot);
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index 1891205..bc23a8e 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -167,4 +167,8 @@
return mHaveStylus;
}
+bool TouchButtonAccumulator::hasButtonTouch() const {
+ return mHaveBtnTouch;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index 65b0a62..c2de23c 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -40,6 +40,7 @@
bool isToolActive() const;
bool isHovering() const;
bool hasStylus() const;
+ bool hasButtonTouch() const;
private:
bool mHaveBtnTouch{};
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index aaf50ce..689fe5c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -22,6 +22,7 @@
#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
#include <fcntl.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/Input.h>
#include <linux/input.h>
@@ -43,6 +44,7 @@
namespace android::inputdispatcher {
using namespace ftl::flag_operators;
+using testing::AllOf;
// An arbitrary time value.
static constexpr nsecs_t ARBITRARY_TIME = 1234;
@@ -102,6 +104,28 @@
<< MotionEvent::actionToString(receivedAction);
}
+MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
+ bool matches = action == arg.getAction();
+ if (!matches) {
+ *result_listener << "expected action " << MotionEvent::actionToString(action)
+ << ", but got " << MotionEvent::actionToString(arg.getAction());
+ }
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ if (!matches) {
+ *result_listener << "; ";
+ }
+ *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
+ matches &= (arg.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+}
+
+MATCHER_P(WithSource, source, "InputEvent with specified source") {
+ *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
+ << inputEventSourceToString(arg.getSource());
+ return arg.getSource() == source;
+}
+
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -1205,6 +1229,12 @@
mInputReceiver->consumeCaptureEvent(hasCapture);
}
+ void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
+ MotionEvent* motionEvent = consumeMotion();
+ ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event";
+ ASSERT_THAT(*motionEvent, matcher);
+ }
+
void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
std::optional<int32_t> expectedDisplayId,
std::optional<int32_t> expectedFlags) {
@@ -2207,6 +2237,48 @@
ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
}
+/**
+ * Inject a mouse hover event followed by a tap from touchscreen.
+ * In the current implementation, the tap does not cause a HOVER_EXIT event.
+ */
+TEST_F(InputDispatcherTest, MouseHoverAndTouchTap) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Inject a hover_move from mouse.
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE,
+ ADISPLAY_ID_DEFAULT, {{50, 50}});
+ motionArgs.xCursorPosition = 50;
+ motionArgs.yCursorPosition = 50;
+ mDispatcher->notifyMotion(&motionArgs);
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE))));
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE))));
+
+ // Tap on the window
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {{10, 10}});
+ mDispatcher->notifyMotion(&motionArgs);
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {{10, 10}});
+ mDispatcher->notifyMotion(&motionArgs);
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
+}
+
TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 879d36e..6b6d0bb 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1349,6 +1349,8 @@
int32_t mGlobalMetaState;
bool mUpdateGlobalMetaStateWasCalled;
int32_t mGeneration;
+ std::optional<nsecs_t> mRequestedTimeout;
+ std::vector<InputDeviceInfo> mExternalStylusDevices;
public:
FakeInputReaderContext(InputReader* reader)
@@ -1382,6 +1384,29 @@
mGeneration = ContextImpl::bumpGeneration();
return mGeneration;
}
+
+ void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
+
+ void assertTimeoutWasRequested(nsecs_t when) {
+ ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
+ << " but there was no timeout requested.";
+ ASSERT_EQ(when, *mRequestedTimeout);
+ mRequestedTimeout.reset();
+ }
+
+ void assertTimeoutWasNotRequested() {
+ ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
+ " but one was requested at time "
+ << *mRequestedTimeout;
+ }
+
+ void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
+ outDevices = mExternalStylusDevices;
+ }
+
+ void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
+ mExternalStylusDevices = devices;
+ }
} mFakeContext;
friend class InputReaderTest;
@@ -2504,7 +2529,8 @@
ASSERT_EQ(BTN_GEAR_UP, keyArgs.scanCode);
}
-// --- TouchProcessTest ---
+// --- TouchIntegrationTest ---
+
class TouchIntegrationTest : public InputReaderIntegrationTest {
protected:
const std::string UNIQUE_ID = "local:0";
@@ -2551,6 +2577,14 @@
InputDeviceInfo mDeviceInfo;
};
+TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) {
+ // The UinputTouchScreen is an MT device that supports MT_TOOL_TYPE and also supports stylus
+ // buttons. It should show up as a touchscreen, stylus, and keyboard (for reporting button
+ // presses).
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD,
+ mDeviceInfo.getSources());
+}
+
TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -2817,108 +2851,412 @@
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
}
-TEST_F(TouchIntegrationTest, StylusButtonsGenerateKeyEvents) {
- mDevice->sendKey(BTN_STYLUS, 1);
- mDevice->sendSync();
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
- AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
- WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+// --- StylusButtonIntegrationTest ---
- mDevice->sendKey(BTN_STYLUS, 0);
- mDevice->sendSync();
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+// Verify the behavior of button presses reported by various kinds of styluses, including buttons
+// reported by the touchscreen's device, by a fused external stylus, and by an un-fused external
+// stylus.
+template <typename UinputStylusDevice>
+class StylusButtonIntegrationTest : public TouchIntegrationTest {
+protected:
+ void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP();
+#endif
+ TouchIntegrationTest::SetUp();
+ mTouchscreen = mDevice.get();
+ mTouchscreenInfo = mDeviceInfo;
+
+ setUpStylusDevice();
+ }
+
+ UinputStylusDevice* mStylus{nullptr};
+ InputDeviceInfo mStylusInfo{};
+
+ UinputTouchScreen* mTouchscreen{nullptr};
+ InputDeviceInfo mTouchscreenInfo{};
+
+private:
+ // When we are attempting to test stylus button events that are sent from the touchscreen,
+ // use the same Uinput device for the touchscreen and the stylus.
+ template <typename T = UinputStylusDevice>
+ std::enable_if_t<std::is_same_v<UinputTouchScreen, T>, void> setUpStylusDevice() {
+ mStylus = mDevice.get();
+ mStylusInfo = mDeviceInfo;
+ }
+
+ // When we are attempting to stylus buttons from an external stylus being merged with touches
+ // from a touchscreen, create a new Uinput device through which stylus buttons can be injected.
+ template <typename T = UinputStylusDevice>
+ std::enable_if_t<!std::is_same_v<UinputTouchScreen, T>, void> setUpStylusDevice() {
+ mStylusDeviceLifecycleTracker = createUinputDevice<T>();
+ mStylus = mStylusDeviceLifecycleTracker.get();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto info = findDeviceByName(mStylus->getName());
+ ASSERT_TRUE(info);
+ mStylusInfo = *info;
+ }
+
+ std::unique_ptr<UinputStylusDevice> mStylusDeviceLifecycleTracker{};
+
+ // Hide the base class's device to expose it with a different name for readability.
+ using TouchIntegrationTest::mDevice;
+ using TouchIntegrationTest::mDeviceInfo;
+};
+
+using StylusButtonIntegrationTestTypes =
+ ::testing::Types<UinputTouchScreen, UinputExternalStylus, UinputExternalStylusWithPressure>;
+TYPED_TEST_SUITE(StylusButtonIntegrationTest, StylusButtonIntegrationTestTypes);
+
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsGenerateKeyEvents) {
+ const auto stylusId = TestFixture::mStylusInfo.getId();
+
+ TestFixture::mStylus->pressKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+
+ TestFixture::mStylus->releaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
- WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TEST_F(TouchIntegrationTest, StylusButtonsSurroundingTouchGesture) {
- const Point centerPoint = mDevice->getCenterPoint();
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingTouchGesture) {
+ const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
+ const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
+ const auto stylusId = TestFixture::mStylusInfo.getId();
// Press the stylus button.
- mDevice->sendKey(BTN_STYLUS, 1);
- mDevice->sendSync();
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ TestFixture::mStylus->pressKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
- WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
// Start and finish a stylus gesture.
- mDevice->sendSlot(FIRST_SLOT);
- mDevice->sendTrackingId(FIRST_TRACKING_ID);
- mDevice->sendToolType(MT_TOOL_PEN);
- mDevice->sendDown(centerPoint);
- mDevice->sendSync();
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ TestFixture::mTouchscreen->sendSlot(FIRST_SLOT);
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendDown(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
- WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY),
+ WithDeviceId(touchscreenId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
- WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY),
+ WithDeviceId(touchscreenId))));
- mDevice->sendTrackingId(INVALID_TRACKING_ID);
- mDevice->sendSync();
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0))));
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0))));
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
// Release the stylus button.
- mDevice->sendKey(BTN_STYLUS, 0);
- mDevice->sendSync();
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ TestFixture::mStylus->releaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
- WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TEST_F(TouchIntegrationTest, StylusButtonsWithinTouchGesture) {
- const Point centerPoint = mDevice->getCenterPoint();
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingHoveringTouchGesture) {
+ const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
+ const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
+ const auto stylusId = TestFixture::mStylusInfo.getId();
+ auto toolTypeDevice =
+ AllOf(WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithDeviceId(touchscreenId));
+
+ // Press the stylus button.
+ TestFixture::mStylus->pressKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+
+ // Start hovering with the stylus.
+ TestFixture::mTouchscreen->sendSlot(FIRST_SLOT);
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendMove(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ // Touch down with the stylus.
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendDown(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ // Stop touching with the stylus, and start hovering.
+ TestFixture::mTouchscreen->sendUp();
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendMove(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ // Stop hovering.
+ TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0))));
+ // TODO(b/257971675): Fix inconsistent button state when exiting hover.
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ // Release the stylus button.
+ TestFixture::mStylus->releaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+}
+
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsWithinTouchGesture) {
+ const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
+ const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
+ const auto stylusId = TestFixture::mStylusInfo.getId();
// Start a stylus gesture.
+ TestFixture::mTouchscreen->sendSlot(FIRST_SLOT);
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendDown(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ // Press and release a stylus button. Each change in button state also generates a MOVE event.
+ TestFixture::mStylus->pressKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY),
+ WithDeviceId(touchscreenId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY),
+ WithDeviceId(touchscreenId))));
+
+ TestFixture::mStylus->releaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ // Finish the stylus gesture.
+ TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+}
+
+// --- ExternalStylusIntegrationTest ---
+
+// Verify the behavior of an external stylus. An external stylus can report pressure or button
+// data independently of the touchscreen, which is then sent as a MotionEvent as part of an
+// ongoing stylus gesture that is being emitted by the touchscreen.
+using ExternalStylusIntegrationTest = TouchIntegrationTest;
+
+TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Create an external stylus capable of reporting pressure data that
+ // should be fused with a touch pointer.
+ std::unique_ptr<UinputExternalStylusWithPressure> stylus =
+ createUinputDevice<UinputExternalStylusWithPressure>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto stylusInfo = findDeviceByName(stylus->getName());
+ ASSERT_TRUE(stylusInfo);
+
+ ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
+
+ const auto touchscreenId = mDeviceInfo.getId();
+
+ // Set a pressure value on the stylus. It doesn't generate any events.
+ const auto& RAW_PRESSURE_MAX = UinputExternalStylusWithPressure::RAW_PRESSURE_MAX;
+ stylus->setPressure(100);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+
+ // Start a finger gesture, and ensure it shows up as stylus gesture
+ // with the pressure set by the external stylus.
mDevice->sendSlot(FIRST_SLOT);
mDevice->sendTrackingId(FIRST_TRACKING_ID);
- mDevice->sendToolType(MT_TOOL_PEN);
+ mDevice->sendToolType(MT_TOOL_FINGER);
mDevice->sendDown(centerPoint);
mDevice->sendSync();
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0))));
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId), WithPressure(100.f / RAW_PRESSURE_MAX))));
- // Press and release a stylus button. Each change in button state also generates a MOVE event.
- mDevice->sendKey(BTN_STYLUS, 1);
- mDevice->sendSync();
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
- AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
- WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+ // Change the pressure on the external stylus, and ensure the touchscreen generates a MOVE
+ // event with the updated pressure.
+ stylus->setPressure(200);
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
- WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
- WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId), WithPressure(200.f / RAW_PRESSURE_MAX))));
- mDevice->sendKey(BTN_STYLUS, 0);
+ // The external stylus did not generate any events.
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
+}
+
+TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Create an external stylus capable of reporting pressure data that
+ // should be fused with a touch pointer.
+ std::unique_ptr<UinputExternalStylusWithPressure> stylus =
+ createUinputDevice<UinputExternalStylusWithPressure>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto stylusInfo = findDeviceByName(stylus->getName());
+ ASSERT_TRUE(stylusInfo);
+
+ ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
+
+ const auto touchscreenId = mDeviceInfo.getId();
+
+ // Set a pressure value of 0 on the stylus. It doesn't generate any events.
+ const auto& RAW_PRESSURE_MAX = UinputExternalStylusWithPressure::RAW_PRESSURE_MAX;
+ // Send a non-zero value first to prevent the kernel from consuming the zero event.
+ stylus->setPressure(100);
+ stylus->setPressure(0);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+
+ // Start a finger gesture. The touch device will withhold generating any touches for
+ // up to 72 milliseconds while waiting for pressure data from the external stylus.
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_FINGER);
+ mDevice->sendDown(centerPoint);
+ auto waitUntil = std::chrono::system_clock::now() +
+ std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT));
mDevice->sendSync();
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
- AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
- WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0))));
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0))));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled(waitUntil));
- // Finish the stylus gesture.
+ // Since the external stylus did not report a pressure value within the timeout,
+ // it shows up as a finger pointer.
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDeviceId(touchscreenId),
+ WithPressure(1.f))));
+
+ // Change the pressure on the external stylus. Since the pressure was not present at the start
+ // of the gesture, it is ignored for now.
+ stylus->setPressure(200);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+
+ // Finish the finger gesture.
mDevice->sendTrackingId(INVALID_TRACKING_ID);
mDevice->sendSync();
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0))));
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
+
+ // Start a new gesture. Since we have a valid pressure value, it shows up as a stylus.
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_FINGER);
+ mDevice->sendDown(centerPoint);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId), WithPressure(200.f / RAW_PRESSURE_MAX))));
+
+ // The external stylus did not generate any events.
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
+}
+
+TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Create an external stylus device that does not support pressure. It should not affect any
+ // touch pointers.
+ std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto stylusInfo = findDeviceByName(stylus->getName());
+ ASSERT_TRUE(stylusInfo);
+
+ ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
+
+ const auto touchscreenId = mDeviceInfo.getId();
+
+ // Start a finger gesture and ensure a finger pointer is generated for it, without waiting for
+ // pressure data from the external stylus.
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_FINGER);
+ mDevice->sendDown(centerPoint);
+ auto waitUntil = std::chrono::system_clock::now() +
+ std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT));
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(
+ mTestListener
+ ->assertNotifyMotionWasCalled(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(
+ AMOTION_EVENT_TOOL_TYPE_FINGER),
+ WithButtonState(0),
+ WithDeviceId(touchscreenId),
+ WithPressure(1.f)),
+ waitUntil));
+
+ // The external stylus did not generate any events.
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
}
// --- InputDeviceTest ---
@@ -3372,6 +3710,16 @@
mReader->loopOnce();
}
+ std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when) {
+ std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
+ for (const NotifyArgs& args : generatedArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return generatedArgs;
+ }
+
static void assertMotionRange(const InputDeviceInfo& info,
int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) {
const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
@@ -7566,6 +7914,360 @@
}
}
+// --- ExternalStylusFusionTest ---
+
+class ExternalStylusFusionTest : public SingleTouchInputMapperTest {
+public:
+ SingleTouchInputMapper& initializeInputMapperWithExternalStylus() {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ mStylusState.when = ARBITRARY_TIME;
+ mStylusState.pressure = 0.f;
+ mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo});
+ configureDevice(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
+ processExternalStylusState(mapper);
+ return mapper;
+ }
+
+ std::list<NotifyArgs> processExternalStylusState(InputMapper& mapper) {
+ std::list<NotifyArgs> generatedArgs = mapper.updateExternalStylusState(mStylusState);
+ for (const NotifyArgs& args : generatedArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return generatedArgs;
+ }
+
+protected:
+ StylusState mStylusState{};
+ static constexpr uint32_t EXPECTED_SOURCE =
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_BLUETOOTH_STYLUS;
+
+ void testStartFusedStylusGesture(SingleTouchInputMapper& mapper) {
+ auto toolTypeSource =
+ AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
+
+ // The first pointer is withheld.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested(
+ ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT));
+
+ // The external stylus reports pressure. The withheld finger pointer is released as a
+ // stylus.
+ mStylusState.pressure = 1.f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // Subsequent pointer events are not withheld.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ }
+
+ void testSuccessfulFusionGesture(SingleTouchInputMapper& mapper) {
+ ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper));
+
+ // Releasing the touch pointer ends the gesture.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+ mStylusState.pressure = 0.f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ }
+
+ void testUnsuccessfulFusionGesture(SingleTouchInputMapper& mapper) {
+ auto toolTypeSource =
+ AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER));
+
+ // The first pointer is withheld when an external stylus is connected,
+ // and a timeout is requested.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested(
+ ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT));
+
+ // If the timeout expires early, it is requested again.
+ handleTimeout(mapper, ARBITRARY_TIME + 1);
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested(
+ ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT));
+
+ // When the timeout expires, the withheld touch is released as a finger pointer.
+ handleTimeout(mapper, ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN))));
+
+ // Subsequent pointer events are not withheld.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ }
+
+private:
+ InputDeviceInfo mExternalStylusDeviceInfo{};
+};
+
+TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ ASSERT_EQ(EXPECTED_SOURCE, mapper.getSources());
+}
+
+TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
+}
+
+TEST_F(ExternalStylusFusionTest, SuccessfulFusion_TouchFirst) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
+}
+
+// Test a successful stylus fusion gesture where the pressure is reported by the external
+// before the touch is reported by the touchscreen.
+TEST_F(ExternalStylusFusionTest, SuccessfulFusion_PressureFirst) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ auto toolTypeSource =
+ AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
+
+ // The external stylus reports pressure first. It is ignored for now.
+ mStylusState.pressure = 1.f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // When the touch goes down afterwards, it is reported as a stylus pointer.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(ExternalStylusFusionTest, FusionIsRepeatedForEachNewGesture) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+
+ ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
+ ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
+
+ ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
+ ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
+ ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
+ ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
+}
+
+TEST_F(ExternalStylusFusionTest, FusedPointerReportsPressureChanges) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ auto toolTypeSource =
+ AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
+
+ mStylusState.pressure = 0.8f;
+ processExternalStylusState(mapper);
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithPressure(0.8f))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // The external stylus reports a pressure change. We wait for some time for a touch event.
+ mStylusState.pressure = 0.6f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is reported within the timeout, it reports the updated pressure.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPressure(0.6f))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // There is another pressure change.
+ mStylusState.pressure = 0.5f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is not reported within the timeout, a move event is generated to report
+ // the new pressure.
+ handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPressure(0.5f))));
+
+ // If a zero pressure is reported before the touch goes up, the previous pressure value is
+ // repeated indefinitely.
+ mStylusState.pressure = 0.0f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+ processMove(mapper, 102, 202);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPressure(0.5f))));
+ processMove(mapper, 103, 203);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPressure(0.5f))));
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(ExternalStylusFusionTest, FusedPointerReportsToolTypeChanges) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ auto source = WithSource(EXPECTED_SOURCE);
+
+ mStylusState.pressure = 1.f;
+ mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_ERASER;
+ processExternalStylusState(mapper);
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_ERASER))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // The external stylus reports a tool change. We wait for some time for a touch event.
+ mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is reported within the timeout, it reports the updated pressure.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // There is another tool type change.
+ mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is not reported within the timeout, a move event is generated to report
+ // the new tool type.
+ handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(ExternalStylusFusionTest, FusedPointerReportsButtons) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ auto toolTypeSource =
+ AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
+
+ ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper));
+
+ // The external stylus reports a button change. We wait for some time for a touch event.
+ mStylusState.buttons = AMOTION_EVENT_BUTTON_STYLUS_PRIMARY;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is reported within the timeout, it reports the updated button state.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // The button is now released.
+ mStylusState.buttons = 0;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is not reported within the timeout, a move event is generated to report
+ // the new button state.
+ handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(0))));
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
// --- MultiTouchInputMapperTest ---
class MultiTouchInputMapperTest : public TouchInputMapperTest {
@@ -10086,6 +10788,54 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
+TEST_F(MultiTouchInputMapperTest, StylusSourceIsAddedDynamicallyFromToolType) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE | TOOL_TYPE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ // Even if the device supports reporting the ABS_MT_TOOL_TYPE axis, which could give it the
+ // ability to report MT_TOOL_PEN, we do not report the device as coming from a stylus source.
+ // Due to limitations in the evdev protocol, we cannot say for certain that a device is capable
+ // of reporting stylus events just because it supports ABS_MT_TOOL_TYPE.
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+
+ // However, if the device ever ends up reporting an event with MT_TOOL_PEN, it should be
+ // reported with the stylus source.
+ processId(mapper, FIRST_TRACKING_ID);
+ processToolType(mapper, MT_TOOL_PEN);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+ // Now that we know the device supports styluses, ensure that the device is re-configured with
+ // the stylus source.
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, mapper.getSources());
+ {
+ const auto& devices = mReader->getInputDevices();
+ auto deviceInfo =
+ std::find_if(devices.begin(), devices.end(),
+ [](const InputDeviceInfo& info) { return info.getId() == DEVICE_ID; });
+ LOG_ALWAYS_FATAL_IF(deviceInfo == devices.end(), "Cannot find InputDevice");
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, deviceInfo->getSources());
+ }
+
+ // Ensure the device was not reset to prevent interruptions of any ongoing gestures.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+}
+
// --- MultiTouchInputMapperTest_ExternalDevice ---
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 5e47b80..2801072 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -69,22 +69,24 @@
ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
}
-void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) {
+void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs,
+ std::optional<TimePoint> waitUntil) {
ASSERT_NO_FATAL_FAILURE(
assertCalled<NotifyMotionArgs>(outEventArgs,
- "Expected notifyMotion() to have been called."));
+ "Expected notifyMotion() to have been called.",
+ waitUntil));
}
void TestInputListener::assertNotifyMotionWasCalled(
- const ::testing::Matcher<NotifyMotionArgs>& matcher) {
+ const ::testing::Matcher<NotifyMotionArgs>& matcher, std::optional<TimePoint> waitUntil) {
NotifyMotionArgs outEventArgs;
- ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs));
+ ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs, waitUntil));
ASSERT_THAT(outEventArgs, matcher);
}
-void TestInputListener::assertNotifyMotionWasNotCalled() {
+void TestInputListener::assertNotifyMotionWasNotCalled(std::optional<TimePoint> waitUntil) {
ASSERT_NO_FATAL_FAILURE(
- assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called."));
+ assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called.", waitUntil));
}
void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
@@ -119,15 +121,18 @@
}
template <class NotifyArgsType>
-void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
+void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message,
+ std::optional<TimePoint> waitUntil) {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
if (queue.empty()) {
- const bool eventReceived =
- mCondition.wait_for(lock, mEventHappenedTimeout,
- [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+ const auto time =
+ waitUntil.value_or(std::chrono::system_clock::now() + mEventHappenedTimeout);
+ const bool eventReceived = mCondition.wait_until(lock, time, [&queue]() REQUIRES(mLock) {
+ return !queue.empty();
+ });
if (!eventReceived) {
FAIL() << "Timed out waiting for event: " << message.c_str();
}
@@ -139,14 +144,16 @@
}
template <class NotifyArgsType>
-void TestInputListener::assertNotCalled(std::string message) {
+void TestInputListener::assertNotCalled(std::string message, std::optional<TimePoint> waitUntil) {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
- const bool eventReceived =
- mCondition.wait_for(lock, mEventDidNotHappenTimeout,
- [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+ const auto time =
+ waitUntil.value_or(std::chrono::system_clock::now() + mEventDidNotHappenTimeout);
+ const bool eventReceived = mCondition.wait_until(lock, time, [&queue]() REQUIRES(mLock) {
+ return !queue.empty();
+ });
if (eventReceived) {
FAIL() << "Unexpected event: " << message.c_str();
}
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 87752e1..9665f70 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -33,6 +33,8 @@
std::chrono::milliseconds eventDidNotHappenTimeout = 0ms);
virtual ~TestInputListener();
+ using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
+
void assertNotifyConfigurationChangedWasCalled(
NotifyConfigurationChangedArgs* outEventArgs = nullptr);
@@ -48,11 +50,13 @@
void assertNotifyKeyWasNotCalled();
- void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
+ void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr,
+ std::optional<TimePoint> waitUntil = {});
- void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher);
+ void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher,
+ std::optional<TimePoint> waitUntil = {});
- void assertNotifyMotionWasNotCalled();
+ void assertNotifyMotionWasNotCalled(std::optional<TimePoint> waitUntil = {});
void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
@@ -63,10 +67,11 @@
private:
template <class NotifyArgsType>
- void assertCalled(NotifyArgsType* outEventArgs, std::string message);
+ void assertCalled(NotifyArgsType* outEventArgs, std::string message,
+ std::optional<TimePoint> waitUntil = {});
template <class NotifyArgsType>
- void assertNotCalled(std::string message);
+ void assertNotCalled(std::string message, std::optional<TimePoint> timeout = {});
template <class NotifyArgsType>
void addToQueue(const NotifyArgsType* args);
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 9a47e3e..9db3422 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -46,7 +46,8 @@
}
MATCHER_P(WithSource, source, "InputEvent with specified source") {
- *result_listener << "expected source " << source << ", but got " << arg.source;
+ *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
+ << inputEventSourceToString(arg.source);
return arg.source == source;
}
@@ -55,6 +56,11 @@
return arg.displayId == displayId;
}
+MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") {
+ *result_listener << "expected deviceId " << deviceId << ", but got " << arg.deviceId;
+ return arg.deviceId == deviceId;
+}
+
MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
*result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
return arg.keyCode == keyCode;
@@ -70,8 +76,8 @@
MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
- *result_listener << "expected pressure " << pressure << ", but got " << pressure;
- return argPressure;
+ *result_listener << "expected pressure " << pressure << ", but got " << argPressure;
+ return argPressure == pressure;
}
MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index bc695b8..97a2614 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -138,14 +138,36 @@
UinputExternalStylus::UinputExternalStylus()
: UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {}
+// --- UinputExternalStylusWithPressure ---
+
+UinputExternalStylusWithPressure::UinputExternalStylusWithPressure()
+ : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {}
+
+void UinputExternalStylusWithPressure::configureDevice(int fd, uinput_user_dev* device) {
+ UinputKeyboard::configureDevice(fd, device);
+
+ ioctl(fd, UI_SET_EVBIT, EV_ABS);
+ ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
+ device->absmin[ABS_PRESSURE] = RAW_PRESSURE_MIN;
+ device->absmax[ABS_PRESSURE] = RAW_PRESSURE_MAX;
+}
+
+void UinputExternalStylusWithPressure::setPressure(int32_t pressure) {
+ injectEvent(EV_ABS, ABS_PRESSURE, pressure);
+ injectEvent(EV_SYN, SYN_REPORT, 0);
+}
+
// --- UinputTouchScreen ---
UinputTouchScreen::UinputTouchScreen(const Rect& size)
- : UinputDevice(DEVICE_NAME, PRODUCT_ID), mSize(size) {}
+ : UinputKeyboard(DEVICE_NAME, PRODUCT_ID,
+ {BTN_TOUCH, BTN_TOOL_PEN, BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}),
+ mSize(size) {}
void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
+ UinputKeyboard::configureDevice(fd, device);
+
// Setup the touch screen device
- ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_EVBIT, EV_REL);
ioctl(fd, UI_SET_EVBIT, EV_ABS);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
@@ -155,10 +177,6 @@
ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
- ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
- ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
- ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
- ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS3);
device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
@@ -210,10 +228,6 @@
injectEvent(EV_SYN, SYN_REPORT, 0);
}
-void UinputTouchScreen::sendKey(int32_t scanCode, int32_t value) {
- injectEvent(EV_KEY, scanCode, value);
-}
-
// Get the center x, y base on the range definition.
const Point UinputTouchScreen::getCenterPoint() {
return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2);
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index d661bd3..51e331d 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -89,9 +89,9 @@
explicit UinputKeyboard(const char* name, int16_t productId = PRODUCT_ID,
std::initializer_list<int> keys = {});
-private:
void configureDevice(int fd, uinput_user_dev* device) override;
+private:
std::set<int> mKeys;
};
@@ -143,13 +143,35 @@
explicit UinputExternalStylus();
};
+// --- UinputExternalStylusWithPressure ---
+
+// A stylus that reports button presses and pressure values.
+class UinputExternalStylusWithPressure : public UinputKeyboard {
+public:
+ static constexpr const char* DEVICE_NAME = "Test Uinput External Stylus With Pressure";
+ static constexpr int16_t PRODUCT_ID = 46;
+
+ static constexpr int32_t RAW_PRESSURE_MIN = 0;
+ static constexpr int32_t RAW_PRESSURE_MAX = 255;
+
+ void setPressure(int32_t pressure);
+
+ template <class D, class... Ts>
+ friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+ void configureDevice(int fd, uinput_user_dev* device) override;
+
+ explicit UinputExternalStylusWithPressure();
+};
+
// --- UinputTouchScreen ---
// A multi-touch touchscreen device with specific size that also supports styluses.
-class UinputTouchScreen : public UinputDevice {
+class UinputTouchScreen : public UinputKeyboard {
public:
static constexpr const char* DEVICE_NAME = "Test Uinput Touch Screen";
- static constexpr int16_t PRODUCT_ID = 46;
+ static constexpr int16_t PRODUCT_ID = 47;
static const int32_t RAW_TOUCH_MIN = 0;
static const int32_t RAW_TOUCH_MAX = 31;
@@ -171,7 +193,6 @@
void sendUp();
void sendToolType(int32_t toolType);
void sendSync();
- void sendKey(int32_t scanCode, int32_t value);
const Point getCenterPoint();
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index f67c610..f5b360f 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -23,6 +23,7 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
+#include <aidl/sensors/convert.h>
using ::aidl::android::hardware::sensors::AdditionalInfo;
using ::aidl::android::hardware::sensors::DynamicSensorInfo;
@@ -34,469 +35,16 @@
using ::android::AidlMessageQueue;
using ::android::hardware::EventFlag;
using ::android::hardware::sensors::V2_1::implementation::MAX_RECEIVE_BUFFER_EVENT_COUNT;
+using ::android::hardware::sensors::implementation::convertToStatus;
+using ::android::hardware::sensors::implementation::convertToSensor;
+using ::android::hardware::sensors::implementation::convertToSensorEvent;
+using ::android::hardware::sensors::implementation::convertFromSensorEvent;
+
namespace android {
namespace {
-status_t convertToStatus(ndk::ScopedAStatus status) {
- if (status.isOk()) {
- return OK;
- } else {
- switch (status.getExceptionCode()) {
- case EX_ILLEGAL_ARGUMENT: {
- return BAD_VALUE;
- }
- case EX_SECURITY: {
- return PERMISSION_DENIED;
- }
- case EX_UNSUPPORTED_OPERATION: {
- return INVALID_OPERATION;
- }
- case EX_SERVICE_SPECIFIC: {
- switch (status.getServiceSpecificError()) {
- case ISensors::ERROR_BAD_VALUE: {
- return BAD_VALUE;
- }
- case ISensors::ERROR_NO_MEMORY: {
- return NO_MEMORY;
- }
- default: {
- return UNKNOWN_ERROR;
- }
- }
- }
- default: {
- return UNKNOWN_ERROR;
- }
- }
- }
-}
-
-void convertToSensor(const SensorInfo &src, sensor_t *dst) {
- dst->name = strdup(src.name.c_str());
- dst->vendor = strdup(src.vendor.c_str());
- dst->version = src.version;
- dst->handle = src.sensorHandle;
- dst->type = (int)src.type;
- dst->maxRange = src.maxRange;
- dst->resolution = src.resolution;
- dst->power = src.power;
- dst->minDelay = src.minDelayUs;
- dst->fifoReservedEventCount = src.fifoReservedEventCount;
- dst->fifoMaxEventCount = src.fifoMaxEventCount;
- dst->stringType = strdup(src.typeAsString.c_str());
- dst->requiredPermission = strdup(src.requiredPermission.c_str());
- dst->maxDelay = src.maxDelayUs;
- dst->flags = src.flags;
- dst->reserved[0] = dst->reserved[1] = 0;
-}
-
-void convertToSensorEvent(const Event &src, sensors_event_t *dst) {
- *dst = {.version = sizeof(sensors_event_t),
- .sensor = src.sensorHandle,
- .type = (int32_t)src.sensorType,
- .reserved0 = 0,
- .timestamp = src.timestamp};
-
- switch (src.sensorType) {
- case SensorType::META_DATA: {
- // Legacy HALs expect the handle reference in the meta data field.
- // Copy it over from the handle of the event.
- dst->meta_data.what = (int32_t)src.payload.get<Event::EventPayload::meta>().what;
- dst->meta_data.sensor = src.sensorHandle;
- // Set the sensor handle to 0 to maintain compatibility.
- dst->sensor = 0;
- break;
- }
-
- case SensorType::ACCELEROMETER:
- case SensorType::MAGNETIC_FIELD:
- case SensorType::ORIENTATION:
- case SensorType::GYROSCOPE:
- case SensorType::GRAVITY:
- case SensorType::LINEAR_ACCELERATION: {
- dst->acceleration.x = src.payload.get<Event::EventPayload::vec3>().x;
- dst->acceleration.y = src.payload.get<Event::EventPayload::vec3>().y;
- dst->acceleration.z = src.payload.get<Event::EventPayload::vec3>().z;
- dst->acceleration.status = (int32_t)src.payload.get<Event::EventPayload::vec3>().status;
- break;
- }
-
- case SensorType::GAME_ROTATION_VECTOR: {
- dst->data[0] = src.payload.get<Event::EventPayload::vec4>().x;
- dst->data[1] = src.payload.get<Event::EventPayload::vec4>().y;
- dst->data[2] = src.payload.get<Event::EventPayload::vec4>().z;
- dst->data[3] = src.payload.get<Event::EventPayload::vec4>().w;
- break;
- }
-
- case SensorType::ROTATION_VECTOR:
- case SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
- dst->data[0] = src.payload.get<Event::EventPayload::data>().values[0];
- dst->data[1] = src.payload.get<Event::EventPayload::data>().values[1];
- dst->data[2] = src.payload.get<Event::EventPayload::data>().values[2];
- dst->data[3] = src.payload.get<Event::EventPayload::data>().values[3];
- dst->data[4] = src.payload.get<Event::EventPayload::data>().values[4];
- break;
- }
-
- case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
- case SensorType::GYROSCOPE_UNCALIBRATED:
- case SensorType::ACCELEROMETER_UNCALIBRATED: {
- dst->uncalibrated_gyro.x_uncalib = src.payload.get<Event::EventPayload::uncal>().x;
- dst->uncalibrated_gyro.y_uncalib = src.payload.get<Event::EventPayload::uncal>().y;
- dst->uncalibrated_gyro.z_uncalib = src.payload.get<Event::EventPayload::uncal>().z;
- dst->uncalibrated_gyro.x_bias = src.payload.get<Event::EventPayload::uncal>().xBias;
- dst->uncalibrated_gyro.y_bias = src.payload.get<Event::EventPayload::uncal>().yBias;
- dst->uncalibrated_gyro.z_bias = src.payload.get<Event::EventPayload::uncal>().zBias;
- break;
- }
-
- case SensorType::HINGE_ANGLE:
- case SensorType::DEVICE_ORIENTATION:
- case SensorType::LIGHT:
- case SensorType::PRESSURE:
- case SensorType::PROXIMITY:
- case SensorType::RELATIVE_HUMIDITY:
- case SensorType::AMBIENT_TEMPERATURE:
- case SensorType::SIGNIFICANT_MOTION:
- case SensorType::STEP_DETECTOR:
- case SensorType::TILT_DETECTOR:
- case SensorType::WAKE_GESTURE:
- case SensorType::GLANCE_GESTURE:
- case SensorType::PICK_UP_GESTURE:
- case SensorType::WRIST_TILT_GESTURE:
- case SensorType::STATIONARY_DETECT:
- case SensorType::MOTION_DETECT:
- case SensorType::HEART_BEAT:
- case SensorType::LOW_LATENCY_OFFBODY_DETECT: {
- dst->data[0] = src.payload.get<Event::EventPayload::scalar>();
- break;
- }
-
- case SensorType::STEP_COUNTER: {
- dst->u64.step_counter = src.payload.get<Event::EventPayload::stepCount>();
- break;
- }
-
- case SensorType::HEART_RATE: {
- dst->heart_rate.bpm = src.payload.get<Event::EventPayload::heartRate>().bpm;
- dst->heart_rate.status =
- (int8_t)src.payload.get<Event::EventPayload::heartRate>().status;
- break;
- }
-
- case SensorType::POSE_6DOF: { // 15 floats
- for (size_t i = 0; i < 15; ++i) {
- dst->data[i] = src.payload.get<Event::EventPayload::pose6DOF>().values[i];
- }
- break;
- }
-
- case SensorType::DYNAMIC_SENSOR_META: {
- dst->dynamic_sensor_meta.connected =
- src.payload.get<Event::EventPayload::dynamic>().connected;
- dst->dynamic_sensor_meta.handle =
- src.payload.get<Event::EventPayload::dynamic>().sensorHandle;
- dst->dynamic_sensor_meta.sensor = NULL; // to be filled in later
-
- memcpy(dst->dynamic_sensor_meta.uuid,
- src.payload.get<Event::EventPayload::dynamic>().uuid.values.data(), 16);
-
- break;
- }
-
- case SensorType::ADDITIONAL_INFO: {
- const AdditionalInfo &srcInfo = src.payload.get<Event::EventPayload::additional>();
-
- additional_info_event_t *dstInfo = &dst->additional_info;
- dstInfo->type = (int32_t)srcInfo.type;
- dstInfo->serial = srcInfo.serial;
-
- switch (srcInfo.payload.getTag()) {
- case AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32: {
- const auto &values =
- srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataInt32>()
- .values;
- CHECK_EQ(values.size() * sizeof(int32_t), sizeof(dstInfo->data_int32));
- memcpy(dstInfo->data_int32, values.data(), sizeof(dstInfo->data_int32));
- break;
- }
- case AdditionalInfo::AdditionalInfoPayload::Tag::dataFloat: {
- const auto &values =
- srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataFloat>()
- .values;
- CHECK_EQ(values.size() * sizeof(float), sizeof(dstInfo->data_float));
- memcpy(dstInfo->data_float, values.data(), sizeof(dstInfo->data_float));
- break;
- }
- default: {
- ALOGE("Invalid sensor additional info tag: %d", (int)srcInfo.payload.getTag());
- }
- }
- break;
- }
-
- case SensorType::HEAD_TRACKER: {
- const auto &ht = src.payload.get<Event::EventPayload::headTracker>();
- dst->head_tracker.rx = ht.rx;
- dst->head_tracker.ry = ht.ry;
- dst->head_tracker.rz = ht.rz;
- dst->head_tracker.vx = ht.vx;
- dst->head_tracker.vy = ht.vy;
- dst->head_tracker.vz = ht.vz;
- dst->head_tracker.discontinuity_count = ht.discontinuityCount;
- break;
- }
-
- case SensorType::ACCELEROMETER_LIMITED_AXES:
- case SensorType::GYROSCOPE_LIMITED_AXES:
- dst->limited_axes_imu.x = src.payload.get<Event::EventPayload::limitedAxesImu>().x;
- dst->limited_axes_imu.y = src.payload.get<Event::EventPayload::limitedAxesImu>().y;
- dst->limited_axes_imu.z = src.payload.get<Event::EventPayload::limitedAxesImu>().z;
- dst->limited_axes_imu.x_supported =
- src.payload.get<Event::EventPayload::limitedAxesImu>().xSupported;
- dst->limited_axes_imu.y_supported =
- src.payload.get<Event::EventPayload::limitedAxesImu>().ySupported;
- dst->limited_axes_imu.z_supported =
- src.payload.get<Event::EventPayload::limitedAxesImu>().zSupported;
- break;
-
- case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED:
- case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED:
- dst->limited_axes_imu_uncalibrated.x_uncalib =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().x;
- dst->limited_axes_imu_uncalibrated.y_uncalib =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().y;
- dst->limited_axes_imu_uncalibrated.z_uncalib =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().z;
- dst->limited_axes_imu_uncalibrated.x_bias =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xBias;
- dst->limited_axes_imu_uncalibrated.y_bias =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().yBias;
- dst->limited_axes_imu_uncalibrated.z_bias =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zBias;
- dst->limited_axes_imu_uncalibrated.x_supported =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xSupported;
- dst->limited_axes_imu_uncalibrated.y_supported =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().ySupported;
- dst->limited_axes_imu_uncalibrated.z_supported =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zSupported;
- break;
-
- case SensorType::HEADING:
- dst->heading.heading = src.payload.get<Event::EventPayload::heading>().heading;
- dst->heading.accuracy = src.payload.get<Event::EventPayload::heading>().accuracy;
- break;
-
- default: {
- CHECK_GE((int32_t)src.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
-
- memcpy(dst->data, src.payload.get<Event::EventPayload::data>().values.data(),
- 16 * sizeof(float));
- break;
- }
- }
-}
-
-void convertFromSensorEvent(const sensors_event_t &src, Event *dst) {
- *dst = {
- .timestamp = src.timestamp,
- .sensorHandle = src.sensor,
- .sensorType = (SensorType)src.type,
- };
-
- switch (dst->sensorType) {
- case SensorType::META_DATA: {
- Event::EventPayload::MetaData meta;
- meta.what = (Event::EventPayload::MetaData::MetaDataEventType)src.meta_data.what;
- // Legacy HALs contain the handle reference in the meta data field.
- // Copy that over to the handle of the event. In legacy HALs this
- // field was expected to be 0.
- dst->sensorHandle = src.meta_data.sensor;
- dst->payload.set<Event::EventPayload::Tag::meta>(meta);
- break;
- }
-
- case SensorType::ACCELEROMETER:
- case SensorType::MAGNETIC_FIELD:
- case SensorType::ORIENTATION:
- case SensorType::GYROSCOPE:
- case SensorType::GRAVITY:
- case SensorType::LINEAR_ACCELERATION: {
- Event::EventPayload::Vec3 vec3;
- vec3.x = src.acceleration.x;
- vec3.y = src.acceleration.y;
- vec3.z = src.acceleration.z;
- vec3.status = (SensorStatus)src.acceleration.status;
- dst->payload.set<Event::EventPayload::Tag::vec3>(vec3);
- break;
- }
-
- case SensorType::GAME_ROTATION_VECTOR: {
- Event::EventPayload::Vec4 vec4;
- vec4.x = src.data[0];
- vec4.y = src.data[1];
- vec4.z = src.data[2];
- vec4.w = src.data[3];
- dst->payload.set<Event::EventPayload::Tag::vec4>(vec4);
- break;
- }
-
- case SensorType::ROTATION_VECTOR:
- case SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
- Event::EventPayload::Data data;
- memcpy(data.values.data(), src.data, 5 * sizeof(float));
- dst->payload.set<Event::EventPayload::Tag::data>(data);
- break;
- }
-
- case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
- case SensorType::GYROSCOPE_UNCALIBRATED:
- case SensorType::ACCELEROMETER_UNCALIBRATED: {
- Event::EventPayload::Uncal uncal;
- uncal.x = src.uncalibrated_gyro.x_uncalib;
- uncal.y = src.uncalibrated_gyro.y_uncalib;
- uncal.z = src.uncalibrated_gyro.z_uncalib;
- uncal.xBias = src.uncalibrated_gyro.x_bias;
- uncal.yBias = src.uncalibrated_gyro.y_bias;
- uncal.zBias = src.uncalibrated_gyro.z_bias;
- dst->payload.set<Event::EventPayload::Tag::uncal>(uncal);
- break;
- }
-
- case SensorType::DEVICE_ORIENTATION:
- case SensorType::LIGHT:
- case SensorType::PRESSURE:
- case SensorType::PROXIMITY:
- case SensorType::RELATIVE_HUMIDITY:
- case SensorType::AMBIENT_TEMPERATURE:
- case SensorType::SIGNIFICANT_MOTION:
- case SensorType::STEP_DETECTOR:
- case SensorType::TILT_DETECTOR:
- case SensorType::WAKE_GESTURE:
- case SensorType::GLANCE_GESTURE:
- case SensorType::PICK_UP_GESTURE:
- case SensorType::WRIST_TILT_GESTURE:
- case SensorType::STATIONARY_DETECT:
- case SensorType::MOTION_DETECT:
- case SensorType::HEART_BEAT:
- case SensorType::LOW_LATENCY_OFFBODY_DETECT:
- case SensorType::HINGE_ANGLE: {
- dst->payload.set<Event::EventPayload::Tag::scalar>((float)src.data[0]);
- break;
- }
-
- case SensorType::STEP_COUNTER: {
- dst->payload.set<Event::EventPayload::Tag::stepCount>(src.u64.step_counter);
- break;
- }
-
- case SensorType::HEART_RATE: {
- Event::EventPayload::HeartRate heartRate;
- heartRate.bpm = src.heart_rate.bpm;
- heartRate.status = (SensorStatus)src.heart_rate.status;
- dst->payload.set<Event::EventPayload::Tag::heartRate>(heartRate);
- break;
- }
-
- case SensorType::POSE_6DOF: { // 15 floats
- Event::EventPayload::Pose6Dof pose6DOF;
- for (size_t i = 0; i < 15; ++i) {
- pose6DOF.values[i] = src.data[i];
- }
- dst->payload.set<Event::EventPayload::Tag::pose6DOF>(pose6DOF);
- break;
- }
-
- case SensorType::DYNAMIC_SENSOR_META: {
- DynamicSensorInfo dynamic;
- dynamic.connected = src.dynamic_sensor_meta.connected;
- dynamic.sensorHandle = src.dynamic_sensor_meta.handle;
-
- memcpy(dynamic.uuid.values.data(), src.dynamic_sensor_meta.uuid, 16);
- dst->payload.set<Event::EventPayload::Tag::dynamic>(dynamic);
- break;
- }
-
- case SensorType::ADDITIONAL_INFO: {
- AdditionalInfo info;
- const additional_info_event_t &srcInfo = src.additional_info;
- info.type = (AdditionalInfo::AdditionalInfoType)srcInfo.type;
- info.serial = srcInfo.serial;
-
- AdditionalInfo::AdditionalInfoPayload::Int32Values data;
- CHECK_EQ(data.values.size() * sizeof(int32_t), sizeof(srcInfo.data_int32));
- memcpy(data.values.data(), srcInfo.data_int32, sizeof(srcInfo.data_int32));
- info.payload.set<AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32>(data);
-
- dst->payload.set<Event::EventPayload::Tag::additional>(info);
- break;
- }
-
- case SensorType::HEAD_TRACKER: {
- Event::EventPayload::HeadTracker headTracker;
- headTracker.rx = src.head_tracker.rx;
- headTracker.ry = src.head_tracker.ry;
- headTracker.rz = src.head_tracker.rz;
- headTracker.vx = src.head_tracker.vx;
- headTracker.vy = src.head_tracker.vy;
- headTracker.vz = src.head_tracker.vz;
- headTracker.discontinuityCount = src.head_tracker.discontinuity_count;
-
- dst->payload.set<Event::EventPayload::Tag::headTracker>(headTracker);
- break;
- }
-
- case SensorType::ACCELEROMETER_LIMITED_AXES:
- case SensorType::GYROSCOPE_LIMITED_AXES: {
- Event::EventPayload::LimitedAxesImu limitedAxesImu;
- limitedAxesImu.x = src.limited_axes_imu.x;
- limitedAxesImu.y = src.limited_axes_imu.y;
- limitedAxesImu.z = src.limited_axes_imu.z;
- limitedAxesImu.xSupported = src.limited_axes_imu.x_supported;
- limitedAxesImu.ySupported = src.limited_axes_imu.y_supported;
- limitedAxesImu.zSupported = src.limited_axes_imu.z_supported;
- dst->payload.set<Event::EventPayload::Tag::limitedAxesImu>(limitedAxesImu);
- break;
- }
-
- case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED:
- case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: {
- Event::EventPayload::LimitedAxesImuUncal limitedAxesImuUncal;
- limitedAxesImuUncal.x = src.limited_axes_imu_uncalibrated.x_uncalib;
- limitedAxesImuUncal.y = src.limited_axes_imu_uncalibrated.y_uncalib;
- limitedAxesImuUncal.z = src.limited_axes_imu_uncalibrated.z_uncalib;
- limitedAxesImuUncal.xBias = src.limited_axes_imu_uncalibrated.x_bias;
- limitedAxesImuUncal.yBias = src.limited_axes_imu_uncalibrated.y_bias;
- limitedAxesImuUncal.zBias = src.limited_axes_imu_uncalibrated.z_bias;
- limitedAxesImuUncal.xSupported = src.limited_axes_imu_uncalibrated.x_supported;
- limitedAxesImuUncal.ySupported = src.limited_axes_imu_uncalibrated.y_supported;
- limitedAxesImuUncal.zSupported = src.limited_axes_imu_uncalibrated.z_supported;
- dst->payload.set<Event::EventPayload::Tag::limitedAxesImuUncal>(limitedAxesImuUncal);
- break;
- }
-
- case SensorType::HEADING: {
- Event::EventPayload::Heading heading;
- heading.heading = src.heading.heading;
- heading.accuracy = src.heading.accuracy;
- dst->payload.set<Event::EventPayload::heading>(heading);
- break;
- }
-
- default: {
- CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
-
- Event::EventPayload::Data data;
- memcpy(data.values.data(), src.data, 16 * sizeof(float));
- dst->payload.set<Event::EventPayload::Tag::data>(data);
- break;
- }
- }
-}
-
void serviceDied(void *cookie) {
ALOGW("Sensors HAL died, attempting to reconnect.");
((AidlSensorHalWrapper *)cookie)->prepareForReconnect();
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 5ad4815..0eeb820 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -75,6 +75,7 @@
static_libs: [
"libaidlcommonsupport",
"android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors-V1-convert",
"android.hardware.sensors-V1-ndk",
],
diff --git a/services/sensorservice/aidl/Android.bp b/services/sensorservice/aidl/Android.bp
new file mode 100644
index 0000000..bbf49da
--- /dev/null
+++ b/services/sensorservice/aidl/Android.bp
@@ -0,0 +1,43 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_shared {
+ name: "libsensorserviceaidl",
+ srcs: [
+ "EventQueue.cpp",
+ "DirectReportChannel.cpp",
+ "SensorManager.cpp",
+ "utils.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ header_libs: ["jni_headers"],
+ shared_libs: [
+ "libbase",
+ "libutils",
+ "libcutils",
+ "libbinder_ndk",
+ "libsensor",
+ "android.frameworks.sensorservice-V1-ndk",
+ "android.hardware.sensors-V1-ndk",
+ ],
+ export_include_dirs: [
+ "include/",
+ ],
+ static_libs: [
+ "android.hardware.sensors-V1-convert",
+ ],
+
+ export_header_lib_headers: ["jni_headers"],
+ local_include_dirs: [
+ "include/sensorserviceaidl/",
+ ],
+}
diff --git a/services/sensorservice/aidl/DirectReportChannel.cpp b/services/sensorservice/aidl/DirectReportChannel.cpp
new file mode 100644
index 0000000..cab53c1
--- /dev/null
+++ b/services/sensorservice/aidl/DirectReportChannel.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DirectReportChannel.h"
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+DirectReportChannel::DirectReportChannel(::android::SensorManager& manager, int channelId)
+ : mManager(manager), mId(channelId) {}
+
+DirectReportChannel::~DirectReportChannel() {
+ mManager.destroyDirectChannel(mId);
+}
+
+ndk::ScopedAStatus DirectReportChannel::configure(
+ int32_t sensorHandle, ::aidl::android::hardware::sensors::ISensors::RateLevel rate,
+ int32_t* _aidl_return) {
+ int token = mManager.configureDirectChannel(mId, sensorHandle, static_cast<int>(rate));
+ if (token <= 0) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(token);
+ }
+ *_aidl_return = token;
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/DirectReportChannel.h b/services/sensorservice/aidl/DirectReportChannel.h
new file mode 100644
index 0000000..d9ea73d
--- /dev/null
+++ b/services/sensorservice/aidl/DirectReportChannel.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <aidl/android/frameworks/sensorservice/BnDirectReportChannel.h>
+#include <aidl/android/hardware/sensors/ISensors.h>
+#include <sensor/SensorManager.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+class DirectReportChannel final
+ : public ::aidl::android::frameworks::sensorservice::BnDirectReportChannel {
+public:
+ DirectReportChannel(::android::SensorManager& manager, int channelId);
+ ~DirectReportChannel();
+
+ ndk::ScopedAStatus configure(int32_t sensorHandle,
+ ::aidl::android::hardware::sensors::ISensors::RateLevel rate,
+ int32_t* _aidl_return) override;
+
+private:
+ ::android::SensorManager& mManager;
+ const int mId;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/EventQueue.cpp b/services/sensorservice/aidl/EventQueue.cpp
new file mode 100644
index 0000000..d4e8906
--- /dev/null
+++ b/services/sensorservice/aidl/EventQueue.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EventQueue.h"
+#include "utils.h"
+
+#include <android-base/logging.h>
+#include <utils/Looper.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+using ::aidl::android::frameworks::sensorservice::IEventQueueCallback;
+using ::aidl::android::hardware::sensors::Event;
+
+class EventQueueLooperCallback : public ::android::LooperCallback {
+public:
+ EventQueueLooperCallback(sp<::android::SensorEventQueue> queue,
+ std::shared_ptr<IEventQueueCallback> callback)
+ : mQueue(queue), mCallback(callback) {}
+
+ int handleEvent(__unused int fd, __unused int events, __unused void* data) {
+ ASensorEvent event;
+ ssize_t actual;
+
+ auto internalQueue = mQueue.promote();
+ if (internalQueue == nullptr) {
+ return 1;
+ }
+
+ while ((actual = internalQueue->read(&event, 1)) > 0) {
+ internalQueue->sendAck(&event, actual);
+ ndk::ScopedAStatus ret = mCallback->onEvent(convertEvent(event));
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Failed to envoke EventQueueCallback: " << ret;
+ }
+ }
+
+ return 1; // continue to receive callbacks
+ }
+
+private:
+ wp<::android::SensorEventQueue> mQueue;
+ std::shared_ptr<IEventQueueCallback> mCallback;
+};
+
+EventQueue::EventQueue(std::shared_ptr<IEventQueueCallback> callback, sp<::android::Looper> looper,
+ sp<::android::SensorEventQueue> internalQueue)
+ : mLooper(looper), mInternalQueue(internalQueue) {
+ mLooper->addFd(internalQueue->getFd(), ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT,
+ new EventQueueLooperCallback(internalQueue, callback), nullptr);
+}
+
+// FIXME why was this on onLastStrongRef instead of dtor?
+EventQueue::~EventQueue() {
+ mLooper->removeFd(mInternalQueue->getFd());
+}
+
+ndk::ScopedAStatus EventQueue::enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs,
+ int64_t in_maxBatchReportLatencyUs) {
+ return convertResult(mInternalQueue->enableSensor(in_sensorHandle, in_samplingPeriodUs,
+ in_maxBatchReportLatencyUs, 0));
+}
+
+ndk::ScopedAStatus EventQueue::disableSensor(int32_t in_sensorHandle) {
+ return convertResult(mInternalQueue->disableSensor(in_sensorHandle));
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/EventQueue.h b/services/sensorservice/aidl/EventQueue.h
new file mode 100644
index 0000000..0ae1eba
--- /dev/null
+++ b/services/sensorservice/aidl/EventQueue.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "SensorManagerAidl.h"
+
+#include <aidl/android/frameworks/sensorservice/BnEventQueue.h>
+#include <sensor/SensorManager.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+struct EventQueue final : public aidl::android::frameworks::sensorservice::BnEventQueue {
+ EventQueue(
+ std::shared_ptr<aidl::android::frameworks::sensorservice::IEventQueueCallback> callback,
+ sp<::android::Looper> looper, sp<::android::SensorEventQueue> internalQueue);
+ ~EventQueue();
+
+ ndk::ScopedAStatus enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs,
+ int64_t in_maxBatchReportLatencyUs) override;
+ ndk::ScopedAStatus disableSensor(int32_t sensorHandle) override;
+
+private:
+ friend class EventQueueLooperCallback;
+ sp<::android::Looper> mLooper;
+ sp<::android::SensorEventQueue> mInternalQueue;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
new file mode 100644
index 0000000..6d8d574
--- /dev/null
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// LOG_TAG defined via build flag.
+#ifndef LOG_TAG
+#define LOG_TAG "AidlSensorManager"
+#endif
+
+#include "DirectReportChannel.h"
+#include "EventQueue.h"
+#include "SensorManagerAidl.h"
+#include "utils.h"
+
+#include <aidl/android/hardware/sensors/ISensors.h>
+#include <android-base/logging.h>
+#include <android/binder_ibinder.h>
+#include <sched.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+using ::aidl::android::frameworks::sensorservice::IDirectReportChannel;
+using ::aidl::android::frameworks::sensorservice::IEventQueue;
+using ::aidl::android::frameworks::sensorservice::IEventQueueCallback;
+using ::aidl::android::frameworks::sensorservice::ISensorManager;
+using ::aidl::android::hardware::common::Ashmem;
+using ::aidl::android::hardware::sensors::ISensors;
+using ::aidl::android::hardware::sensors::SensorInfo;
+using ::aidl::android::hardware::sensors::SensorType;
+using ::android::frameworks::sensorservice::implementation::SensorManagerAidl;
+
+static const char* POLL_THREAD_NAME = "aidl_ssvc_poll";
+
+SensorManagerAidl::SensorManagerAidl(JavaVM* vm)
+ : mLooper(new Looper(false)), mStopThread(true), mJavaVm(vm) {}
+SensorManagerAidl::~SensorManagerAidl() {
+ // Stops pollAll inside the thread.
+ std::lock_guard<std::mutex> lock(mThreadMutex);
+
+ mStopThread = true;
+ if (mLooper != nullptr) {
+ mLooper->wake();
+ }
+ if (mPollThread.joinable()) {
+ mPollThread.join();
+ }
+}
+
+ndk::ScopedAStatus createDirectChannel(::android::SensorManager& manager, size_t size, int type,
+ const native_handle_t* handle,
+ std::shared_ptr<IDirectReportChannel>* chan) {
+ int channelId = manager.createDirectChannel(size, type, handle);
+ if (channelId < 0) {
+ return convertResult(channelId);
+ }
+ if (channelId == 0) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+ *chan = ndk::SharedRefBase::make<DirectReportChannel>(manager, channelId);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createAshmemDirectChannel(
+ const Ashmem& in_mem, int64_t in_size,
+ std::shared_ptr<IDirectReportChannel>* _aidl_return) {
+ if (in_size > in_mem.size || in_size < ISensors::DIRECT_REPORT_SENSOR_EVENT_TOTAL_LENGTH) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE);
+ }
+ native_handle_t* handle = native_handle_create(1, 0);
+ handle->data[0] = dup(in_mem.fd.get());
+
+ auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_ASHMEM,
+ handle, _aidl_return);
+ int result = native_handle_close(handle);
+ CHECK(result == 0) << "Failed to close the native_handle_t: " << result;
+ result = native_handle_delete(handle);
+ CHECK(result == 0) << "Failed to delete the native_handle_t: " << result;
+
+ return status;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createGrallocDirectChannel(
+ const ndk::ScopedFileDescriptor& in_mem, int64_t in_size,
+ std::shared_ptr<IDirectReportChannel>* _aidl_return) {
+ native_handle_t* handle = native_handle_create(1, 0);
+ handle->data[0] = dup(in_mem.get());
+
+ auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_GRALLOC,
+ handle, _aidl_return);
+ int result = native_handle_close(handle);
+ CHECK(result == 0) << "Failed to close the native_handle_t: " << result;
+ result = native_handle_delete(handle);
+ CHECK(result == 0) << "Failed to delete the native_handle_t: " << result;
+
+ return status;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createEventQueue(
+ const std::shared_ptr<IEventQueueCallback>& in_callback,
+ std::shared_ptr<IEventQueue>* _aidl_return) {
+ if (in_callback == nullptr) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE);
+ }
+
+ sp<::android::Looper> looper = getLooper();
+ if (looper == nullptr) {
+ LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue cannot initialize looper";
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+
+ String8 package(String8::format("aidl_client_pid_%d", AIBinder_getCallingPid()));
+ sp<::android::SensorEventQueue> internalQueue = getInternalManager().createEventQueue(package);
+ if (internalQueue == nullptr) {
+ LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue returns nullptr.";
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+
+ *_aidl_return = ndk::SharedRefBase::make<EventQueue>(in_callback, looper, internalQueue);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+SensorInfo convertSensor(Sensor src) {
+ SensorInfo dst;
+ dst.sensorHandle = src.getHandle();
+ dst.name = src.getName();
+ dst.vendor = src.getVendor();
+ dst.version = src.getVersion();
+ dst.type = static_cast<SensorType>(src.getType());
+ dst.typeAsString = src.getStringType();
+ // maxRange uses maxValue because ::android::Sensor wraps the
+ // internal sensor_t in this way.
+ dst.maxRange = src.getMaxValue();
+ dst.resolution = src.getResolution();
+ dst.power = src.getPowerUsage();
+ dst.minDelayUs = src.getMinDelay();
+ dst.fifoReservedEventCount = src.getFifoReservedEventCount();
+ dst.fifoMaxEventCount = src.getFifoMaxEventCount();
+ dst.requiredPermission = src.getRequiredPermission();
+ dst.maxDelayUs = src.getMaxDelay();
+ dst.flags = src.getFlags();
+ return dst;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::getDefaultSensor(SensorType in_type,
+ SensorInfo* _aidl_return) {
+ ::android::Sensor const* sensor =
+ getInternalManager().getDefaultSensor(static_cast<int>(in_type));
+ if (!sensor) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_NOT_EXIST);
+ }
+ *_aidl_return = convertSensor(*sensor);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SensorManagerAidl::getSensorList(std::vector<SensorInfo>* _aidl_return) {
+ Sensor const* const* list;
+ _aidl_return->clear();
+ ssize_t count = getInternalManager().getSensorList(&list);
+ if (count < 0 || list == nullptr) {
+ LOG(ERROR) << "SensorMAanger::getSensorList failed with count: " << count;
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+ _aidl_return->reserve(static_cast<size_t>(count));
+ for (ssize_t i = 0; i < count; ++i) {
+ _aidl_return->push_back(convertSensor(*list[i]));
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+::android::SensorManager& SensorManagerAidl::getInternalManager() {
+ std::lock_guard<std::mutex> lock(mInternalManagerMutex);
+ if (mInternalManager == nullptr) {
+ mInternalManager = &::android::SensorManager::getInstanceForPackage(
+ String16(ISensorManager::descriptor));
+ }
+ return *mInternalManager;
+}
+
+/* One global looper for all event queues created from this SensorManager. */
+sp<Looper> SensorManagerAidl::getLooper() {
+ std::lock_guard<std::mutex> lock(mThreadMutex);
+
+ if (!mPollThread.joinable()) {
+ // if thread not initialized, start thread
+ mStopThread = false;
+ std::thread pollThread{[&stopThread = mStopThread, looper = mLooper, javaVm = mJavaVm] {
+ struct sched_param p = {0};
+ p.sched_priority = 10;
+ if (sched_setscheduler(0 /* current thread*/, SCHED_FIFO, &p) != 0) {
+ LOG(ERROR) << "Could not use SCHED_FIFO for looper thread: " << strerror(errno);
+ }
+
+ // set looper
+ Looper::setForThread(looper);
+
+ // Attach the thread to JavaVM so that pollAll do not crash if the thread
+ // eventually calls into Java.
+ JavaVMAttachArgs args{.version = JNI_VERSION_1_2,
+ .name = POLL_THREAD_NAME,
+ .group = nullptr};
+ JNIEnv* env;
+ if (javaVm->AttachCurrentThread(&env, &args) != JNI_OK) {
+ LOG(FATAL) << "Cannot attach SensorManager looper thread to Java VM.";
+ }
+
+ LOG(INFO) << POLL_THREAD_NAME << " started.";
+ for (;;) {
+ int pollResult = looper->pollAll(-1 /* timeout */);
+ if (pollResult == Looper::POLL_WAKE) {
+ if (stopThread == true) {
+ LOG(INFO) << POLL_THREAD_NAME << ": requested to stop";
+ break;
+ } else {
+ LOG(INFO) << POLL_THREAD_NAME << ": spurious wake up, back to work";
+ }
+ } else {
+ LOG(ERROR) << POLL_THREAD_NAME << ": Looper::pollAll returns unexpected "
+ << pollResult;
+ break;
+ }
+ }
+
+ if (javaVm->DetachCurrentThread() != JNI_OK) {
+ LOG(ERROR) << "Cannot detach SensorManager looper thread from Java VM.";
+ }
+
+ LOG(INFO) << POLL_THREAD_NAME << " is terminated.";
+ }};
+ mPollThread = std::move(pollThread);
+ }
+ return mLooper;
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
new file mode 100644
index 0000000..c77ee88
--- /dev/null
+++ b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/frameworks/sensorservice/BnSensorManager.h>
+#include <jni.h>
+#include <sensor/SensorManager.h>
+#include <utils/Looper.h>
+#include <mutex>
+#include <thread>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+class SensorManagerAidl : public ::aidl::android::frameworks::sensorservice::BnSensorManager {
+public:
+ explicit SensorManagerAidl(JavaVM* vm);
+ ~SensorManagerAidl();
+
+ ::ndk::ScopedAStatus createAshmemDirectChannel(
+ const ::aidl::android::hardware::common::Ashmem& in_mem, int64_t in_size,
+ std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>*
+ _aidl_return) override;
+ ::ndk::ScopedAStatus createEventQueue(
+ const std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueueCallback>&
+ in_callback,
+ std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueue>* _aidl_return)
+ override;
+ ::ndk::ScopedAStatus createGrallocDirectChannel(
+ const ::ndk::ScopedFileDescriptor& in_buffer, int64_t in_size,
+ std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>*
+ _aidl_return) override;
+ ::ndk::ScopedAStatus getDefaultSensor(
+ ::aidl::android::hardware::sensors::SensorType in_type,
+ ::aidl::android::hardware::sensors::SensorInfo* _aidl_return) override;
+ ::ndk::ScopedAStatus getSensorList(
+ std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) override;
+
+private:
+ // Block until ::android::SensorManager is initialized.
+ ::android::SensorManager& getInternalManager();
+ sp<Looper> getLooper();
+
+ std::mutex mInternalManagerMutex;
+ ::android::SensorManager* mInternalManager = nullptr; // does not own
+ sp<Looper> mLooper;
+
+ volatile bool mStopThread;
+ std::mutex mThreadMutex; // protects mPollThread
+ std::thread mPollThread;
+
+ JavaVM* mJavaVm;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/utils.cpp b/services/sensorservice/aidl/utils.cpp
new file mode 100644
index 0000000..26bcdc5
--- /dev/null
+++ b/services/sensorservice/aidl/utils.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+
+#include <aidl/android/frameworks/sensorservice/ISensorManager.h>
+#include <aidl/sensors/convert.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+ndk::ScopedAStatus convertResult(status_t src) {
+ using ::aidl::android::frameworks::sensorservice::ISensorManager;
+
+ int err = 0;
+ switch (src) {
+ case OK:
+ return ndk::ScopedAStatus::ok();
+ case NAME_NOT_FOUND:
+ err = ISensorManager::RESULT_NOT_EXIST;
+ break;
+ case NO_MEMORY:
+ err = ISensorManager::RESULT_NO_MEMORY;
+ break;
+ case NO_INIT:
+ err = ISensorManager::RESULT_NO_INIT;
+ break;
+ case PERMISSION_DENIED:
+ err = ISensorManager::RESULT_PERMISSION_DENIED;
+ break;
+ case BAD_VALUE:
+ err = ISensorManager::RESULT_BAD_VALUE;
+ break;
+ case INVALID_OPERATION:
+ err = ISensorManager::RESULT_INVALID_OPERATION;
+ break;
+ default:
+ err = ISensorManager::RESULT_UNKNOWN_ERROR;
+ }
+ return ndk::ScopedAStatus::fromServiceSpecificError(err);
+}
+
+::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& src) {
+ ::aidl::android::hardware::sensors::Event dst;
+ ::android::hardware::sensors::implementation::
+ convertFromSensorEvent(reinterpret_cast<const sensors_event_t&>(src), &dst);
+ return dst;
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/utils.h b/services/sensorservice/aidl/utils.h
new file mode 100644
index 0000000..06ba59e
--- /dev/null
+++ b/services/sensorservice/aidl/utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <aidl/android/hardware/sensors/Event.h>
+#include <sensor/Sensor.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+::ndk::ScopedAStatus convertResult(status_t status);
+::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& event);
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 9868c8e..a5195f4 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -41,6 +41,7 @@
#include "Display/DisplaySnapshot.h"
#include "DisplayDevice.h"
+#include "FrontEnd/DisplayInfo.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
#include "SurfaceFlinger.h"
@@ -131,7 +132,7 @@
}
}
-auto DisplayDevice::getInputInfo() const -> InputInfo {
+auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo {
gui::DisplayInfo info;
info.displayId = getLayerStack().id;
@@ -160,7 +161,9 @@
return {.info = info,
.transform = displayTransform,
.receivesInput = receivesInput(),
- .isSecure = isSecure()};
+ .isSecure = isSecure(),
+ .isPrimary = isPrimary(),
+ .rotationFlags = ui::Transform::toRotationFlags(mOrientation)};
}
void DisplayDevice::setPowerMode(hal::PowerMode mode) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 1602a71..4b64aab 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -45,11 +45,11 @@
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
+#include "FrontEnd/DisplayInfo.h"
#include "Scheduler/RefreshRateSelector.h"
#include "ThreadContext.h"
#include "TracedOrdinal.h"
#include "Utils/Dumper.h"
-
namespace android {
class Fence;
@@ -167,14 +167,7 @@
void setDisplayName(const std::string& displayName);
const std::string& getDisplayName() const { return mDisplayName; }
- struct InputInfo {
- gui::DisplayInfo info;
- ui::Transform transform;
- bool receivesInput;
- bool isSecure;
- };
-
- InputInfo getInputInfo() const;
+ surfaceflinger::frontend::DisplayInfo getFrontEndInfo() const;
/* ------------------------------------------------------------------------
* Display power mode management.
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
new file mode 100644
index 0000000..0c7b24a
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/DisplayInfo.h>
+
+namespace android::surfaceflinger::frontend {
+
+// Display information needed to populate input and calculate layer geometry.
+struct DisplayInfo {
+ gui::DisplayInfo info;
+ ui::Transform transform;
+ bool receivesInput;
+ bool isSecure;
+ // TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed.
+ bool isPrimary;
+ ui::Transform::RotationFlags rotationFlags;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 95961fe..8629671 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -24,7 +24,7 @@
#include "TransactionHandler.h"
-namespace android {
+namespace android::surfaceflinger::frontend {
void TransactionHandler::queueTransaction(TransactionState&& state) {
mLocklessTransactionQueue.push(std::move(state));
@@ -186,4 +186,4 @@
mStalledTransactions.erase(it);
}
}
-} // namespace android
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 2b6f07d..a06b870 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -18,9 +18,6 @@
#include <semaphore.h>
#include <cstdint>
-#include <mutex>
-#include <queue>
-#include <thread>
#include <vector>
#include <LocklessQueue.h>
@@ -30,6 +27,10 @@
#include <ftl/small_vector.h>
namespace android {
+
+class TestableSurfaceFlinger;
+namespace surfaceflinger::frontend {
+
class TransactionHandler {
public:
struct TransactionFlushState {
@@ -60,7 +61,7 @@
private:
// For unit tests
- friend class TestableSurfaceFlinger;
+ friend class ::android::TestableSurfaceFlinger;
int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&);
TransactionReadiness applyFilters(TransactionFlushState&);
@@ -71,5 +72,5 @@
ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
std::vector<uint64_t> mStalledTransactions;
};
-
+} // namespace surfaceflinger::frontend
} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e3fe7a7..8a3e784 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -195,8 +195,7 @@
mDrawingState.color.b = -1.0_hf;
}
- mFrameTracker.setDisplayRefreshPeriod(
- args.flinger->mScheduler->getVsyncPeriodFromRefreshRateSelector());
+ mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod());
mOwnerUid = args.ownerUid;
mOwnerPid = args.ownerPid;
@@ -3974,17 +3973,14 @@
}
LayerSnapshotGuard::LayerSnapshotGuard(Layer* layer) : mLayer(layer) {
- LOG_ALWAYS_FATAL_IF(!mLayer, "LayerSnapshotGuard received a null layer.");
- mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot);
- LOG_ALWAYS_FATAL_IF(!mLayer->mLayerFE->mSnapshot,
- "LayerFE snapshot null after taking ownership from layer");
+ if (mLayer) {
+ mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot);
+ }
}
LayerSnapshotGuard::~LayerSnapshotGuard() {
if (mLayer) {
mLayer->mSnapshot = std::move(mLayer->mLayerFE->mSnapshot);
- LOG_ALWAYS_FATAL_IF(!mLayer->mSnapshot,
- "Layer snapshot null after taking ownership from LayerFE");
}
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 0e80817..65ee487 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -363,14 +363,16 @@
}
}
- void resetIdleTimer(bool kernelOnly) {
- if (!mIdleTimer) {
- return;
+ void resetKernelIdleTimer() {
+ if (mIdleTimer && mConfig.kernelIdleTimerController) {
+ mIdleTimer->reset();
}
- if (kernelOnly && !mConfig.kernelIdleTimerController.has_value()) {
- return;
+ }
+
+ void resetIdleTimer() {
+ if (mIdleTimer) {
+ mIdleTimer->reset();
}
- mIdleTimer->reset();
}
void dump(utils::Dumper&) const EXCLUDES(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 6108d92..f1fcc88 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -70,7 +70,7 @@
mTouchTimer.reset();
// Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
- setRefreshRateSelector(nullptr);
+ demoteLeaderDisplay();
}
void Scheduler::startTimers() {
@@ -95,40 +95,29 @@
}
}
-void Scheduler::setRefreshRateSelector(RefreshRateSelectorPtr newSelectorPtr) {
- // No need to lock for reads on kMainThreadContext.
- if (const auto& selectorPtr = FTL_FAKE_GUARD(mRefreshRateSelectorLock, mRefreshRateSelector)) {
- unbindIdleTimer(*selectorPtr);
- }
+void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
+ demoteLeaderDisplay();
- {
- // Clear state that depends on the current RefreshRateSelector.
- std::scoped_lock lock(mPolicyLock);
- mPolicy = {};
- }
-
- std::scoped_lock lock(mRefreshRateSelectorLock);
- mRefreshRateSelector = std::move(newSelectorPtr);
-
- if (mRefreshRateSelector) {
- bindIdleTimer(*mRefreshRateSelector);
- }
+ std::scoped_lock lock(mDisplayLock);
+ promoteLeaderDisplay(leaderIdOpt);
}
void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
- if (!mLeaderDisplayId) {
- mLeaderDisplayId = displayId;
- }
+ demoteLeaderDisplay();
+ std::scoped_lock lock(mDisplayLock);
mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
+
+ promoteLeaderDisplay();
}
void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
- if (mLeaderDisplayId == displayId) {
- mLeaderDisplayId.reset();
- }
+ demoteLeaderDisplay();
+ std::scoped_lock lock(mDisplayLock);
mRefreshRateSelectors.erase(displayId);
+
+ promoteLeaderDisplay();
}
void Scheduler::run() {
@@ -163,7 +152,7 @@
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
const bool supportsFrameRateOverrideByContent =
- holdRefreshRateSelector()->supportsFrameRateOverrideByContent();
+ leaderSelectorPtr()->supportsFrameRateOverrideByContent();
return mFrameRateOverrideMappings
.getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
}
@@ -178,8 +167,6 @@
}
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
- std::scoped_lock lock(mRefreshRateSelectorLock);
-
return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
};
@@ -187,7 +174,7 @@
impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
return [this](uid_t uid) {
- const Fps refreshRate = holdRefreshRateSelector()->getActiveModePtr()->getFps();
+ const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
const auto frameRate = getFrameRateOverride(uid);
@@ -281,7 +268,7 @@
void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
const bool supportsFrameRateOverrideByContent =
- holdRefreshRateSelector()->supportsFrameRateOverrideByContent();
+ leaderSelectorPtr()->supportsFrameRateOverrideByContent();
std::vector<FrameRateOverride> overrides =
mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -322,8 +309,7 @@
// If the mode is not the current mode, this means that a
// mode change is in progress. In that case we shouldn't dispatch an event
// as it will be dispatched when the current mode changes.
- if (std::scoped_lock lock(mRefreshRateSelectorLock);
- mRefreshRateSelector->getActiveModePtr() != mPolicy.mode) {
+ if (leaderSelectorPtr()->getActiveModePtr() != mPolicy.mode) {
return;
}
@@ -416,10 +402,7 @@
const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- const auto refreshRate = [&] {
- std::scoped_lock lock(mRefreshRateSelectorLock);
- return mRefreshRateSelector->getActiveModePtr()->getFps();
- }();
+ const auto refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
resyncToHardwareVsync(false, refreshRate);
}
}
@@ -478,12 +461,9 @@
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
LayerHistory::LayerUpdateType updateType) {
- {
- std::scoped_lock lock(mRefreshRateSelectorLock);
- if (!mRefreshRateSelector->canSwitch()) return;
+ if (leaderSelectorPtr()->canSwitch()) {
+ mLayerHistory.record(layer, presentTime, systemTime(), updateType);
}
-
- mLayerHistory.record(layer, presentTime, systemTime(), updateType);
}
void Scheduler::setModeChangePending(bool pending) {
@@ -496,7 +476,7 @@
}
void Scheduler::chooseRefreshRateForContent() {
- const auto selectorPtr = holdRefreshRateSelector();
+ const auto selectorPtr = leaderSelectorPtr();
if (!selectorPtr->canSwitch()) return;
ATRACE_CALL();
@@ -506,16 +486,13 @@
}
void Scheduler::resetIdleTimer() {
- std::scoped_lock lock(mRefreshRateSelectorLock);
- mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ false);
+ leaderSelectorPtr()->resetIdleTimer();
}
void Scheduler::onTouchHint() {
if (mTouchTimer) {
mTouchTimer->reset();
-
- std::scoped_lock lock(mRefreshRateSelectorLock);
- mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ true);
+ leaderSelectorPtr()->resetKernelIdleTimer();
}
}
@@ -535,30 +512,12 @@
mLayerHistory.clear();
}
-void Scheduler::bindIdleTimer(RefreshRateSelector& selector) {
- selector.setIdleTimerCallbacks(
- {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
- .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
-
- selector.startIdleTimer();
-}
-
-void Scheduler::unbindIdleTimer(RefreshRateSelector& selector) {
- selector.stopIdleTimer();
- selector.clearIdleTimerCallbacks();
-}
-
void Scheduler::kernelIdleTimerCallback(TimerState state) {
ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
// TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
// magic number
- const Fps refreshRate = [&] {
- std::scoped_lock lock(mRefreshRateSelectorLock);
- return mRefreshRateSelector->getActiveModePtr()->getFps();
- }();
+ const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
using namespace fps_approx_ops;
@@ -614,7 +573,11 @@
}
{
utils::Dumper::Section section(dumper, "Policy"sv);
-
+ {
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+ dumper.dump("leaderDisplayId"sv, mLeaderDisplayId);
+ }
dumper.dump("layerHistory"sv, mLayerHistory.dump());
dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval));
dumper.dump("displayPowerTimer"sv, mDisplayPowerTimer.transform(&OneShotTimer::interval));
@@ -638,17 +601,44 @@
}
bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
- // we always update mFrameRateOverridesByContent here
- // supportsFrameRateOverridesByContent will be checked
- // when getting FrameRateOverrides from mFrameRateOverrideMappings
- if (!consideredSignals.idle) {
- const auto frameRateOverrides =
- holdRefreshRateSelector()->getFrameRateOverrides(mPolicy.contentRequirements,
- displayRefreshRate,
- consideredSignals);
- return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+ if (consideredSignals.idle) return false;
+
+ const auto frameRateOverrides =
+ leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
+ displayRefreshRate, consideredSignals);
+
+ // Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying
+ // the FrameRateOverrideMappings rather than here.
+ return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+}
+
+void Scheduler::promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
+ // TODO(b/241286431): Choose the leader display.
+ mLeaderDisplayId = leaderIdOpt.value_or(mRefreshRateSelectors.begin()->first);
+ ALOGI("Display %s is the leader", to_string(*mLeaderDisplayId).c_str());
+
+ if (const auto leaderPtr = leaderSelectorPtrLocked()) {
+ leaderPtr->setIdleTimerCallbacks(
+ {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+ .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+ .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+ .onExpired =
+ [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+
+ leaderPtr->startIdleTimer();
}
- return false;
+}
+
+void Scheduler::demoteLeaderDisplay() {
+ // No need to lock for reads on kMainThreadContext.
+ if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) {
+ leaderPtr->stopIdleTimer();
+ leaderPtr->clearIdleTimerCallbacks();
+ }
+
+ // Clear state that depends on the leader's RefreshRateSelector.
+ std::scoped_lock lock(mPolicyLock);
+ mPolicy = {};
}
template <typename S, typename T>
@@ -660,23 +650,29 @@
bool frameRateOverridesChanged;
{
- std::lock_guard<std::mutex> lock(mPolicyLock);
+ std::scoped_lock lock(mPolicyLock);
auto& currentState = mPolicy.*statePtr;
if (currentState == newState) return {};
currentState = std::forward<T>(newState);
- auto modeChoices = chooseDisplayModes();
-
- // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest to go
- // through. Fix this by tracking per-display Scheduler::Policy and timers.
+ DisplayModeChoiceMap modeChoices;
DisplayModePtr modePtr;
- std::tie(modePtr, consideredSignals) =
- modeChoices.get(*mLeaderDisplayId)
- .transform([](const DisplayModeChoice& choice) {
- return std::make_pair(choice.modePtr, choice.consideredSignals);
- })
- .value();
+ {
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ modeChoices = chooseDisplayModes();
+
+ // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest
+ // to go through. Fix this by tracking per-display Scheduler::Policy and timers.
+ std::tie(modePtr, consideredSignals) =
+ modeChoices.get(*mLeaderDisplayId)
+ .transform([](const DisplayModeChoice& choice) {
+ return std::make_pair(choice.modePtr, choice.consideredSignals);
+ })
+ .value();
+ }
modeRequests.reserve(modeChoices.size());
for (auto& [id, choice] : modeChoices) {
@@ -807,7 +803,7 @@
// Make sure the stored mode is up to date.
if (mPolicy.mode) {
const auto ranking =
- holdRefreshRateSelector()
+ leaderSelectorPtr()
->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
.ranking;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 04f3b69..fb23071 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -22,7 +22,6 @@
#include <future>
#include <memory>
#include <mutex>
-#include <optional>
#include <unordered_map>
#include <utility>
@@ -33,6 +32,8 @@
#include <ui/GraphicTypes.h>
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#include <ftl/fake_guard.h>
+#include <ftl/optional.h>
#include <scheduler/Features.h>
#include <scheduler/Time.h>
#include <ui/DisplayId.h>
@@ -108,12 +109,15 @@
void startTimers();
- using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
- void setRefreshRateSelector(RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
- EXCLUDES(mRefreshRateSelectorLock);
+ // TODO(b/241285191): Remove this API by promoting leader in onScreen{Acquired,Released}.
+ void setLeaderDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
- void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr);
- void unregisterDisplay(PhysicalDisplayId);
+ using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
+
+ void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+ void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
void run();
@@ -165,7 +169,7 @@
// Otherwise, if hardware vsync is not already enabled then this method will
// no-op.
void resyncToHardwareVsync(bool makeAvailable, Fps refreshRate);
- void resync() EXCLUDES(mRefreshRateSelectorLock);
+ void resync() EXCLUDES(mDisplayLock);
void forceNextResync() { mLastResyncTime = 0; }
// Passes a vsync sample to VsyncController. periodFlushed will be true if
@@ -176,14 +180,14 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
- void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType)
- EXCLUDES(mRefreshRateSelectorLock);
+ void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType)
+ EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
void setDefaultFrameRateCompatibility(Layer*);
void deregisterLayer(Layer*);
// Detects content using layer history, and selects a matching refresh rate.
- void chooseRefreshRateForContent() EXCLUDES(mRefreshRateSelectorLock);
+ void chooseRefreshRateForContent() EXCLUDES(mDisplayLock);
void resetIdleTimer();
@@ -228,11 +232,10 @@
void setGameModeRefreshRateForUid(FrameRateOverride);
// Retrieves the overridden refresh rate for a given uid.
- std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateSelectorLock);
+ std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
- nsecs_t getVsyncPeriodFromRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) {
- std::scoped_lock lock(mRefreshRateSelectorLock);
- return mRefreshRateSelector->getActiveModePtr()->getFps().getPeriodNsecs();
+ nsecs_t getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
+ return leaderSelectorPtr()->getActiveModePtr()->getFps().getPeriodNsecs();
}
// Returns the framerate of the layer with the given sequence ID
@@ -255,21 +258,23 @@
sp<EventThreadConnection> createConnectionInternal(
EventThread*, EventRegistrationFlags eventRegistration = {});
- void bindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext, mRefreshRateSelectorLock);
-
- // Blocks until the timer thread exits. `mRefreshRateSelectorLock` must not be locked by the
- // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
- static void unbindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext)
- EXCLUDES(mRefreshRateSelectorLock);
-
// Update feature state machine to given state when corresponding timer resets or expires.
- void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateSelectorLock);
+ void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock);
void idleTimerCallback(TimerState);
void touchTimerCallback(TimerState);
void displayPowerTimerCallback(TimerState);
void setVsyncPeriod(nsecs_t period);
+ // Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new
+ // `mLeaderDisplayId` is never `std::nullopt`.
+ void promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt = std::nullopt)
+ REQUIRES(kMainThreadContext, mDisplayLock);
+
+ // Blocks until the leader's idle timer thread exits. `mDisplayLock` must not be locked by the
+ // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
+ void demoteLeaderDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
+
struct Policy;
// Sets the S state of the policy to the T value under mPolicyLock, and chooses a display mode
@@ -296,23 +301,20 @@
};
using DisplayModeChoiceMap = display::PhysicalDisplayMap<PhysicalDisplayId, DisplayModeChoice>;
- DisplayModeChoiceMap chooseDisplayModes() const REQUIRES(mPolicyLock);
+
+ // See mDisplayLock for thread safety.
+ DisplayModeChoiceMap chooseDisplayModes() const
+ REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext);
GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
- void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateSelectorLock);
+ void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
- android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
- EXCLUDES(mRefreshRateSelectorLock);
+ android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
- RefreshRateSelectorPtr holdRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) {
- std::scoped_lock lock(mRefreshRateSelectorLock);
- return mRefreshRateSelector;
- }
-
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
sp<EventThreadConnection> connection;
@@ -342,10 +344,34 @@
ISchedulerCallback& mSchedulerCallback;
+ // mDisplayLock may be locked while under mPolicyLock.
mutable std::mutex mPolicyLock;
- display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors;
- std::optional<PhysicalDisplayId> mLeaderDisplayId;
+ // Only required for reads outside kMainThreadContext. kMainThreadContext is the only writer, so
+ // must lock for writes but not reads. See also mPolicyLock for locking order.
+ mutable std::mutex mDisplayLock;
+
+ display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors
+ GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
+
+ ftl::Optional<PhysicalDisplayId> mLeaderDisplayId GUARDED_BY(mDisplayLock)
+ GUARDED_BY(kMainThreadContext);
+
+ RefreshRateSelectorPtr leaderSelectorPtr() const EXCLUDES(mDisplayLock) {
+ std::scoped_lock lock(mDisplayLock);
+ return leaderSelectorPtrLocked();
+ }
+
+ RefreshRateSelectorPtr leaderSelectorPtrLocked() const REQUIRES(mDisplayLock) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ const RefreshRateSelectorPtr noLeader;
+ return mLeaderDisplayId
+ .and_then([this](PhysicalDisplayId leaderId)
+ REQUIRES(mDisplayLock, kMainThreadContext) {
+ return mRefreshRateSelectors.get(leaderId);
+ })
+ .value_or(std::cref(noLeader));
+ }
struct Policy {
// Policy for choosing the display mode.
@@ -367,10 +393,6 @@
std::optional<ModeChangedParams> cachedModeChangedParams;
} mPolicy GUARDED_BY(mPolicyLock);
- // TODO(b/255635821): Remove this by instead looking up the `mLeaderDisplayId` selector.
- mutable std::mutex mRefreshRateSelectorLock;
- RefreshRateSelectorPtr mRefreshRateSelector GUARDED_BY(mRefreshRateSelectorLock);
-
std::mutex mVsyncTimelineLock;
std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
GUARDED_BY(mVsyncTimelineLock);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 581dd13..9f26129 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -180,6 +180,7 @@
using base::StringAppendF;
using display::PhysicalDisplay;
using display::PhysicalDisplays;
+using frontend::TransactionHandler;
using gui::DisplayInfo;
using gui::GameMode;
using gui::IDisplayEventConnection;
@@ -2190,14 +2191,13 @@
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
- std::vector<sp<Layer>> layers;
+ std::vector<Layer*> layers;
mDrawingState.traverseInZOrder([&refreshArgs, &layers](Layer* layer) {
- auto strongLayer = sp<Layer>::fromExisting(layer);
if (auto layerFE = layer->getCompositionEngineLayerFE()) {
layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
refreshArgs.layers.push_back(layerFE);
- layers.push_back(std::move(strongLayer));
+ layers.push_back(layer);
}
});
refreshArgs.blursAreExpensive = mBlursAreExpensive;
@@ -2229,8 +2229,8 @@
{
std::vector<LayerSnapshotGuard> layerSnapshotGuards;
- for (auto& layer : layers) {
- layerSnapshotGuards.emplace_back(layer.get());
+ for (Layer* layer : layers) {
+ layerSnapshotGuards.emplace_back(layer);
}
mCompositionEngine->present(refreshArgs);
}
@@ -2959,18 +2959,16 @@
displaySurface, producer);
if (mScheduler && !display->isVirtual()) {
- auto selectorPtr = display->holdRefreshRateSelector();
-
- // Display modes are reloaded on hotplug reconnect.
- if (display->isPrimary()) {
+ const auto displayId = display->getPhysicalId();
+ {
// TODO(b/241285876): Annotate `processDisplayAdded` instead.
ftl::FakeGuard guard(kMainThreadContext);
- mScheduler->setRefreshRateSelector(selectorPtr);
+
+ // For hotplug reconnect, renew the registration since display modes have been reloaded.
+ mScheduler->registerDisplay(displayId, display->holdRefreshRateSelector());
}
- const auto displayId = display->getPhysicalId();
- mScheduler->registerDisplay(displayId, std::move(selectorPtr));
- dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
+ dispatchDisplayHotplugEvent(displayId, true);
}
mDisplays.try_emplace(displayToken, std::move(display));
@@ -3126,6 +3124,10 @@
const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
if (displayTransactionNeeded) {
processDisplayChangesLocked();
+ mFrontEndDisplayInfos.clear();
+ for (const auto& [_, display] : mDisplays) {
+ mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo());
+ }
}
mForceTransactionDisplayChange = displayTransactionNeeded;
@@ -3295,29 +3297,6 @@
void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
std::vector<DisplayInfo>& outDisplayInfos) {
- display::DisplayMap<ui::LayerStack, DisplayDevice::InputInfo> displayInputInfos;
-
- for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
- const auto layerStack = display->getLayerStack();
- const auto info = display->getInputInfo();
-
- const auto [it, emplaced] = displayInputInfos.try_emplace(layerStack, info);
- if (emplaced) {
- continue;
- }
-
- // If the layer stack is mirrored on multiple displays, the first display that is configured
- // to receive input takes precedence.
- auto& otherInfo = it->second;
- if (otherInfo.receivesInput) {
- ALOGW_IF(display->receivesInput(),
- "Multiple displays claim to accept input for the same layer stack: %u",
- layerStack.id);
- } else {
- otherInfo = info;
- }
- }
-
static size_t sNumWindowInfos = 0;
outWindowInfos.reserve(sNumWindowInfos);
sNumWindowInfos = 0;
@@ -3325,8 +3304,8 @@
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (!layer->needsInputInfo()) return;
- const auto opt = displayInputInfos.get(layer->getLayerStack())
- .transform([](const DisplayDevice::InputInfo& info) {
+ const auto opt = mFrontEndDisplayInfos.get(layer->getLayerStack())
+ .transform([](const frontend::DisplayInfo& info) {
return Layer::InputDisplayArgs{&info.transform, info.isSecure};
});
@@ -3335,8 +3314,8 @@
sNumWindowInfos = outWindowInfos.size();
- outDisplayInfos.reserve(displayInputInfos.size());
- for (const auto& [_, info] : displayInputInfos) {
+ outDisplayInfos.reserve(mFrontEndDisplayInfos.size());
+ for (const auto& [_, info] : mFrontEndDisplayInfos) {
outDisplayInfos.push_back(info.info);
}
}
@@ -3351,12 +3330,7 @@
std::vector<LayerSnapshotGuard> layerSnapshotGuards;
mDrawingState.traverse([&layerSnapshotGuards](Layer* layer) {
- auto strongLayer = sp<Layer>::fromExisting(layer);
- const LayerSnapshot* snapshot = layer->getLayerSnapshot();
- if (!snapshot) {
- LOG_ALWAYS_FATAL("Layer snapshot unexpectedly null");
- }
- if (snapshot->compositionType ==
+ if (layer->getLayerSnapshot()->compositionType ==
aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
layer->updateSnapshot(false /* updateGeometry */);
layerSnapshotGuards.emplace_back(layer);
@@ -3429,9 +3403,7 @@
!getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
features |= Feature::kPresentFences;
}
-
- auto selectorPtr = display->holdRefreshRateSelector();
- if (selectorPtr->kernelIdleTimerController()) {
+ if (display->refreshRateSelector().kernelIdleTimerController()) {
features |= Feature::kKernelIdleTimer;
}
@@ -3439,8 +3411,7 @@
static_cast<ISchedulerCallback&>(*this),
features);
mScheduler->createVsyncSchedule(features);
- mScheduler->setRefreshRateSelector(selectorPtr);
- mScheduler->registerDisplay(display->getPhysicalId(), std::move(selectorPtr));
+ mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
setVsyncEnabled(false);
mScheduler->startTimers();
@@ -6505,7 +6476,6 @@
layers.reserve(layerCount);
std::unordered_set<compositionengine::LayerFE*> filterForScreenshot;
traverseLayers([&](Layer* layer) {
- auto strongLayer = sp<Layer>::fromExisting(layer);
captureResults.capturedHdrLayers |= isHdrLayer(layer);
// Layer::prepareClientComposition uses the layer's snapshot to populate the resulting
// LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are
@@ -7001,9 +6971,11 @@
}
mActiveDisplayId = activeDisplay->getPhysicalId();
activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
+
updateInternalDisplayVsyncLocked(activeDisplay);
mScheduler->setModeChangePending(false);
- mScheduler->setRefreshRateSelector(activeDisplay->holdRefreshRateSelector());
+ mScheduler->setLeaderDisplay(mActiveDisplayId);
+
onActiveDisplaySizeChanged(activeDisplay);
mActiveDisplayTransformHint = activeDisplay->getTransformHint();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0706598..50e8e73 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -66,6 +66,7 @@
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
#include "FlagManager.h"
+#include "FrontEnd/DisplayInfo.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/TransactionHandler.h"
#include "LayerVector.h"
@@ -120,6 +121,7 @@
class ScreenCapturer;
class WindowInfosListenerInvoker;
+using frontend::TransactionHandler;
using gui::CaptureArgs;
using gui::DisplayCaptureArgs;
using gui::IRegionSamplingListener;
@@ -464,8 +466,7 @@
typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
void modulateVsync(Handler handler, Args... args) {
if (const auto config = (*mVsyncModulator.*handler)(args...)) {
- const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateSelector();
- setVsyncConfig(*config, vsyncPeriod);
+ setVsyncConfig(*config, mScheduler->getLeaderVsyncPeriod());
}
}
@@ -928,7 +929,8 @@
const sp<compositionengine::DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext);
- void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
+ void processDisplayRemoved(const wp<IBinder>& displayToken)
+ REQUIRES(mStateLock, kMainThreadContext);
void processDisplayChanged(const wp<IBinder>& displayToken,
const DisplayDeviceState& currentState,
const DisplayDeviceState& drawingState)
@@ -1366,6 +1368,7 @@
} mPowerHintSessionMode;
TransactionHandler mTransactionHandler;
+ display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index ee5392e..577f84e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -230,7 +230,10 @@
ISchedulerCallback& callback)
: Scheduler(*this, callback, Feature::kContentDetection) {
mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
- setRefreshRateSelector(std::move(selectorPtr));
+
+ const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
+ selectorPtr->getActiveMode().getPhysicalDisplayId());
+ registerDisplay(displayId, std::move(selectorPtr));
}
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -242,7 +245,7 @@
auto &mutableLayerHistory() { return mLayerHistory; }
- auto refreshRateSelector() { return holdRefreshRateSelector(); }
+ auto refreshRateSelector() { return leaderSelectorPtr(); }
void replaceTouchTimer(int64_t millis) {
if (mTouchTimer) {
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index ea4666e..8e333a3 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -190,8 +190,10 @@
sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
ASSERT_EQ(1u, mScheduler->layerHistorySize());
- mScheduler->setRefreshRateSelector(
- std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId()));
+ // Replace `mSelector` with a new `RefreshRateSelector` that has different display modes.
+ mScheduler->registerDisplay(kDisplayId1,
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+ kDisplay1Mode60->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -234,11 +236,9 @@
}
TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
- const auto selectorPtr =
- std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId());
-
- mScheduler->registerDisplay(kDisplayId1, selectorPtr);
- mScheduler->setRefreshRateSelector(selectorPtr);
+ mScheduler->registerDisplay(kDisplayId1,
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+ kDisplay1Mode60->getId()));
const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ba214d5..3f8fe0d 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -43,7 +43,10 @@
ISchedulerCallback& callback)
: Scheduler(*this, callback, Feature::kContentDetection) {
mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
- setRefreshRateSelector(std::move(selectorPtr));
+
+ const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
+ selectorPtr->getActiveMode().getPhysicalDisplayId());
+ registerDisplay(displayId, std::move(selectorPtr));
ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
// Execute task to prevent broken promise exception on destruction.
@@ -67,12 +70,27 @@
auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
- auto refreshRateSelector() { return holdRefreshRateSelector(); }
- bool hasRefreshRateSelectors() const { return !mRefreshRateSelectors.empty(); }
+ auto refreshRateSelector() { return leaderSelectorPtr(); }
- void setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) {
+ const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS {
+ return mRefreshRateSelectors;
+ }
+
+ bool hasRefreshRateSelectors() const { return !refreshRateSelectors().empty(); }
+
+ void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
ftl::FakeGuard guard(kMainThreadContext);
- return Scheduler::setRefreshRateSelector(std::move(selectorPtr));
+ Scheduler::registerDisplay(displayId, std::move(selectorPtr));
+ }
+
+ void unregisterDisplay(PhysicalDisplayId displayId) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ Scheduler::unregisterDisplay(displayId);
+ }
+
+ void setLeaderDisplay(PhysicalDisplayId displayId) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ Scheduler::setLeaderDisplay(displayId);
}
auto& mutableLayerHistory() { return mLayerHistory; }
@@ -115,14 +133,13 @@
using Scheduler::DisplayModeChoice;
using Scheduler::DisplayModeChoiceMap;
- DisplayModeChoiceMap chooseDisplayModes() {
- std::lock_guard<std::mutex> lock(mPolicyLock);
+ DisplayModeChoiceMap chooseDisplayModes() NO_THREAD_SAFETY_ANALYSIS {
return Scheduler::chooseDisplayModes();
}
void dispatchCachedReportedMode() {
std::lock_guard<std::mutex> lock(mPolicyLock);
- return Scheduler::dispatchCachedReportedMode();
+ Scheduler::dispatchCachedReportedMode();
}
void clearCachedReportedMode() {
@@ -131,7 +148,7 @@
}
void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
- return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
+ Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
}
private:
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index dbb8bd8..c15b3c8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -843,33 +843,36 @@
sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
- if (mFlinger.scheduler()) {
- mFlinger.scheduler()->registerDisplay(display->getPhysicalId(),
- display->holdRefreshRateSelector());
- }
DisplayDeviceState state;
state.isSecure = mCreationArgs.isSecure;
if (mConnectionType) {
LOG_ALWAYS_FATAL_IF(!displayId);
- const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
- LOG_ALWAYS_FATAL_IF(!physicalId);
+ const auto physicalIdOpt = PhysicalDisplayId::tryCast(*displayId);
+ LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
+ const auto physicalId = *physicalIdOpt;
+
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
const auto activeMode = modes.get(activeModeId);
LOG_ALWAYS_FATAL_IF(!activeMode);
- state.physical = {.id = *physicalId,
+ state.physical = {.id = physicalId,
.hwcDisplayId = *mHwcDisplayId,
.activeMode = activeMode->get()};
const auto it = mFlinger.mutablePhysicalDisplays()
- .emplace_or_replace(*physicalId, mDisplayToken, *physicalId,
+ .emplace_or_replace(physicalId, mDisplayToken, physicalId,
*mConnectionType, std::move(modes),
ui::ColorModes(), std::nullopt)
.first;
+ if (mFlinger.scheduler()) {
+ mFlinger.scheduler()->registerDisplay(physicalId,
+ display->holdRefreshRateSelector());
+ }
+
display->setActiveMode(activeModeId, it->second.snapshot());
}
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 488d4a9..d84698f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -42,6 +42,8 @@
using testing::Return;
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using frontend::TransactionHandler;
+
constexpr nsecs_t TRANSACTION_TIMEOUT = s2ns(5);
class TransactionApplicationTest : public testing::Test {
public:
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index ba98696..40cf9fb 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -49,7 +49,13 @@
* in VkBindImageMemorySwapchainInfoKHR will be additionally chained to the
* pNext chain of VkBindImageMemoryInfo and passed down to the driver.
*/
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9
+ *
+ * This version of the extension is largely designed to clean up the mix of
+ * GrallocUsage and GrallocUsage2
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
#define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
@@ -61,6 +67,8 @@
VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID \
VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
+#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID \
+ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 3)
/* clang-format off */
typedef enum VkSwapchainImageUsageFlagBitsANDROID {
@@ -90,6 +98,7 @@
* format: gralloc format requested when the buffer was allocated
* usage: gralloc usage requested when the buffer was allocated
* usage2: gralloc usage requested when the buffer was allocated
+ * usage3: gralloc usage requested when the buffer was allocated
*/
typedef struct {
VkStructureType sType;
@@ -98,7 +107,8 @@
int stride;
int format;
int usage; /* DEPRECATED in SPEC_VERSION 6 */
- VkNativeBufferUsage2ANDROID usage2; /* ADDED in SPEC_VERSION 6 */
+ VkNativeBufferUsage2ANDROID usage2; /* DEPRECATED in SPEC_VERSION 9 */
+ uint64_t usage3; /* ADDED in SPEC_VERSION 9 */
} VkNativeBufferANDROID;
/*
@@ -127,6 +137,21 @@
VkBool32 sharedImage;
} VkPhysicalDevicePresentationPropertiesANDROID;
+/*
+ * struct VkGrallocUsageInfoANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * format: value specifying the format the image will be created with
+ * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
+ */
+typedef struct {
+ VkStructureType sType;
+ const void* pNext;
+ VkFormat format;
+ VkImageUsageFlags imageUsage;
+} VkGrallocUsageInfoANDROID;
+
/* DEPRECATED in SPEC_VERSION 6 */
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(
VkDevice device,
@@ -134,7 +159,7 @@
VkImageUsageFlags imageUsage,
int* grallocUsage);
-/* ADDED in SPEC_VERSION 6 */
+/* DEPRECATED in SPEC_VERSION 9 */
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(
VkDevice device,
VkFormat format,
@@ -143,6 +168,12 @@
uint64_t* grallocConsumerUsage,
uint64_t* grallocProducerUsage);
+/* ADDED in SPEC_VERSION 9 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage3ANDROID)(
+ VkDevice device,
+ const VkGrallocUsageInfoANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage);
+
typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(
VkDevice device,
VkImage image,
@@ -167,7 +198,7 @@
int* grallocUsage
);
-/* ADDED in SPEC_VERSION 6 */
+/* DEPRECATED in SPEC_VERSION 9 */
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
VkDevice device,
VkFormat format,
@@ -177,6 +208,13 @@
uint64_t* grallocProducerUsage
);
+/* ADDED in SPEC_VERSION 9 */
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage3ANDROID(
+ VkDevice device,
+ const VkGrallocUsageInfoANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage
+);
+
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
VkDevice device,
VkImage image,
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 7664518..4927150 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1027,6 +1027,39 @@
}
}
+bool GetAndroidNativeBufferSpecVersion9Support(
+ VkPhysicalDevice physicalDevice) {
+ const InstanceData& data = GetData(physicalDevice);
+
+ // Call to get propertyCount
+ uint32_t propertyCount = 0;
+ ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
+ VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+ physicalDevice, nullptr, &propertyCount, nullptr);
+ ATRACE_END();
+
+ // Call to enumerate properties
+ std::vector<VkExtensionProperties> properties(propertyCount);
+ ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
+ result = data.driver.EnumerateDeviceExtensionProperties(
+ physicalDevice, nullptr, &propertyCount, properties.data());
+ ATRACE_END();
+
+ for (uint32_t i = 0; i < propertyCount; i++) {
+ auto& prop = properties[i];
+
+ if (strcmp(prop.extensionName,
+ VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
+ continue;
+
+ if (prop.specVersion >= 9) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
VkResult EnumerateDeviceExtensionProperties(
VkPhysicalDevice physicalDevice,
const char* pLayerName,
@@ -1061,6 +1094,37 @@
VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION});
}
+ // Conditionally add VK_EXT_IMAGE_COMPRESSION_CONTROL* if feature and ANB
+ // support is provided by the driver
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT
+ swapchainCompFeats = {};
+ swapchainCompFeats.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT;
+ swapchainCompFeats.pNext = nullptr;
+ VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
+ imageCompFeats.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
+ imageCompFeats.pNext = &swapchainCompFeats;
+
+ VkPhysicalDeviceFeatures2 feats2 = {};
+ feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ feats2.pNext = &imageCompFeats;
+
+ GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+
+ bool anb9 = GetAndroidNativeBufferSpecVersion9Support(physicalDevice);
+
+ if (anb9 && imageCompFeats.imageCompressionControl) {
+ loader_extensions.push_back(
+ {VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME,
+ VK_EXT_IMAGE_COMPRESSION_CONTROL_SPEC_VERSION});
+ }
+ if (anb9 && swapchainCompFeats.imageCompressionControlSwapchain) {
+ loader_extensions.push_back(
+ {VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME,
+ VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_SPEC_VERSION});
+ }
+
// enumerate our extensions first
if (!pLayerName && pProperties) {
uint32_t count = std::min(
@@ -1254,15 +1318,18 @@
return VK_ERROR_INCOMPATIBLE_DRIVER;
}
- // sanity check ANDROID_native_buffer implementation, whose set of
+ // Confirming ANDROID_native_buffer implementation, whose set of
// entrypoints varies according to the spec version.
if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) &&
!data->driver.GetSwapchainGrallocUsageANDROID &&
- !data->driver.GetSwapchainGrallocUsage2ANDROID) {
- ALOGE("Driver's implementation of ANDROID_native_buffer is broken;"
- " must expose at least one of "
- "vkGetSwapchainGrallocUsageANDROID or "
- "vkGetSwapchainGrallocUsage2ANDROID");
+ !data->driver.GetSwapchainGrallocUsage2ANDROID &&
+ !data->driver.GetSwapchainGrallocUsage3ANDROID) {
+ ALOGE(
+ "Driver's implementation of ANDROID_native_buffer is broken;"
+ " must expose at least one of "
+ "vkGetSwapchainGrallocUsageANDROID or "
+ "vkGetSwapchainGrallocUsage2ANDROID or "
+ "vkGetSwapchainGrallocUsage3ANDROID");
data->driver.DestroyDevice(dev, pAllocator);
FreeDeviceData(data, data_allocator);
@@ -1441,10 +1508,83 @@
if (driver.GetPhysicalDeviceFeatures2) {
driver.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
+ } else {
+ driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
+ }
+
+ // Conditionally add imageCompressionControlSwapchain if
+ // imageCompressionControl is supported Check for imageCompressionControl in
+ // the pChain
+ bool imageCompressionControl = false;
+ bool imageCompressionControlInChain = false;
+ bool imageCompressionControlSwapchainInChain = false;
+ VkPhysicalDeviceFeatures2* pFeats = pFeatures;
+ while (pFeats) {
+ switch (pFeats->sType) {
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT: {
+ const VkPhysicalDeviceImageCompressionControlFeaturesEXT*
+ compressionFeat = reinterpret_cast<
+ const VkPhysicalDeviceImageCompressionControlFeaturesEXT*>(
+ pFeats);
+ imageCompressionControl =
+ compressionFeat->imageCompressionControl;
+ imageCompressionControlInChain = true;
+ } break;
+
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+ imageCompressionControlSwapchainInChain = true;
+ } break;
+
+ default:
+ break;
+ }
+ pFeats = reinterpret_cast<VkPhysicalDeviceFeatures2*>(pFeats->pNext);
+ }
+
+ if (!imageCompressionControlSwapchainInChain) {
return;
}
- driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
+ // If not in pchain, explicitly query for imageCompressionControl
+ if (!imageCompressionControlInChain) {
+ VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
+ imageCompFeats.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
+ imageCompFeats.pNext = nullptr;
+
+ VkPhysicalDeviceFeatures2 feats2 = {};
+ feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ feats2.pNext = &imageCompFeats;
+
+ if (driver.GetPhysicalDeviceFeatures2) {
+ driver.GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+ } else {
+ driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, &feats2);
+ }
+
+ imageCompressionControl = imageCompFeats.imageCompressionControl;
+ }
+
+ // Only enumerate imageCompressionControlSwapchin if imageCompressionControl
+ if (imageCompressionControl) {
+ pFeats = pFeatures;
+ while (pFeats) {
+ switch (pFeats->sType) {
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*
+ compressionFeat = reinterpret_cast<
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>(
+ pFeats);
+ compressionFeat->imageCompressionControlSwapchain = true;
+ } break;
+
+ default:
+ break;
+ }
+ pFeats =
+ reinterpret_cast<VkPhysicalDeviceFeatures2*>(pFeats->pNext);
+ }
+ }
}
void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 14c516b..4d2bbd6 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -107,6 +107,8 @@
VkPhysicalDevice physicalDevice,
VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties);
+bool GetAndroidNativeBufferSpecVersion9Support(VkPhysicalDevice physicalDevice);
+
VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance,
const char* pName);
VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device,
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index b436db1..de98aa7 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -496,6 +496,13 @@
nullptr,
},
{
+ "vkGetSwapchainGrallocUsage3ANDROID",
+ ProcHook::DEVICE,
+ ProcHook::ANDROID_native_buffer,
+ nullptr,
+ nullptr,
+ },
+ {
"vkGetSwapchainGrallocUsageANDROID",
ProcHook::DEVICE,
ProcHook::ANDROID_native_buffer,
@@ -664,6 +671,7 @@
INIT_PROC(false, dev, GetDeviceQueue2);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
+ INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage3ANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
// clang-format on
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 079f9cc..2f60086 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -123,6 +123,7 @@
PFN_vkGetDeviceQueue2 GetDeviceQueue2;
PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
+ PFN_vkGetSwapchainGrallocUsage3ANDROID GetSwapchainGrallocUsage3ANDROID;
PFN_vkAcquireImageANDROID AcquireImageANDROID;
PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
// clang-format on
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index f49e8f3..b189c68 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -178,6 +178,7 @@
vkGetImageSparseMemoryRequirements;
vkGetImageSparseMemoryRequirements2; # introduced=28
vkGetImageSubresourceLayout;
+ vkGetImageSubresourceLayout2EXT; # introduced=UpsideDownCake
vkGetInstanceProcAddr;
vkGetMemoryAndroidHardwareBufferANDROID; # introduced=28
vkGetPhysicalDeviceExternalBufferProperties; # introduced=28
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 87b3a89..475bc40 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -715,6 +715,17 @@
capabilities->minImageExtent = VkExtent2D{1, 1};
capabilities->maxImageExtent = VkExtent2D{4096, 4096};
+ if (capabilities->maxImageExtent.height <
+ capabilities->currentExtent.height) {
+ capabilities->maxImageExtent.height =
+ capabilities->currentExtent.height;
+ }
+
+ if (capabilities->maxImageExtent.width <
+ capabilities->currentExtent.width) {
+ capabilities->maxImageExtent.width = capabilities->currentExtent.width;
+ }
+
capabilities->maxImageArrayLayers = 1;
capabilities->supportedTransforms = kSupportedTransforms;
@@ -743,7 +754,6 @@
const InstanceData& instance_data = GetData(pdev);
- bool wide_color_support = false;
uint64_t consumer_usage = 0;
bool colorspace_ext =
instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
@@ -754,27 +764,15 @@
if (!surfaceless_enabled) {
return VK_ERROR_SURFACE_LOST_KHR;
}
- // Support for VK_GOOGLE_surfaceless_query. The EGL loader
- // unconditionally supports wide color formats, even if they will cause
- // a SurfaceFlinger fallback. Based on that, wide_color_support will be
- // set to true in this case.
- wide_color_support = true;
+ // Support for VK_GOOGLE_surfaceless_query.
// TODO(b/203826952): research proper value; temporarily use the
// values seen on Pixel
consumer_usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY;
} else {
Surface& surface = *SurfaceFromHandle(surface_handle);
- int err = native_window_get_wide_color_support(surface.window.get(),
- &wide_color_support);
- if (err) {
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- ALOGV("wide_color_support is: %d", wide_color_support);
-
consumer_usage = surface.consumer_usage;
}
- wide_color_support = wide_color_support && colorspace_ext;
AHardwareBuffer_Desc desc = {};
desc.width = 1;
@@ -796,9 +794,6 @@
VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT});
all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT});
- }
-
- if (wide_color_support) {
all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
all_formats.emplace_back(VkSurfaceFormatKHR{
@@ -828,8 +823,6 @@
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
VK_COLOR_SPACE_PASS_THROUGH_EXT});
- }
- if (wide_color_support) {
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT});
@@ -848,8 +841,6 @@
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_COLOR_SPACE_PASS_THROUGH_EXT});
- }
- if (wide_color_support) {
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
@@ -948,11 +939,60 @@
surface_formats.data());
if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
+ const auto& driver = GetData(physicalDevice).driver;
+
// marshal results individually due to stride difference.
- // completely ignore any chained extension structs.
uint32_t formats_to_marshal = *pSurfaceFormatCount;
for (uint32_t i = 0u; i < formats_to_marshal; i++) {
pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+
+ // Query the compression properties for the surface format
+ if (pSurfaceFormats[i].pNext) {
+ VkImageCompressionPropertiesEXT* surfaceCompressionProps =
+ reinterpret_cast<VkImageCompressionPropertiesEXT*>(
+ pSurfaceFormats[i].pNext);
+
+ if (surfaceCompressionProps &&
+ driver.GetPhysicalDeviceImageFormatProperties2KHR) {
+ VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {};
+ imageFormatInfo.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
+ imageFormatInfo.format =
+ pSurfaceFormats[i].surfaceFormat.format;
+ imageFormatInfo.pNext = nullptr;
+
+ VkImageCompressionControlEXT compressionControl = {};
+ compressionControl.sType =
+ VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT;
+ compressionControl.pNext = imageFormatInfo.pNext;
+
+ imageFormatInfo.pNext = &compressionControl;
+
+ VkImageCompressionPropertiesEXT compressionProps = {};
+ compressionProps.sType =
+ VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT;
+ compressionProps.pNext = nullptr;
+
+ VkImageFormatProperties2KHR imageFormatProps = {};
+ imageFormatProps.sType =
+ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
+ imageFormatProps.pNext = &compressionProps;
+
+ VkResult compressionRes =
+ driver.GetPhysicalDeviceImageFormatProperties2KHR(
+ physicalDevice, &imageFormatInfo,
+ &imageFormatProps);
+ if (compressionRes == VK_SUCCESS) {
+ surfaceCompressionProps->imageCompressionFlags =
+ compressionProps.imageCompressionFlags;
+ surfaceCompressionProps
+ ->imageCompressionFixedRateFlags =
+ compressionProps.imageCompressionFixedRateFlags;
+ } else {
+ return compressionRes;
+ }
+ }
+ }
}
}
@@ -1370,8 +1410,48 @@
num_images = 1;
}
+ void* usage_info_pNext = nullptr;
+ VkImageCompressionControlEXT image_compression = {};
uint64_t native_usage = 0;
- if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+ if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
+ VkGrallocUsageInfoANDROID gralloc_usage_info = {};
+ gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
+ gralloc_usage_info.format = create_info->imageFormat;
+ gralloc_usage_info.imageUsage = create_info->imageUsage;
+
+ // Look through the pNext chain for an image compression control struct
+ // if one is found AND the appropriate extensions are enabled,
+ // append it to be the gralloc usage pNext chain
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+ create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(
+ create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
+
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+ gralloc_usage_info.pNext = usage_info_pNext;
+
+ result = dispatch.GetSwapchainGrallocUsage3ANDROID(
+ device, &gralloc_usage_info, &native_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
uint64_t consumer_usage, producer_usage;
ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
result = dispatch.GetSwapchainGrallocUsage2ANDROID(
@@ -1383,7 +1463,7 @@
return VK_ERROR_SURFACE_LOST_KHR;
}
native_usage =
- convertGralloc1ToBufferUsage(consumer_usage, producer_usage);
+ convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
} else if (dispatch.GetSwapchainGrallocUsageANDROID) {
ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
int32_t legacy_usage = 0;
@@ -1437,7 +1517,7 @@
#pragma clang diagnostic ignored "-Wold-style-cast"
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID,
#pragma clang diagnostic pop
- .pNext = nullptr,
+ .pNext = usage_info_pNext,
.usage = swapchain_image_usage,
};
VkNativeBufferANDROID image_native_buffer = {
@@ -1495,6 +1575,7 @@
android_convertGralloc0To1Usage(int(img.buffer->usage),
&image_native_buffer.usage2.producer,
&image_native_buffer.usage2.consumer);
+ image_native_buffer.usage3 = img.buffer->usage;
ATRACE_BEGIN("CreateImage");
result =
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 3c91150..f998b1a 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -948,6 +948,17 @@
return VK_SUCCESS;
}
+VkResult GetSwapchainGrallocUsage3ANDROID(
+ VkDevice,
+ const VkGrallocUsageInfoANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage) {
+ // The null driver never reads or writes the gralloc buffer
+ ALOGV("TODO: vk%s - grallocUsageInfo->format:%i", __FUNCTION__,
+ grallocUsageInfo->format);
+ *grallocUsage = 0;
+ return VK_SUCCESS;
+}
+
VkResult AcquireImageANDROID(VkDevice,
VkImage,
int fence,
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index f6dcf09..0cb7bd3 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -261,6 +261,7 @@
{"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
{"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
{"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
+ {"vkGetSwapchainGrallocUsage3ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage3ANDROID>(GetSwapchainGrallocUsage3ANDROID))},
{"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
{"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
{"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 3e003e3..5c7fea0 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -209,6 +209,7 @@
VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage3ANDROID(VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage);
VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index 4176509..c25c6cb 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -69,6 +69,7 @@
_OPTIONAL_COMMANDS = [
'vkGetSwapchainGrallocUsageANDROID',
'vkGetSwapchainGrallocUsage2ANDROID',
+ 'vkGetSwapchainGrallocUsage3ANDROID',
]
# Dict for mapping dispatch table to a type.