Merge "Add metrics for logging SurfaceControl events" into main
diff --git a/Android.bp b/Android.bp
index 72311f0..13954ef 100644
--- a/Android.bp
+++ b/Android.bp
@@ -125,3 +125,9 @@
srcs: ["aidl/android/hardware/display/IDeviceProductInfoConstants.aidl"],
path: "aidl",
}
+
+dirgroup {
+ name: "trusty_dirgroup_frameworks_native",
+ dirs: ["libs/binder"],
+ visibility: ["//trusty/vendor/google/aosp/scripts"],
+}
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index b22cc2a..372008e 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -118,6 +118,12 @@
],
}
+prebuilt_etc {
+ name: "default_screenshot",
+ src: "res/default_screenshot.png",
+ filename_from_src: true,
+}
+
cc_binary {
name: "dumpstate",
defaults: ["dumpstate_defaults"],
@@ -130,6 +136,7 @@
required: [
"atrace",
"bugreport_procdump",
+ "default_screenshot",
"dmabuf_dump",
"ip",
"iptables",
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index 6f7fea3..ce7c55c 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -108,7 +108,7 @@
const uint32_t cap_syslog_mask = CAP_TO_MASK(CAP_SYSLOG);
const uint32_t cap_syslog_index = CAP_TO_INDEX(CAP_SYSLOG);
- bool has_cap_syslog = (capdata[cap_syslog_index].effective & cap_syslog_mask) != 0;
+ bool has_cap_syslog = (capdata[cap_syslog_index].permitted & cap_syslog_mask) != 0;
memset(&capdata, 0, sizeof(capdata));
if (has_cap_syslog) {
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index d427ecf..d24edc4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -206,6 +206,9 @@
static const std::string SHUTDOWN_CHECKPOINTS_DIR = "/data/system/shutdown-checkpoints/";
static const std::string SHUTDOWN_CHECKPOINTS_FILE_PREFIX = "checkpoints-";
+// File path to default screenshot image, that used when failed to capture the real screenshot.
+static const std::string DEFAULT_SCREENSHOT_PATH = "/system/etc/default_screenshot.png";
+
// TODO: temporary variables and functions used during C++ refactoring
#define RETURN_IF_USER_DENIED_CONSENT() \
@@ -765,10 +768,14 @@
bool copy_succeeded = android::os::CopyFileToFd(ds.screenshot_path_,
ds.options_->screenshot_fd.get());
- ds.options_->is_screenshot_copied = copy_succeeded;
if (copy_succeeded) {
android::os::UnlinkAndLogOnError(ds.screenshot_path_);
+ } else {
+ MYLOGE("Failed to copy screenshot to a permanent file.\n");
+ copy_succeeded = android::os::CopyFileToFd(DEFAULT_SCREENSHOT_PATH,
+ ds.options_->screenshot_fd.get());
}
+ ds.options_->is_screenshot_copied = copy_succeeded;
return android::binder::Status::ok();
}
@@ -3442,7 +3449,9 @@
// Do an early return if there were errors. We make an exception for consent
// timing out because it's possible the user got distracted. In this case the
// bugreport is not shared but made available for manual retrieval.
- MYLOGI("User denied consent. Returning\n");
+ MYLOGI("Bug report generation failed, this could have been due to"
+ " several reasons such as BR copy failed, user consent was"
+ " not grated etc. Returning\n");
return status;
}
if (status == Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
@@ -3729,12 +3738,16 @@
if (options_->do_screenshot &&
options_->screenshot_fd.get() != -1 &&
!options_->is_screenshot_copied) {
- copy_succeeded = android::os::CopyFileToFd(screenshot_path_,
+ bool is_screenshot_copied = android::os::CopyFileToFd(screenshot_path_,
options_->screenshot_fd.get());
- options_->is_screenshot_copied = copy_succeeded;
- if (copy_succeeded) {
+ if (is_screenshot_copied) {
android::os::UnlinkAndLogOnError(screenshot_path_);
+ } else {
+ MYLOGE("Failed to copy screenshot to a permanent file.\n");
+ is_screenshot_copied = android::os::CopyFileToFd(DEFAULT_SCREENSHOT_PATH,
+ options_->screenshot_fd.get());
}
+ options_->is_screenshot_copied = is_screenshot_copied;
}
}
return copy_succeeded ? Dumpstate::RunStatus::OK : Dumpstate::RunStatus::ERROR;
@@ -3825,7 +3838,7 @@
DurationReporter::~DurationReporter() {
if (!title_.empty()) {
float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC;
- if (elapsed >= .5f || verbose_) {
+ if (elapsed >= 1.0f || verbose_) {
MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed);
}
if (!logcat_only_) {
diff --git a/cmds/dumpstate/res/default_screenshot.png b/cmds/dumpstate/res/default_screenshot.png
new file mode 100644
index 0000000..10f36aa
--- /dev/null
+++ b/cmds/dumpstate/res/default_screenshot.png
Binary files differ
diff --git a/cmds/evemu-record/README.md b/cmds/evemu-record/README.md
index 5d16d51..cd28520 100644
--- a/cmds/evemu-record/README.md
+++ b/cmds/evemu-record/README.md
@@ -38,10 +38,10 @@
### Timestamp bases
By default, event timestamps are recorded relative to the time of the first event received during
-the recording. Passing `--timestamp-base=boot` causes the timestamps to be recorded relative to the
-system boot time instead. While this does not affect the playback of the recording, it can be useful
-for matching recorded events with other logs that use such timestamps, such as `dmesg` or the
-touchpad gesture debug logs emitted by `TouchpadInputMapper`.
+the recording. Passing `--timestamp-base=epoch` causes the timestamps to be recorded as Unix
+timestamps, relative to the Unix epoch (00:00:00 UTC on 1st January 1970). While this does not
+affect the playback of the recording, it can make the events in the recording easier to match up
+with those from other log sources, like logcat.
[FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu
[format]: https://gitlab.freedesktop.org/libevdev/evemu#device-description-format
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 43048db..917d9a7 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -59,6 +59,7 @@
PLAYER_MUTE_PLAYBACK_RESTRICTED = (1 << 3),
PLAYER_MUTE_CLIENT_VOLUME = (1 << 4),
PLAYER_MUTE_VOLUME_SHAPER = (1 << 5),
+ PLAYER_MUTE_PORT_VOLUME = (1 << 6),
};
struct mute_state_t {
@@ -72,8 +73,10 @@
bool muteFromPlaybackRestricted = false;
/** Flag used when audio track was muted by client volume. */
bool muteFromClientVolume = false;
- /** Flag used when volume is muted by volume shaper. */
+ /** Flag used when volume is muted by volume shaper. */
bool muteFromVolumeShaper = false;
+ /** Flag used when volume is muted by port volume. */
+ bool muteFromPortVolume = false;
explicit operator int() const
{
@@ -83,6 +86,7 @@
result |= muteFromPlaybackRestricted * PLAYER_MUTE_PLAYBACK_RESTRICTED;
result |= muteFromClientVolume * PLAYER_MUTE_CLIENT_VOLUME;
result |= muteFromVolumeShaper * PLAYER_MUTE_VOLUME_SHAPER;
+ result |= muteFromPortVolume * PLAYER_MUTE_PORT_VOLUME;
return result;
}
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 1a48239..6a248ef 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -266,6 +266,7 @@
public:
InputDeviceInfo();
InputDeviceInfo(const InputDeviceInfo& other);
+ InputDeviceInfo& operator=(const InputDeviceInfo& other);
~InputDeviceInfo();
struct MotionRange {
@@ -315,13 +316,11 @@
inline const InputDeviceViewBehavior& getViewBehavior() const { return mViewBehavior; }
- inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) {
- mKeyCharacterMap = value;
+ inline void setKeyCharacterMap(std::unique_ptr<KeyCharacterMap> value) {
+ mKeyCharacterMap = std::move(value);
}
- inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const {
- return mKeyCharacterMap;
- }
+ inline const KeyCharacterMap* getKeyCharacterMap() const { return mKeyCharacterMap.get(); }
inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; }
inline bool hasVibrator() const { return mHasVibrator; }
@@ -364,7 +363,7 @@
std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo;
uint32_t mSources;
int32_t mKeyboardType;
- std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
+ std::unique_ptr<KeyCharacterMap> mKeyCharacterMap;
std::optional<InputDeviceUsiVersion> mUsiVersion;
ui::LogicalDisplayId mAssociatedDisplayId{ui::LogicalDisplayId::INVALID};
bool mEnabled;
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 1899a66..1696a62 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -250,6 +250,9 @@
public:
MotionEventBuilder(int32_t action, int32_t source) {
mAction = action;
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ mFlags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
mSource = source;
mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
mDownTime = mEventTime;
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 67b37b1..0a9e74f 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -72,7 +72,7 @@
};
/* Loads a key character map from a file. */
- static base::Result<std::shared_ptr<KeyCharacterMap>> load(const std::string& filename,
+ static base::Result<std::unique_ptr<KeyCharacterMap>> load(const std::string& filename,
Format format);
/* Loads a key character map from its string contents. */
@@ -137,6 +137,9 @@
/* Returns keycode after applying Android key code remapping defined in mKeyRemapping */
int32_t applyKeyRemapping(int32_t fromKeyCode) const;
+ /** Returns list of keycodes that remap to provided keycode (@see setKeyRemapping()) */
+ std::vector<int32_t> findKeyCodesMappedToKeyCode(int32_t toKeyCode) const;
+
/* Returns the <keyCode, metaState> pair after applying key behavior defined in the kcm file,
* that tries to find a replacement key code based on current meta state */
std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode,
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
index 4aaeddd..47519c2 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -65,7 +65,8 @@
* extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is
* not null, interpolation will occur. If `futureSample` is null and there is enough historical
* data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and
- * `motionEvent` is unmodified.
+ * `motionEvent` is unmodified. Furthermore, motionEvent is not resampled if resampleTime equals
+ * the last sample eventTime of motionEvent.
*/
void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
const InputMessage* futureSample) override;
@@ -99,6 +100,17 @@
RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
/**
+ * Latest sample in mLatestSamples after resampling motion event. Used to compare if a pointer
+ * does not move between samples.
+ */
+ std::optional<Sample> mLastRealSample;
+
+ /**
+ * Latest prediction. Used to overwrite motion event samples if a set of conditions is met.
+ */
+ std::optional<Sample> mPreviousPrediction;
+
+ /**
* Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If
* motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are
* added to mLatestSamples.
@@ -143,6 +155,23 @@
*/
std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;
+ /**
+ * Iterates through motion event samples, and calls overwriteStillPointers on each sample.
+ */
+ void overwriteMotionEventSamples(MotionEvent& motionEvent) const;
+
+ /**
+ * Overwrites with resampled data the pointer coordinates that did not move between motion event
+ * samples, that is, both x and y values are identical to mLastRealSample.
+ */
+ void overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const;
+
+ /**
+ * Overwrites the pointer coordinates of a sample with event time older than
+ * that of mPreviousPrediction.
+ */
+ void overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const;
+
inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
};
} // namespace android
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index e5eee34..b7308c2 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -83,6 +83,7 @@
HWUI = 2,
GAME = 3,
APP = 4,
+ SYSUI = 5,
};
/**
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 379b609..6903cb5 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -489,6 +489,7 @@
"ProcessState.cpp",
"Static.cpp",
":libbinder_aidl",
+ ":libbinder_accessor_aidl",
":libbinder_device_interface_sources",
],
target: {
@@ -801,7 +802,6 @@
"aidl/android/os/IServiceManager.aidl",
"aidl/android/os/Service.aidl",
"aidl/android/os/ServiceDebugInfo.aidl",
- ":libbinder_accessor_aidl",
],
path: "aidl",
}
@@ -812,26 +812,7 @@
"aidl/android/os/IAccessor.aidl",
],
path: "aidl",
-}
-
-// TODO(b/353492849): Make this interface private to libbinder.
-aidl_interface {
- name: "android.os.accessor",
- srcs: [":libbinder_accessor_aidl"],
- unstable: true,
- backend: {
- rust: {
- enabled: true,
- apex_available: [
- "com.android.virt",
- ],
- },
- },
- visibility: [
- ":__subpackages__",
- "//system/tools/aidl:__subpackages__",
- "//packages/modules/Virtualization:__subpackages__",
- ],
+ visibility: [":__subpackages__"],
}
aidl_interface {
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index 52b485a..f7b9f05 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -31,7 +31,8 @@
#endif
using AidlServiceManager = android::os::IServiceManager;
-using IAccessor = android::os::IAccessor;
+using android::os::IAccessor;
+using binder::Status;
static const char* kStaticCachableList[] = {
// go/keep-sorted start
@@ -39,10 +40,14 @@
"account",
"activity",
"alarm",
+ "android.frameworks.stats.IStats/default",
"android.system.keystore2.IKeystoreService/default",
"appops",
"audio",
+ "autofill",
+ "batteryproperties",
"batterystats",
+ "biometic",
"carrier_config",
"connectivity",
"content",
@@ -58,6 +63,7 @@
"jobscheduler",
"legacy_permission",
"location",
+ "lock_settings",
"media.extractor",
"media.metrics",
"media.player",
@@ -78,15 +84,19 @@
"phone",
"platform_compat",
"power",
+ "processinfo",
"role",
+ "sensitive_content_protection_service",
"sensorservice",
"statscompanion",
"telephony.registry",
"thermalservice",
"time_detector",
+ "tracing.proxy",
"trust",
"uimode",
"user",
+ "vibrator",
"virtualdevice",
"virtualdevice_native",
"webviewupdate",
@@ -109,19 +119,38 @@
return false;
}
-binder::Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName,
- const os::Service& service) {
+Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName,
+ const os::Service& service) {
if (!kUseCache) {
- return binder::Status::ok();
+ return Status::ok();
}
+ std::string traceStr;
+ if (atrace_is_tag_enabled(ATRACE_TAG_AIDL)) {
+ traceStr = "BinderCacheWithInvalidation::updateCache : " + serviceName;
+ }
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL, traceStr.c_str());
+
if (service.getTag() == os::Service::Tag::binder) {
sp<IBinder> binder = service.get<os::Service::Tag::binder>();
- if (binder && mCacheForGetService->isClientSideCachingEnabled(serviceName) &&
- binder->isBinderAlive()) {
+ if (!binder) {
+ binder::ScopedTrace
+ aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache failed: binder_null");
+ } else if (!binder->isBinderAlive()) {
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache failed: "
+ "isBinderAlive_false");
+ } else if (mCacheForGetService->isClientSideCachingEnabled(serviceName)) {
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache successful");
return mCacheForGetService->setItem(serviceName, binder);
+ } else {
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache failed: "
+ "caching_not_enabled");
}
}
- return binder::Status::ok();
+ return Status::ok();
}
bool BackendUnifiedServiceManager::returnIfCached(const std::string& serviceName,
@@ -147,21 +176,20 @@
return mTheRealServiceManager;
}
-binder::Status BackendUnifiedServiceManager::getService(const ::std::string& name,
- sp<IBinder>* _aidl_return) {
+Status BackendUnifiedServiceManager::getService(const ::std::string& name,
+ sp<IBinder>* _aidl_return) {
os::Service service;
- binder::Status status = getService2(name, &service);
+ Status status = getService2(name, &service);
*_aidl_return = service.get<os::Service::Tag::binder>();
return status;
}
-binder::Status BackendUnifiedServiceManager::getService2(const ::std::string& name,
- os::Service* _out) {
+Status BackendUnifiedServiceManager::getService2(const ::std::string& name, os::Service* _out) {
if (returnIfCached(name, _out)) {
- return binder::Status::ok();
+ return Status::ok();
}
os::Service service;
- binder::Status status = mTheRealServiceManager->getService2(name, &service);
+ Status status = mTheRealServiceManager->getService2(name, &service);
if (status.isOk()) {
status = toBinderService(name, service, _out);
@@ -172,14 +200,13 @@
return status;
}
-binder::Status BackendUnifiedServiceManager::checkService(const ::std::string& name,
- os::Service* _out) {
+Status BackendUnifiedServiceManager::checkService(const ::std::string& name, os::Service* _out) {
os::Service service;
if (returnIfCached(name, _out)) {
- return binder::Status::ok();
+ return Status::ok();
}
- binder::Status status = mTheRealServiceManager->checkService(name, &service);
+ Status status = mTheRealServiceManager->checkService(name, &service);
if (status.isOk()) {
status = toBinderService(name, service, _out);
if (status.isOk()) {
@@ -189,16 +216,15 @@
return status;
}
-binder::Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name,
- const os::Service& in,
- os::Service* _out) {
+Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name,
+ const os::Service& in, os::Service* _out) {
switch (in.getTag()) {
case os::Service::Tag::binder: {
if (in.get<os::Service::Tag::binder>() == nullptr) {
// failed to find a service. Check to see if we have any local
// injected Accessors for this service.
os::Service accessor;
- binder::Status status = getInjectedAccessor(name, &accessor);
+ Status status = getInjectedAccessor(name, &accessor);
if (!status.isOk()) {
*_out = os::Service::make<os::Service::Tag::binder>(nullptr);
return status;
@@ -214,7 +240,7 @@
}
*_out = in;
- return binder::Status::ok();
+ return Status::ok();
}
case os::Service::Tag::accessor: {
sp<IBinder> accessorBinder = in.get<os::Service::Tag::accessor>();
@@ -222,11 +248,11 @@
if (accessor == nullptr) {
ALOGE("Service#accessor doesn't have accessor. VM is maybe starting...");
*_out = os::Service::make<os::Service::Tag::binder>(nullptr);
- return binder::Status::ok();
+ return Status::ok();
}
auto request = [=] {
os::ParcelFileDescriptor fd;
- binder::Status ret = accessor->addConnection(&fd);
+ Status ret = accessor->addConnection(&fd);
if (ret.isOk()) {
return base::unique_fd(fd.release());
} else {
@@ -239,11 +265,11 @@
if (status != OK) {
ALOGE("Failed to set up preconnected binder RPC client: %s",
statusToString(status).c_str());
- return binder::Status::fromStatusT(status);
+ return Status::fromStatusT(status);
}
session->setSessionSpecificRoot(accessorBinder);
*_out = os::Service::make<os::Service::Tag::binder>(session->getRootObject());
- return binder::Status::ok();
+ return Status::ok();
}
default: {
LOG_ALWAYS_FATAL("Unknown service type: %d", in.getTag());
@@ -251,53 +277,52 @@
}
}
-binder::Status BackendUnifiedServiceManager::addService(const ::std::string& name,
- const sp<IBinder>& service,
- bool allowIsolated, int32_t dumpPriority) {
+Status BackendUnifiedServiceManager::addService(const ::std::string& name,
+ const sp<IBinder>& service, bool allowIsolated,
+ int32_t dumpPriority) {
return mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
}
-binder::Status BackendUnifiedServiceManager::listServices(
- int32_t dumpPriority, ::std::vector<::std::string>* _aidl_return) {
+Status BackendUnifiedServiceManager::listServices(int32_t dumpPriority,
+ ::std::vector<::std::string>* _aidl_return) {
return mTheRealServiceManager->listServices(dumpPriority, _aidl_return);
}
-binder::Status BackendUnifiedServiceManager::registerForNotifications(
+Status BackendUnifiedServiceManager::registerForNotifications(
const ::std::string& name, const sp<os::IServiceCallback>& callback) {
return mTheRealServiceManager->registerForNotifications(name, callback);
}
-binder::Status BackendUnifiedServiceManager::unregisterForNotifications(
+Status BackendUnifiedServiceManager::unregisterForNotifications(
const ::std::string& name, const sp<os::IServiceCallback>& callback) {
return mTheRealServiceManager->unregisterForNotifications(name, callback);
}
-binder::Status BackendUnifiedServiceManager::isDeclared(const ::std::string& name,
- bool* _aidl_return) {
+Status BackendUnifiedServiceManager::isDeclared(const ::std::string& name, bool* _aidl_return) {
return mTheRealServiceManager->isDeclared(name, _aidl_return);
}
-binder::Status BackendUnifiedServiceManager::getDeclaredInstances(
+Status BackendUnifiedServiceManager::getDeclaredInstances(
const ::std::string& iface, ::std::vector<::std::string>* _aidl_return) {
return mTheRealServiceManager->getDeclaredInstances(iface, _aidl_return);
}
-binder::Status BackendUnifiedServiceManager::updatableViaApex(
+Status BackendUnifiedServiceManager::updatableViaApex(
const ::std::string& name, ::std::optional<::std::string>* _aidl_return) {
return mTheRealServiceManager->updatableViaApex(name, _aidl_return);
}
-binder::Status BackendUnifiedServiceManager::getUpdatableNames(
- const ::std::string& apexName, ::std::vector<::std::string>* _aidl_return) {
+Status BackendUnifiedServiceManager::getUpdatableNames(const ::std::string& apexName,
+ ::std::vector<::std::string>* _aidl_return) {
return mTheRealServiceManager->getUpdatableNames(apexName, _aidl_return);
}
-binder::Status BackendUnifiedServiceManager::getConnectionInfo(
+Status BackendUnifiedServiceManager::getConnectionInfo(
const ::std::string& name, ::std::optional<os::ConnectionInfo>* _aidl_return) {
return mTheRealServiceManager->getConnectionInfo(name, _aidl_return);
}
-binder::Status BackendUnifiedServiceManager::registerClientCallback(
+Status BackendUnifiedServiceManager::registerClientCallback(
const ::std::string& name, const sp<IBinder>& service,
const sp<os::IClientCallback>& callback) {
return mTheRealServiceManager->registerClientCallback(name, service, callback);
}
-binder::Status BackendUnifiedServiceManager::tryUnregisterService(const ::std::string& name,
- const sp<IBinder>& service) {
+Status BackendUnifiedServiceManager::tryUnregisterService(const ::std::string& name,
+ const sp<IBinder>& service) {
return mTheRealServiceManager->tryUnregisterService(name, service);
}
-binder::Status BackendUnifiedServiceManager::getServiceDebugInfo(
+Status BackendUnifiedServiceManager::getServiceDebugInfo(
::std::vector<os::ServiceDebugInfo>* _aidl_return) {
return mTheRealServiceManager->getServiceDebugInfo(_aidl_return);
}
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index 47b2ec9..feb8470 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -18,6 +18,7 @@
#include <android/os/BnServiceManager.h>
#include <android/os/IServiceManager.h>
#include <binder/IPCThreadState.h>
+#include <binder/Trace.h>
#include <map>
#include <memory>
@@ -59,6 +60,12 @@
}
bool removeItem(const std::string& key, const sp<IBinder>& who) {
+ std::string traceStr;
+ uint64_t tag = ATRACE_TAG_AIDL;
+ if (atrace_is_tag_enabled(tag)) {
+ traceStr = "BinderCacheWithInvalidation::removeItem " + key;
+ }
+ binder::ScopedTrace aidlTrace(tag, traceStr.c_str());
std::lock_guard<std::mutex> lock(mCacheMutex);
if (auto it = mCache.find(key); it != mCache.end()) {
if (it->second.service == who) {
@@ -81,11 +88,22 @@
if (item->localBinder() == nullptr) {
status_t status = item->linkToDeath(deathRecipient);
if (status != android::OK) {
+ std::string traceStr;
+ uint64_t tag = ATRACE_TAG_AIDL;
+ if (atrace_is_tag_enabled(tag)) {
+ traceStr =
+ "BinderCacheWithInvalidation::setItem Failed LinkToDeath for service " +
+ key + " : " + std::to_string(status);
+ }
+ binder::ScopedTrace aidlTrace(tag, traceStr.c_str());
+
ALOGE("Failed to linkToDeath binder for service %s. Error: %d", key.c_str(),
status);
return binder::Status::fromStatusT(status);
}
}
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::setItem Successfully Cached");
std::lock_guard<std::mutex> lock(mCacheMutex);
Entry entry = {.service = item, .deathRecipient = deathRecipient};
mCache[key] = entry;
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index eae844c..3758b65 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -197,7 +197,9 @@
&& currentValue < sBinderProxyCountHighWatermark
&& ((trackedValue & WARNING_REACHED_MASK) == 0)) [[unlikely]] {
sTrackingMap[trackedUid] |= WARNING_REACHED_MASK;
- if (sWarningCallback) sWarningCallback(trackedUid);
+ if (sWarningCallback) {
+ *postTask = [=]() { sWarningCallback(trackedUid); };
+ }
} else if (currentValue >= sBinderProxyCountHighWatermark) {
ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
getuid(), trackedUid, trackedValue);
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 77b80ef..32388db 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -386,7 +386,7 @@
ALOGE("Binder is null");
return BAD_VALUE;
}
- sp<IAccessor> accessor = interface_cast<IAccessor>(binder);
+ sp<IAccessor> accessor = checked_interface_cast<IAccessor>(binder);
if (accessor == nullptr) {
ALOGE("This binder for %s is not an IAccessor binder", String8(instance).c_str());
return BAD_TYPE;
@@ -420,6 +420,28 @@
return binder;
}
+status_t delegateAccessor(const String16& name, const sp<IBinder>& accessor,
+ sp<IBinder>* delegator) {
+ LOG_ALWAYS_FATAL_IF(delegator == nullptr, "delegateAccessor called with a null out param");
+ if (accessor == nullptr) {
+ ALOGW("Accessor argument to delegateAccessor is null.");
+ *delegator = nullptr;
+ return OK;
+ }
+ status_t status = validateAccessor(name, accessor);
+ if (status != OK) {
+ ALOGE("The provided accessor binder is not an IAccessor for instance %s. Status: "
+ "%s",
+ String8(name).c_str(), statusToString(status).c_str());
+ return status;
+ }
+ // validateAccessor already called checked_interface_cast and made sure this
+ // is a valid accessor object.
+ *delegator = sp<android::os::IAccessorDelegator>::make(interface_cast<IAccessor>(accessor));
+
+ return OK;
+}
+
#if !defined(__ANDROID_VNDK__)
// IPermissionController is not accessible to vendors
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 842ea77..18c4134 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1186,6 +1186,10 @@
//printf("Writing %ld bytes, padded to %ld\n", len, padded);
uint8_t* const data = mData+mDataPos;
+ if (status_t status = validateReadData(mDataPos + padded); status != OK) {
+ return nullptr; // drops status
+ }
+
// Need to pad at end?
if (padded != len) {
#if BYTE_ORDER == BIG_ENDIAN
@@ -1774,6 +1778,10 @@
const bool enoughObjects = kernelFields->mObjectsSize < kernelFields->mObjectsCapacity;
if (enoughData && enoughObjects) {
restart_write:
+ if (status_t status = validateReadData(mDataPos + sizeof(val)); status != OK) {
+ return status;
+ }
+
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
// remember if it's a file descriptor
@@ -2020,6 +2028,10 @@
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
+ if (status_t status = validateReadData(mDataPos + sizeof(val)); status != OK) {
+ return status;
+ }
+
memcpy(mData + mDataPos, &val, sizeof(val));
return finishWrite(sizeof(val));
}
@@ -2656,14 +2668,14 @@
}
#endif // BINDER_WITH_KERNEL_IPC
-void Parcel::closeFileDescriptors() {
+void Parcel::closeFileDescriptors(size_t newObjectsSize) {
if (auto* kernelFields = maybeKernelFields()) {
#ifdef BINDER_WITH_KERNEL_IPC
size_t i = kernelFields->mObjectsSize;
if (i > 0) {
// ALOGI("Closing file descriptors for %zu objects...", i);
}
- while (i > 0) {
+ while (i > newObjectsSize) {
i--;
const flat_binder_object* flat =
reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
@@ -2674,6 +2686,7 @@
}
}
#else // BINDER_WITH_KERNEL_IPC
+ (void)newObjectsSize;
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
#endif // BINDER_WITH_KERNEL_IPC
} else if (auto* rpcFields = maybeRpcFields()) {
@@ -2898,7 +2911,7 @@
//ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
auto* kernelFields = maybeKernelFields();
// Close FDs before freeing, otherwise they will leak for kernel binder.
- closeFileDescriptors();
+ closeFileDescriptors(/*newObjectsSize=*/0);
mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
kernelFields ? kernelFields->mObjectsSize : 0);
} else {
@@ -3035,13 +3048,38 @@
objectsSize = 0;
} else {
if (kernelFields) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ validateReadData(mDataSize); // hack to sort the objects
while (objectsSize > 0) {
- if (kernelFields->mObjects[objectsSize - 1] < desired) break;
+ if (kernelFields->mObjects[objectsSize - 1] + sizeof(flat_binder_object) <=
+ desired)
+ break;
objectsSize--;
}
+#endif // BINDER_WITH_KERNEL_IPC
} else {
while (objectsSize > 0) {
- if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break;
+ // Object size varies by type.
+ uint32_t pos = rpcFields->mObjectPositions[objectsSize - 1];
+ size_t size = sizeof(RpcFields::ObjectType);
+ uint32_t minObjectEnd;
+ if (__builtin_add_overflow(pos, sizeof(RpcFields::ObjectType), &minObjectEnd) ||
+ minObjectEnd > mDataSize) {
+ return BAD_VALUE;
+ }
+ const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
+ switch (type) {
+ case RpcFields::TYPE_BINDER_NULL:
+ break;
+ case RpcFields::TYPE_BINDER:
+ size += sizeof(uint64_t); // address
+ break;
+ case RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR:
+ size += sizeof(int32_t); // fd index
+ break;
+ }
+
+ if (pos + size <= desired) break;
objectsSize--;
}
}
@@ -3090,15 +3128,24 @@
if (mData) {
memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
}
+#ifdef BINDER_WITH_KERNEL_IPC
if (objects && kernelFields && kernelFields->mObjects) {
memcpy(objects, kernelFields->mObjects, objectsSize * sizeof(binder_size_t));
+ // All FDs are owned when `mOwner`, even when `cookie == 0`. When
+ // we switch to `!mOwner`, we need to explicitly mark the FDs as
+ // owned.
+ for (size_t i = 0; i < objectsSize; i++) {
+ flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]);
+ if (flat->hdr.type == BINDER_TYPE_FD) {
+ flat->cookie = 1;
+ }
+ }
}
// ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
if (kernelFields) {
- // TODO(b/239222407): This seems wrong. We should only free FDs when
- // they are in a truncated section of the parcel.
- closeFileDescriptors();
+ closeFileDescriptors(objectsSize);
}
+#endif // BINDER_WITH_KERNEL_IPC
mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
kernelFields ? kernelFields->mObjectsSize : 0);
mOwner = nullptr;
@@ -3225,11 +3272,19 @@
}
while (rpcFields->mObjectPositions.size() > newObjectsSize) {
uint32_t pos = rpcFields->mObjectPositions.back();
- rpcFields->mObjectPositions.pop_back();
+ uint32_t minObjectEnd;
+ if (__builtin_add_overflow(pos, sizeof(RpcFields::ObjectType), &minObjectEnd) ||
+ minObjectEnd > mDataSize) {
+ return BAD_VALUE;
+ }
const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
if (type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
- const auto fdIndex =
- *reinterpret_cast<const int32_t*>(mData + pos + sizeof(RpcFields::ObjectType));
+ uint32_t objectEnd;
+ if (__builtin_add_overflow(minObjectEnd, sizeof(int32_t), &objectEnd) ||
+ objectEnd > mDataSize) {
+ return BAD_VALUE;
+ }
+ const auto fdIndex = *reinterpret_cast<const int32_t*>(mData + minObjectEnd);
if (rpcFields->mFds == nullptr || fdIndex < 0 ||
static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) {
ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu",
@@ -3239,6 +3294,7 @@
// In practice, this always removes the last element.
rpcFields->mFds->erase(rpcFields->mFds->begin() + fdIndex);
}
+ rpcFields->mObjectPositions.pop_back();
}
return OK;
}
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 95a5da2..ab44957 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -94,6 +94,9 @@
},
{
"name": "libbinder_rpc_unstable_bindgen_test"
+ },
+ {
+ "name": "binderCacheUnitTest"
}
],
"presubmit-large": [
@@ -133,9 +136,6 @@
{
"name": "binder_sdk_test",
"host": true
- },
- {
- "name": "binderCacheUnitTest"
}
],
"imports": [
diff --git a/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
index 75f8753..9bac386 100644
--- a/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
+++ b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
@@ -16,6 +16,8 @@
package android.content.pm;
+import android.content.pm.StagedApexInfo;
+
/**
* This event is designed for notification to native code listener about
* any changes to set of apex packages staged for installation on next boot.
@@ -23,5 +25,5 @@
* @hide
*/
parcelable ApexStagedEvent {
- @utf8InCpp String[] stagedApexModuleNames;
+ StagedApexInfo[] stagedApexInfos;
}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 3ddfefa..0f0be2f 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -135,13 +135,7 @@
void unregisterStagedApexObserver(in IStagedApexObserver observer);
/**
- * Get APEX module names of all APEX that are staged ready for installation
+ * Get information of staged APEXes.
*/
- @utf8InCpp String[] getStagedApexModuleNames();
-
- /**
- * Get information of APEX which is staged ready for installation.
- * Returns null if no such APEX is found.
- */
- @nullable StagedApexInfo getStagedApexInfo(in @utf8InCpp String moduleName);
+ StagedApexInfo[] getStagedApexInfos();
}
diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
index 949835b..8f7ad30 100644
--- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
+++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
@@ -22,6 +22,7 @@
*
* @hide
*/
+@JavaDerive(equals=true)
parcelable StagedApexInfo {
@utf8InCpp String moduleName;
@utf8InCpp String diskImagePath;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 2b23276..81f7cdb 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -291,6 +291,28 @@
* \return OK if the binder is an IAccessor for `instance`
*/
LIBBINDER_EXPORTED status_t validateAccessor(const String16& instance, const sp<IBinder>& binder);
+
+/**
+ * Have libbinder wrap this IAccessor binder in an IAccessorDelegator and return
+ * it.
+ *
+ * This is required only in very specific situations when the process that has
+ * permissions to connect the to RPC service's socket and create the FD for it
+ * is in a separate process from this process that wants to service the Accessor
+ * binder and the communication between these two processes is binder RPC. This
+ * is needed because the binder passed over the binder RPC connection can not be
+ * used as a kernel binder, and needs to be wrapped by a kernel binder that can
+ * then be registered with service manager.
+ *
+ * \param instance name of the Accessor.
+ * \param binder to wrap in a Delegator and register with service manager.
+ * \param outDelegator the wrapped kernel binder for IAccessorDelegator
+ *
+ * \return OK if the binder is an IAccessor for `instance` and the delegator was
+ * successfully created.
+ */
+LIBBINDER_EXPORTED status_t delegateAccessor(const String16& name, const sp<IBinder>& accessor,
+ sp<IBinder>* delegator);
#endif // __TRUSTY__
#ifndef __ANDROID__
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index ed4e211..ceab20a 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -648,8 +648,8 @@
LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const;
private:
- // Explicitly close all file descriptors in the parcel.
- void closeFileDescriptors();
+ // Close all file descriptors in the parcel at object positions >= newObjectsSize.
+ void closeFileDescriptors(size_t newObjectsSize);
// `objects` and `objectsSize` always 0 for RPC Parcels.
typedef void (*release_func)(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
@@ -1239,7 +1239,7 @@
if (__builtin_mul_overflow(size, sizeof(T), &dataLen)) {
return -EOVERFLOW;
}
- auto data = reinterpret_cast<const T*>(readInplace(dataLen));
+ auto data = readInplace(dataLen);
if (data == nullptr) return BAD_VALUE;
// std::vector::insert and similar methods will require type-dependent
// byte alignment when inserting from a const iterator such as `data`,
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index c671eed..0b4f196 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -152,6 +152,14 @@
return callParcel("writeParcelableVector",
[&]() { return parcel->writeParcelableVector(v); });
}
+
+ status_t read(const Parcel& parcel, std::vector<bool>* v) const {
+ return callParcel("readBoolVector", [&]() { return parcel.readBoolVector(v); });
+ }
+ status_t write(Parcel* parcel, const std::vector<bool>& v) const {
+ return callParcel("writeBoolVector", [&]() { return parcel->writeBoolVector(v); });
+ }
+
status_t read(const Parcel& parcel, float* f) const {
return callParcel("readFloat", [&]() { return parcel.readFloat(f); });
}
diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp
index 3c32a39..886eb4b 100644
--- a/libs/binder/ndk/binder_rpc.cpp
+++ b/libs/binder/ndk/binder_rpc.cpp
@@ -255,7 +255,7 @@
"new variant was added to the ABinderRpc_ConnectionInfo and this needs to be "
"updated.");
}
- return OK;
+ return STATUS_OK;
};
sp<IBinder> accessorBinder = android::createAccessor(String16(instance), std::move(generate));
if (accessorBinder == nullptr) {
@@ -302,6 +302,28 @@
}
}
+binder_status_t ABinderRpc_Accessor_delegateAccessor(const char* instance, AIBinder* accessor,
+ AIBinder** outDelegator) {
+ LOG_ALWAYS_FATAL_IF(outDelegator == nullptr, "The outDelegator argument is null");
+ if (instance == nullptr || accessor == nullptr) {
+ ALOGW("instance or accessor arguments to ABinderRpc_Accessor_delegateBinder are null");
+ *outDelegator = nullptr;
+ return STATUS_UNEXPECTED_NULL;
+ }
+ sp<IBinder> accessorBinder = accessor->getBinder();
+
+ sp<IBinder> delegator;
+ status_t status = android::delegateAccessor(String16(instance), accessorBinder, &delegator);
+ if (status != OK) {
+ return PruneStatusT(status);
+ }
+ sp<AIBinder> binder = ABpBinder::lookupOrCreateFromBinder(delegator);
+ // This AIBinder needs a strong ref to pass ownership to the caller
+ binder->incStrong(nullptr);
+ *outDelegator = binder.get();
+ return STATUS_OK;
+}
+
ABinderRpc_ConnectionInfo* ABinderRpc_ConnectionInfo_new(const sockaddr* addr, socklen_t len) {
if (addr == nullptr || len < 0 || static_cast<size_t>(len) < sizeof(sa_family_t)) {
ALOGE("Invalid arguments in ABinderRpc_Connection_new");
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 8296356..0ad110e 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -30,7 +30,7 @@
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
-#if defined(__ANDROID_VENDOR__)
+#if defined(__ANDROID_VENDOR_API__)
#include <android/llndk-versioning.h>
#elif !defined(API_LEVEL_AT_LEAST)
#if defined(__BIONIC__)
@@ -39,7 +39,7 @@
#else
#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
#endif // __BIONIC__
-#endif // __ANDROID_VENDOR__
+#endif // __ANDROID_VENDOR_API__
#if __has_include(<android/binder_shell.h>)
#include <android/binder_shell.h>
@@ -297,11 +297,10 @@
}
#endif
-// TODO(b/368559337): fix versioning on product partition
-#if !defined(__ANDROID_PRODUCT__) && \
- (defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36)
+#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36
if API_LEVEL_AT_LEAST (36, 202504) {
- if (codeToFunction != nullptr) {
+ if (codeToFunction != nullptr &&
+ (&AIBinder_Class_setTransactionCodeToFunctionNameMap != nullptr)) {
AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction,
functionCount);
}
diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
index c1d0e9f..83976b3 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -22,8 +22,8 @@
#include <set>
#include <sstream>
-// Include llndk-versioning.h only for vendor build as it is not available for NDK headers.
-#if defined(__ANDROID_VENDOR__)
+// Include llndk-versioning.h only for non-system build as it is not available for NDK headers.
+#if defined(__ANDROID_VENDOR_API__)
#include <android/llndk-versioning.h>
#elif !defined(API_LEVEL_AT_LEAST)
#if defined(__BIONIC__)
@@ -32,7 +32,7 @@
#else
#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
#endif // __BIONIC__
-#endif // __ANDROID_VENDOR__
+#endif // __ANDROID_VENDOR_API__
namespace aidl::android::os {
diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h
index 7132554..66667d3 100644
--- a/libs/binder/ndk/include_platform/android/binder_rpc.h
+++ b/libs/binder/ndk/include_platform/android/binder_rpc.h
@@ -265,6 +265,40 @@
__INTRODUCED_IN(36);
/**
+ * Wrap an ABinderRpc_Accessor proxy binder with a delegator binder.
+ *
+ * The IAccessorDelegator binder delegates all calls to the proxy binder.
+ *
+ * This is required only in very specific situations when the process that has
+ * permissions to connect the to RPC service's socket and create the FD for it
+ * is in a separate process from this process that wants to serve the Accessor
+ * binder and the communication between these two processes is binder RPC. This
+ * is needed because the binder passed over the binder RPC connection can not be
+ * used as a kernel binder, and needs to be wrapped by a kernel binder that can
+ * then be registered with service manager.
+ *
+ * \param instance name of the service associated with the Accessor
+ * \param binder the AIBinder* from the ABinderRpc_Accessor from the
+ * ABinderRpc_Accessor_asBinder. The other process across the binder RPC
+ * connection will have called this and passed the AIBinder* across a
+ * binder interface to the process calling this function.
+ * \param outDelegator the AIBinder* for the kernel binder that wraps the
+ * 'binder' argument and delegates all calls to it. The caller now owns
+ * this object with one strong ref count and is responsible for removing
+ * that ref count with with AIBinder_decStrong when the caller wishes to
+ * drop the reference.
+ * \return STATUS_OK on success.
+ * STATUS_UNEXPECTED_NULL if instance or binder arguments are null.
+ * STATUS_BAD_TYPE if the binder is not an IAccessor.
+ * STATUS_NAME_NOT_FOUND if the binder is an IAccessor, but not
+ * associated with the provided instance name.
+ */
+binder_status_t ABinderRpc_Accessor_delegateAccessor(const char* _Nonnull instance,
+ AIBinder* _Nonnull binder,
+ AIBinder* _Nullable* _Nonnull outDelegator)
+ __INTRODUCED_IN(36);
+
+/**
* Create a new ABinderRpc_ConnectionInfo with sockaddr. This can be supported socket
* types like sockaddr_vm (vsock) and sockaddr_un (Unix Domain Sockets).
*
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index c885816..4d691f8 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -257,6 +257,7 @@
ABinderRpc_registerAccessorProvider; # systemapi
ABinderRpc_unregisterAccessorProvider; # systemapi
ABinderRpc_Accessor_new; # systemapi
+ ABinderRpc_Accessor_delegateAccessor; #systemapi
ABinderRpc_Accessor_delete; # systemapi
ABinderRpc_Accessor_asBinder; # systemapi
ABinderRpc_Accessor_fromBinder; # systemapi
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 4545d7b..020ebcc 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -16,7 +16,6 @@
"libdowncast_rs",
"liblibc",
"liblog_rust",
- "libnix",
],
host_supported: true,
vendor_available: true,
@@ -200,7 +199,6 @@
"libdowncast_rs",
"liblibc",
"liblog_rust",
- "libnix",
],
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 0e8e388..f7f3f35 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -123,7 +123,7 @@
#[cfg(not(trusty))]
pub use state::{ProcessState, ThreadState};
#[cfg(not(any(android_vendor, android_vndk)))]
-pub use system_only::{Accessor, ConnectionInfo};
+pub use system_only::{delegate_accessor, Accessor, ConnectionInfo};
/// Binder result containing a [`Status`] on error.
pub type Result<T> = std::result::Result<T, Status>;
diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
index a91d84d..9833cbe 100644
--- a/libs/binder/rust/src/system_only.rs
+++ b/libs/binder/rust/src/system_only.rs
@@ -14,16 +14,17 @@
* limitations under the License.
*/
+use crate::binder::AsNative;
+use crate::error::{status_result, Result};
use crate::proxy::SpIBinder;
use crate::sys;
use std::ffi::{c_void, CStr, CString};
use std::os::raw::c_char;
-use libc::sockaddr;
-use nix::sys::socket::{SockaddrLike, UnixAddr, VsockAddr};
+use libc::{sockaddr, sockaddr_un, sockaddr_vm, socklen_t};
use std::sync::Arc;
-use std::{fmt, ptr};
+use std::{fmt, mem, ptr};
/// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management.
///
@@ -42,9 +43,9 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionInfo {
/// For vsock connection
- Vsock(VsockAddr),
+ Vsock(sockaddr_vm),
/// For unix domain socket connection
- Unix(UnixAddr),
+ Unix(sockaddr_un),
}
/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
@@ -146,13 +147,21 @@
match connection {
ConnectionInfo::Vsock(addr) => {
// Safety: The sockaddr is being copied in the NDK API
- unsafe { sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr(), addr.len()) }
+ unsafe {
+ sys::ABinderRpc_ConnectionInfo_new(
+ &addr as *const sockaddr_vm as *const sockaddr,
+ mem::size_of::<sockaddr_vm>() as socklen_t,
+ )
+ }
}
ConnectionInfo::Unix(addr) => {
// Safety: The sockaddr is being copied in the NDK API
// The cast is from sockaddr_un* to sockaddr*.
unsafe {
- sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr() as *const sockaddr, addr.len())
+ sys::ABinderRpc_ConnectionInfo_new(
+ &addr as *const sockaddr_un as *const sockaddr,
+ mem::size_of::<sockaddr_un>() as socklen_t,
+ )
}
}
}
@@ -185,3 +194,27 @@
}
}
}
+
+/// Register a new service with the default service manager.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
+pub fn delegate_accessor(name: &str, mut binder: SpIBinder) -> Result<SpIBinder> {
+ let instance = CString::new(name).unwrap();
+ let mut delegator = ptr::null_mut();
+ let status =
+ // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both pointers.
+ // `AServiceManager_addService` creates a new strong reference and copies
+ // the string, so both pointers need only be valid until the call returns.
+ unsafe { sys::ABinderRpc_Accessor_delegateAccessor(instance.as_ptr(),
+ binder.as_native_mut(), &mut delegator) };
+
+ status_result(status)?;
+
+ // Safety: `delegator` is either null or a valid, owned pointer at this
+ // point, so can be safely passed to `SpIBinder::from_raw`.
+ Ok(unsafe { SpIBinder::from_raw(delegator).expect("Expected valid binder at this point") })
+}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index bdb7e4a..489fa0a 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -945,6 +945,43 @@
assert!(deleted.load(Ordering::Relaxed));
}
+ #[test]
+ fn test_accessor_delegator_new_each_time() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let delegator_binder =
+ binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+ let delegator_binder2 =
+ binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+
+ // The delegate_accessor creates new delegators each time
+ assert!(delegator_binder != delegator_binder2);
+ }
+
+ #[test]
+ fn test_accessor_delegate_the_delegator() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let delegator_binder =
+ binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+ let delegator_binder2 =
+ binder::delegate_accessor("foo.service", delegator_binder.clone().unwrap());
+
+ assert!(delegator_binder.clone() == delegator_binder);
+ // The delegate_accessor creates new delegators each time. Even when they are delegators
+ // of delegators.
+ assert!(delegator_binder != delegator_binder2);
+ }
+
+ #[test]
+ fn test_accessor_delegator_wrong_name() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let delegator_binder =
+ binder::delegate_accessor("NOT.foo.service", accessor.as_binder().unwrap());
+ assert_eq!(delegator_binder, Err(StatusCode::NAME_NOT_FOUND));
+ }
+
#[tokio::test]
async fn reassociate_rust_binder_async() {
let service_name = "testing_service";
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 8b0dda3..28a3f65 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -731,6 +731,9 @@
"liblog",
"libutils",
],
+ static_libs: [
+ "libgmock",
+ ],
test_suites: [
"general-tests",
"vts",
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index bcab6de..970852c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -46,6 +46,7 @@
#include <linux/sched.h>
#include <sys/epoll.h>
+#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -110,6 +111,8 @@
BINDER_LIB_TEST_LINK_DEATH_TRANSACTION,
BINDER_LIB_TEST_WRITE_FILE_TRANSACTION,
BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION,
+ BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION,
+ BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION,
BINDER_LIB_TEST_EXIT_TRANSACTION,
BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
@@ -536,6 +539,30 @@
};
};
+ssize_t countFds() {
+ return std::distance(std::filesystem::directory_iterator("/proc/self/fd"),
+ std::filesystem::directory_iterator{});
+}
+
+struct FdLeakDetector {
+ int startCount;
+
+ FdLeakDetector() {
+ // This log statement is load bearing. We have to log something before
+ // counting FDs to make sure the logging system is initialized, otherwise
+ // the sockets it opens will look like a leak.
+ ALOGW("FdLeakDetector counting FDs.");
+ startCount = countFds();
+ }
+ ~FdLeakDetector() {
+ int endCount = countFds();
+ if (startCount != endCount) {
+ ADD_FAILURE() << "fd count changed (" << startCount << " -> " << endCount
+ << ") fd leak?";
+ }
+ }
+};
+
TEST_F(BinderLibTest, CannotUseBinderAfterFork) {
// EXPECT_DEATH works by forking the process
EXPECT_DEATH({ ProcessState::self(); }, "libbinder ProcessState can not be used after fork");
@@ -1175,6 +1202,100 @@
EXPECT_EQ(0, read(read_end.get(), readbuf.data(), datasize));
}
+TEST_F(BinderLibTest, RecvOwnedFileDescriptors) {
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION, data,
+ &reply));
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvOwnedFileDescriptorsAndWriteInt) {
+ GTEST_SKIP() << "triggers fdsan false positive: b/370824489";
+
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION, data,
+ &reply));
+ reply.setDataPosition(reply.dataSize());
+ reply.writeInt32(0);
+ reply.setDataPosition(0);
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvOwnedFileDescriptorsAndTruncate) {
+ GTEST_SKIP() << "triggers fdsan false positive: b/370824489";
+
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION, data,
+ &reply));
+ reply.setDataSize(reply.dataSize() - sizeof(flat_binder_object));
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(BAD_TYPE, reply.readUniqueFileDescriptor(&b));
+}
+
+TEST_F(BinderLibTest, RecvUnownedFileDescriptors) {
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION, data,
+ &reply));
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvUnownedFileDescriptorsAndWriteInt) {
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION, data,
+ &reply));
+ reply.setDataPosition(reply.dataSize());
+ reply.writeInt32(0);
+ reply.setDataPosition(0);
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvUnownedFileDescriptorsAndTruncate) {
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION, data,
+ &reply));
+ reply.setDataSize(reply.dataSize() - sizeof(flat_binder_object));
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(BAD_TYPE, reply.readUniqueFileDescriptor(&b));
+}
+
TEST_F(BinderLibTest, PromoteLocal) {
sp<IBinder> strong = new BBinder();
wp<IBinder> weak = strong;
@@ -2224,6 +2345,40 @@
if (ret != size) return UNKNOWN_ERROR;
return NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION: {
+ unique_fd fd1(memfd_create("memfd1", MFD_CLOEXEC));
+ if (!fd1.ok()) {
+ PLOGE("memfd_create failed");
+ return UNKNOWN_ERROR;
+ }
+ unique_fd fd2(memfd_create("memfd2", MFD_CLOEXEC));
+ if (!fd2.ok()) {
+ PLOGE("memfd_create failed");
+ return UNKNOWN_ERROR;
+ }
+ status_t ret;
+ ret = reply->writeFileDescriptor(fd1.release(), true);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ ret = reply->writeFileDescriptor(fd2.release(), true);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION: {
+ status_t ret;
+ ret = reply->writeFileDescriptor(STDOUT_FILENO, false);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ ret = reply->writeFileDescriptor(STDERR_FILENO, false);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION:
alarm(10);
return NO_ERROR;
@@ -2261,7 +2416,7 @@
if (ret != NO_ERROR) {
return ret;
}
- auto event = frozenStateChangeCallback->events.popWithTimeout(10ms);
+ auto event = frozenStateChangeCallback->events.popWithTimeout(1000ms);
if (!event.has_value()) {
return NOT_ENOUGH_DATA;
}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 077a33a..506fc71 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -75,6 +75,8 @@
constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0";
#endif
+constexpr char kKnownAidlService[] = "activity";
+
static std::string WaitStatusToString(int wstatus) {
if (WIFEXITED(wstatus)) {
return "exit status " + std::to_string(WEXITSTATUS(wstatus));
@@ -1549,22 +1551,47 @@
EXPECT_EQ(nullptr, ABinderRpc_ConnectionInfo_new(reinterpret_cast<const sockaddr*>(&addr), 0));
}
-TEST_P(BinderRpcAccessor, ARpcGetService) {
+TEST_F(BinderARpcNdk, ARpcDelegateAccessorWrongInstance) {
+ AccessorProviderData* data = new AccessorProviderData();
+ ABinderRpc_Accessor* accessor = getAccessor(kARpcInstance, data);
+ ASSERT_NE(accessor, nullptr);
+ AIBinder* localAccessorBinder = ABinderRpc_Accessor_asBinder(accessor);
+ EXPECT_NE(localAccessorBinder, nullptr);
+
+ AIBinder* delegatorBinder = nullptr;
+ binder_status_t status =
+ ABinderRpc_Accessor_delegateAccessor("bar", localAccessorBinder, &delegatorBinder);
+ EXPECT_EQ(status, NAME_NOT_FOUND);
+
+ AIBinder_decStrong(localAccessorBinder);
+ ABinderRpc_Accessor_delete(accessor);
+ delete data;
+}
+
+TEST_F(BinderARpcNdk, ARpcDelegateNonAccessor) {
+ auto service = defaultServiceManager()->checkService(String16(kKnownAidlService));
+ ASSERT_NE(nullptr, service);
+ ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(service));
+
+ AIBinder* delegatorBinder = nullptr;
+ binder_status_t status =
+ ABinderRpc_Accessor_delegateAccessor("bar", binder.get(), &delegatorBinder);
+
+ EXPECT_EQ(status, BAD_TYPE);
+}
+
+inline void getServiceTest(BinderRpcTestProcessSession& proc,
+ ABinderRpc_AccessorProvider_getAccessorCallback getAccessor) {
constexpr size_t kNumThreads = 10;
bool isDeleted = false;
- auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-
AccessorProviderData* data =
new AccessorProviderData{proc.proc->sessions[0].addr, proc.proc->sessions[0].addrLen,
&isDeleted};
-
ABinderRpc_AccessorProvider* provider =
ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices,
kARpcNumSupportedServices, data,
accessorProviderDataOnDelete);
-
EXPECT_NE(provider, nullptr);
EXPECT_FALSE(isDeleted);
@@ -1580,6 +1607,45 @@
waitForExtraSessionCleanup(proc);
}
+TEST_P(BinderRpcAccessor, ARpcGetService) {
+ constexpr size_t kNumThreads = 10;
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ getServiceTest(proc, getAccessor);
+}
+
+// Create accessors and wrap each of the accessors in a delegator
+ABinderRpc_Accessor* getDelegatedAccessor(const char* instance, void* cookie) {
+ ABinderRpc_Accessor* accessor = getAccessor(instance, cookie);
+ AIBinder* accessorBinder = ABinderRpc_Accessor_asBinder(accessor);
+ // Once we have a handle to the AIBinder which holds a reference to the
+ // underlying accessor IBinder, we can get rid of the ABinderRpc_Accessor
+ ABinderRpc_Accessor_delete(accessor);
+
+ AIBinder* delegatorBinder = nullptr;
+ binder_status_t status =
+ ABinderRpc_Accessor_delegateAccessor(instance, accessorBinder, &delegatorBinder);
+ // No longer need this AIBinder. The delegator has a reference to the
+ // underlying IBinder on success, and on failure we are done here.
+ AIBinder_decStrong(accessorBinder);
+ if (status != OK || delegatorBinder == nullptr) {
+ ALOGE("Unexpected behavior. Status: %s, delegator ptr: %p", statusToString(status).c_str(),
+ delegatorBinder);
+ return nullptr;
+ }
+
+ return ABinderRpc_Accessor_fromBinder(instance, delegatorBinder);
+}
+
+TEST_P(BinderRpcAccessor, ARpcGetServiceWithDelegator) {
+ constexpr size_t kNumThreads = 10;
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ getServiceTest(proc, getDelegatedAccessor);
+}
+
#endif // BINDER_WITH_KERNEL_IPC
#ifdef BINDER_RPC_TO_TRUSTY_TEST
@@ -1845,7 +1911,7 @@
ASSERT_NE(nullptr, sm);
// Any Java service with non-empty getInterfaceDescriptor() would do.
// Let's pick activity.
- auto binder = sm->checkService(String16("activity"));
+ auto binder = sm->checkService(String16(kKnownAidlService));
ASSERT_NE(nullptr, binder);
auto descriptor = binder->getInterfaceDescriptor();
ASSERT_GE(descriptor.size(), 0u);
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 0aa678d..849dc7c 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -40,6 +40,8 @@
#include <sys/eventfd.h>
#include <sys/prctl.h>
+#include <gmock/gmock.h>
+
using namespace std::chrono_literals; // NOLINT - google-build-using-namespace
using android::binder::unique_fd;
@@ -222,6 +224,7 @@
SetDeathToken = IBinder::FIRST_CALL_TRANSACTION,
ReturnsNoMemory,
LogicalNot,
+ LogicalNotVector,
ModifyEnum,
IncrementFlattenable,
IncrementLightFlattenable,
@@ -249,6 +252,7 @@
// These are ordered according to their corresponding methods in SafeInterface::ParcelHandler
virtual status_t logicalNot(bool a, bool* notA) const = 0;
+ virtual status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const = 0;
virtual status_t modifyEnum(TestEnum a, TestEnum* b) const = 0;
virtual status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const = 0;
virtual status_t increment(const TestLightFlattenable& a,
@@ -288,7 +292,14 @@
}
status_t logicalNot(bool a, bool* notA) const override {
ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
- return callRemote<decltype(&ISafeInterfaceTest::logicalNot)>(Tag::LogicalNot, a, notA);
+ using Signature = status_t (ISafeInterfaceTest::*)(bool, bool*) const;
+ return callRemote<Signature>(Tag::LogicalNot, a, notA);
+ }
+ status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<bool>&,
+ std::vector<bool>*) const;
+ return callRemote<Signature>(Tag::LogicalNotVector, a, notA);
}
status_t modifyEnum(TestEnum a, TestEnum* b) const override {
ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
@@ -406,6 +417,14 @@
*notA = !a;
return NO_ERROR;
}
+ status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ notA->clear();
+ for (bool value : a) {
+ notA->push_back(!value);
+ }
+ return NO_ERROR;
+ }
status_t modifyEnum(TestEnum a, TestEnum* b) const override {
ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
*b = (a == TestEnum::INITIAL) ? TestEnum::FINAL : TestEnum::INVALID;
@@ -513,7 +532,13 @@
return callLocal(data, reply, &ISafeInterfaceTest::returnsNoMemory);
}
case ISafeInterfaceTest::Tag::LogicalNot: {
- return callLocal(data, reply, &ISafeInterfaceTest::logicalNot);
+ using Signature = status_t (ISafeInterfaceTest::*)(bool a, bool* notA) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::logicalNot);
+ }
+ case ISafeInterfaceTest::Tag::LogicalNotVector: {
+ using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<bool>& a,
+ std::vector<bool>* notA) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::logicalNot);
}
case ISafeInterfaceTest::Tag::ModifyEnum: {
return callLocal(data, reply, &ISafeInterfaceTest::modifyEnum);
@@ -639,6 +664,15 @@
ASSERT_EQ(!b, notB);
}
+TEST_F(SafeInterfaceTest, TestLogicalNotVector) {
+ const std::vector<bool> a = {true, false, true};
+ std::vector<bool> notA;
+ status_t result = mSafeInterfaceTest->logicalNot(a, ¬A);
+ ASSERT_EQ(NO_ERROR, result);
+ std::vector<bool> expected = {false, true, false};
+ ASSERT_THAT(notA, testing::ContainerEq(expected));
+}
+
TEST_F(SafeInterfaceTest, TestModifyEnum) {
const TestEnum a = TestEnum::INITIAL;
TestEnum b = TestEnum::INVALID;
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 25e6a52..49f4cba 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -50,9 +50,28 @@
using namespace std::chrono_literals;
namespace {
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+// RAII wrapper to defer arbitrary work until the Deferred instance is deleted.
+template <class F>
+class Deferred {
+public:
+ explicit Deferred(F f) : mF{std::move(f)} {}
+
+ ~Deferred() { mF(); }
+
+ Deferred(const Deferred&) = delete;
+ Deferred& operator=(const Deferred&) = delete;
+
+private:
+ F mF;
+};
+#endif
+
inline const char* boolToString(bool b) {
return b ? "true" : "false";
}
+
} // namespace
namespace android {
@@ -77,12 +96,6 @@
std::unique_lock _lock{mutex}; \
base::ScopedLockAssertion assumeLocked(mutex);
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-static ReleaseBufferCallback EMPTY_RELEASE_CALLBACK =
- [](const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
- std::optional<uint32_t> /*currentMaxAcquiredBufferCount*/) {};
-#endif
-
void BLASTBufferItemConsumer::onDisconnect() {
Mutex::Autolock lock(mMutex);
mPreviouslyConnected = mCurrentlyConnected;
@@ -225,9 +238,8 @@
this);
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
- std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> bufferReleaseConsumer;
- gui::BufferReleaseChannel::open(mName, bufferReleaseConsumer, mBufferReleaseProducer);
- mBufferReleaseReader = std::make_shared<BufferReleaseReader>(std::move(bufferReleaseConsumer));
+ gui::BufferReleaseChannel::open(mName, mBufferReleaseConsumer, mBufferReleaseProducer);
+ mBufferReleaseReader.emplace(*this);
#endif
BQA_LOGV("BLASTBufferQueue created");
@@ -260,7 +272,7 @@
// safe default, most producers are expected to override this
mProducer->setMaxDequeuedBufferCount(2);
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
- mBufferReleaseThread.start(sp<BLASTBufferQueue>::fromExisting(this));
+ mBufferReleaseThread.emplace(sp<BLASTBufferQueue>::fromExisting(this));
#endif
}
@@ -636,7 +648,7 @@
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
ReleaseBufferCallback releaseBufferCallback =
- applyTransaction ? EMPTY_RELEASE_CALLBACK : makeReleaseBufferCallbackThunk();
+ applyTransaction ? nullptr : makeReleaseBufferCallbackThunk();
#else
auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
#endif
@@ -1137,6 +1149,24 @@
#endif
};
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+class BBQBufferQueueCore : public BufferQueueCore {
+public:
+ explicit BBQBufferQueueCore(const wp<BLASTBufferQueue>& bbq) : mBLASTBufferQueue{bbq} {}
+
+ void notifyBufferReleased() const override {
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return;
+ }
+ bbq->mBufferReleaseReader->interruptBlockingRead();
+ }
+
+private:
+ wp<BLASTBufferQueue> mBLASTBufferQueue;
+};
+#endif
+
// Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls
// can be non-blocking when the producer is in the client process.
class BBQBufferQueueProducer : public BufferQueueProducer {
@@ -1188,6 +1218,44 @@
return BufferQueueProducer::query(what, value);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ status_t waitForBufferRelease(std::unique_lock<std::mutex>& bufferQueueLock,
+ nsecs_t timeout) const override {
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return OK;
+ }
+
+ // BufferQueue has already checked if we have a free buffer. If there's an unread interrupt,
+ // we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure
+ // we don't miss an interrupt.
+ bbq->mBufferReleaseReader->clearInterrupts();
+ bbq->mThreadsBlockingOnDequeue++;
+ bufferQueueLock.unlock();
+ Deferred cleanup{[&]() {
+ bufferQueueLock.lock();
+ bbq->mThreadsBlockingOnDequeue--;
+ }};
+
+ ATRACE_FORMAT("waiting for free buffer");
+ ReleaseCallbackId id;
+ sp<Fence> fence;
+ uint32_t maxAcquiredBufferCount;
+ status_t status =
+ bbq->mBufferReleaseReader->readBlocking(id, fence, maxAcquiredBufferCount, timeout);
+ if (status == TIMED_OUT) {
+ return TIMED_OUT;
+ } else if (status != OK) {
+ // Waiting was interrupted or an error occurred. BufferQueueProducer will check if we
+ // have a free buffer and call this method again if not.
+ return OK;
+ }
+
+ bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount);
+ return OK;
+ }
+#endif
+
private:
const wp<BLASTBufferQueue> mBLASTBufferQueue;
};
@@ -1201,14 +1269,18 @@
LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BLASTBufferQueue: outProducer must not be NULL");
LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BLASTBufferQueue: outConsumer must not be NULL");
- sp<BufferQueueCore> core(new BufferQueueCore());
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ auto core = sp<BBQBufferQueueCore>::make(this);
+#else
+ auto core = sp<BufferQueueCore>::make();
+#endif
LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore");
- sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core, this));
+ auto producer = sp<BBQBufferQueueProducer>::make(core, this);
LOG_ALWAYS_FATAL_IF(producer == nullptr,
"BLASTBufferQueue: failed to create BBQBufferQueueProducer");
- sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));
+ auto consumer = sp<BufferQueueConsumer>::make(core);
consumer->setAllowExtraAcquire(true);
LOG_ALWAYS_FATAL_IF(consumer == nullptr,
"BLASTBufferQueue: failed to create BufferQueueConsumer");
@@ -1273,10 +1345,8 @@
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(
- std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> endpoint)
- : mEndpoint{std::move(endpoint)} {
- mEpollFd = android::base::unique_fd{epoll_create1(0)};
+BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} {
+ mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)};
LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(),
"Failed to create buffer release epoll file descriptor. errno=%d "
"message='%s'",
@@ -1284,9 +1354,9 @@
epoll_event registerEndpointFd{};
registerEndpointFd.events = EPOLLIN;
- registerEndpointFd.data.fd = mEndpoint->getFd();
- status_t status =
- epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mEndpoint->getFd(), ®isterEndpointFd);
+ registerEndpointFd.data.fd = mBbq.mBufferReleaseConsumer->getFd();
+ status_t status = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mBbq.mBufferReleaseConsumer->getFd(),
+ ®isterEndpointFd);
LOG_ALWAYS_FATAL_IF(status == -1,
"Failed to register buffer release consumer file descriptor with epoll. "
"errno=%d message='%s'",
@@ -1308,78 +1378,153 @@
errno, strerror(errno));
}
-BLASTBufferQueue::BufferReleaseReader& BLASTBufferQueue::BufferReleaseReader::operator=(
- BufferReleaseReader&& other) {
- if (this != &other) {
- ftl::FakeGuard guard{mMutex};
- ftl::FakeGuard otherGuard{other.mMutex};
- mEndpoint = std::move(other.mEndpoint);
- mEpollFd = std::move(other.mEpollFd);
- mEventFd = std::move(other.mEventFd);
- }
- return *this;
-}
-
status_t BLASTBufferQueue::BufferReleaseReader::readBlocking(ReleaseCallbackId& outId,
sp<Fence>& outFence,
- uint32_t& outMaxAcquiredBufferCount) {
+ uint32_t& outMaxAcquiredBufferCount,
+ nsecs_t timeout) {
+ // TODO(b/363290953) epoll_wait only has millisecond timeout precision. If timeout is less than
+ // 1ms, then we round timeout up to 1ms. Otherwise, we round timeout to the nearest
+ // millisecond. Once epoll_pwait2 can be used in libgui, we can specify timeout with nanosecond
+ // precision.
+ int timeoutMs = -1;
+ if (timeout == 0) {
+ timeoutMs = 0;
+ } else if (timeout > 0) {
+ const int nsPerMs = 1000000;
+ if (timeout < nsPerMs) {
+ timeoutMs = 1;
+ } else {
+ timeoutMs = static_cast<int>(
+ std::chrono::round<std::chrono::milliseconds>(std::chrono::nanoseconds{timeout})
+ .count());
+ }
+ }
+
epoll_event event{};
- while (true) {
- int eventCount = epoll_wait(mEpollFd.get(), &event, 1 /* maxevents */, -1 /* timeout */);
- if (eventCount == 1) {
- break;
- }
- if (eventCount == -1 && errno != EINTR) {
- ALOGE("epoll_wait error while waiting for buffer release. errno=%d message='%s'", errno,
- strerror(errno));
- }
+ int eventCount;
+ do {
+ eventCount = epoll_wait(mEpollFd.get(), &event, 1 /*maxevents*/, timeoutMs);
+ } while (eventCount == -1 && errno != EINTR);
+
+ if (eventCount == -1) {
+ ALOGE("epoll_wait error while waiting for buffer release. errno=%d message='%s'", errno,
+ strerror(errno));
+ return UNKNOWN_ERROR;
+ }
+
+ if (eventCount == 0) {
+ return TIMED_OUT;
}
if (event.data.fd == mEventFd.get()) {
- uint64_t value;
- if (read(mEventFd.get(), &value, sizeof(uint64_t)) == -1 && errno != EWOULDBLOCK) {
- ALOGE("error while reading from eventfd. errno=%d message='%s'", errno,
- strerror(errno));
- }
+ clearInterrupts();
return WOULD_BLOCK;
}
- std::lock_guard lock{mMutex};
- return mEndpoint->readReleaseFence(outId, outFence, outMaxAcquiredBufferCount);
+ return mBbq.mBufferReleaseConsumer->readReleaseFence(outId, outFence,
+ outMaxAcquiredBufferCount);
}
void BLASTBufferQueue::BufferReleaseReader::interruptBlockingRead() {
- uint64_t value = 1;
- if (write(mEventFd.get(), &value, sizeof(uint64_t)) == -1) {
+ if (eventfd_write(mEventFd.get(), 1) == -1) {
ALOGE("failed to notify dequeue event. errno=%d message='%s'", errno, strerror(errno));
}
}
-void BLASTBufferQueue::BufferReleaseThread::start(const sp<BLASTBufferQueue>& bbq) {
- mRunning = std::make_shared<std::atomic_bool>(true);
- mReader = bbq->mBufferReleaseReader;
- std::thread([running = mRunning, reader = mReader, weakBbq = wp<BLASTBufferQueue>(bbq)]() {
+void BLASTBufferQueue::BufferReleaseReader::clearInterrupts() {
+ eventfd_t value;
+ if (eventfd_read(mEventFd.get(), &value) == -1 && errno != EWOULDBLOCK) {
+ ALOGE("error while reading from eventfd. errno=%d message='%s'", errno, strerror(errno));
+ }
+}
+
+BLASTBufferQueue::BufferReleaseThread::BufferReleaseThread(const sp<BLASTBufferQueue>& bbq) {
+ android::base::unique_fd epollFd{epoll_create1(EPOLL_CLOEXEC)};
+ LOG_ALWAYS_FATAL_IF(!epollFd.ok(),
+ "Failed to create buffer release background thread epoll file descriptor. "
+ "errno=%d message='%s'",
+ errno, strerror(errno));
+
+ epoll_event registerEndpointFd{};
+ registerEndpointFd.events = EPOLLIN;
+ registerEndpointFd.data.fd = bbq->mBufferReleaseConsumer->getFd();
+ status_t status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, bbq->mBufferReleaseConsumer->getFd(),
+ ®isterEndpointFd);
+ LOG_ALWAYS_FATAL_IF(status == -1,
+ "Failed to register background thread buffer release consumer file "
+ "descriptor with epoll. errno=%d message='%s'",
+ errno, strerror(errno));
+
+ // EventFd is used to break the background thread's loop.
+ android::base::unique_fd eventFd{eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)};
+ LOG_ALWAYS_FATAL_IF(!eventFd.ok(),
+ "Failed to create background thread buffer release event file descriptor. "
+ "errno=%d message='%s'",
+ errno, strerror(errno));
+
+ epoll_event registerEventFd{};
+ registerEventFd.events = EPOLLIN;
+ registerEventFd.data.fd = eventFd.get();
+ status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), ®isterEventFd);
+ LOG_ALWAYS_FATAL_IF(status == -1,
+ "Failed to register background thread event file descriptor with epoll. "
+ "errno=%d message='%s'",
+ errno, strerror(errno));
+
+ mEventFd = eventFd.get();
+
+ std::thread([epollFd = std::move(epollFd), eventFd = std::move(eventFd),
+ weakBbq = wp<BLASTBufferQueue>(bbq)]() {
pthread_setname_np(pthread_self(), "BufferReleaseThread");
- while (*running) {
- ReleaseCallbackId id;
- sp<Fence> fence;
- uint32_t maxAcquiredBufferCount;
- if (status_t status = reader->readBlocking(id, fence, maxAcquiredBufferCount);
- status != OK) {
+ while (true) {
+ epoll_event event{};
+ int eventCount;
+ do {
+ eventCount = epoll_wait(epollFd.get(), &event, 1 /*maxevents*/, -1 /*timeout*/);
+ } while (eventCount == -1 && errno != EINTR);
+
+ if (eventCount == -1) {
+ ALOGE("epoll_wait error while waiting for buffer release in background thread. "
+ "errno=%d message='%s'",
+ errno, strerror(errno));
continue;
}
+
+ // EventFd is used to join this thread.
+ if (event.data.fd == eventFd.get()) {
+ return;
+ }
+
sp<BLASTBufferQueue> bbq = weakBbq.promote();
if (!bbq) {
return;
}
+
+ // If there are threads blocking on dequeue, give those threads priority for handling
+ // the release.
+ if (bbq->mThreadsBlockingOnDequeue > 0) {
+ std::this_thread::sleep_for(0ms);
+ continue;
+ }
+
+ ReleaseCallbackId id;
+ sp<Fence> fence;
+ uint32_t maxAcquiredBufferCount;
+ status_t status = bbq->mBufferReleaseConsumer->readReleaseFence(id, fence,
+ maxAcquiredBufferCount);
+ if (status != OK) {
+ ALOGE("failed to read from buffer release consumer in background thread. errno=%d "
+ "message='%s'",
+ errno, strerror(errno));
+ continue;
+ }
bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount);
}
}).detach();
}
BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() {
- *mRunning = false;
- mReader->interruptBlockingRead();
+ eventfd_write(mEventFd, 1);
}
#endif
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 69d25be..d0607bf 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -297,7 +297,11 @@
// We might have freed a slot while dropping old buffers, or the producer
// may be blocked waiting for the number of buffers in the queue to
// decrease.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mCore->notifyBufferReleased();
+#else
mCore->mDequeueCondition.notify_all();
+#endif
ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size()));
#ifndef NO_BINDER
@@ -350,7 +354,12 @@
mCore->mActiveBuffers.erase(slot);
mCore->mFreeSlots.insert(slot);
mCore->clearBufferSlotLocked(slot);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mCore->notifyBufferReleased();
+#else
mCore->mDequeueCondition.notify_all();
+#endif
+
VALIDATE_CONSISTENCY();
}
@@ -520,7 +529,12 @@
}
BQ_LOGV("releaseBuffer: releasing slot %d", slot);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mCore->notifyBufferReleased();
+#else
mCore->mDequeueCondition.notify_all();
+#endif
+
VALIDATE_CONSISTENCY();
} // Autolock scope
@@ -574,7 +588,11 @@
mCore->mQueue.clear();
mCore->freeAllBuffersLocked();
mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mCore->notifyBufferReleased();
+#else
mCore->mDequeueCondition.notify_all();
+#endif
return NO_ERROR;
}
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index e0c5b1f..d52cf70 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -371,6 +371,12 @@
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+void BufferQueueCore::notifyBufferReleased() const {
+ mDequeueCondition.notify_all();
+}
+#endif
+
#if DEBUG_ONLY_CODE
void BufferQueueCore::validateConsistencyLocked() const {
static const useconds_t PAUSE_TIME = 0;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index da74e9c..473a374 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -202,7 +202,11 @@
if (delta < 0) {
listener = mCore->mConsumerListener;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mCore->notifyBufferReleased();
+#else
mCore->mDequeueCondition.notify_all();
+#endif
} // Autolock scope
// Call back without lock held
@@ -254,7 +258,12 @@
}
mCore->mAsyncMode = async;
VALIDATE_CONSISTENCY();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mCore->notifyBufferReleased();
+#else
mCore->mDequeueCondition.notify_all();
+#endif
+
if (delta < 0) {
listener = mCore->mConsumerListener;
}
@@ -376,6 +385,12 @@
(acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
return WOULD_BLOCK;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ if (status_t status = waitForBufferRelease(lock, mDequeueTimeout);
+ status == TIMED_OUT) {
+ return TIMED_OUT;
+ }
+#else
if (mDequeueTimeout >= 0) {
std::cv_status result = mCore->mDequeueCondition.wait_for(lock,
std::chrono::nanoseconds(mDequeueTimeout));
@@ -385,12 +400,29 @@
} else {
mCore->mDequeueCondition.wait(lock);
}
+#endif
}
} // while (tryAgain)
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+status_t BufferQueueProducer::waitForBufferRelease(std::unique_lock<std::mutex>& lock,
+ nsecs_t timeout) const {
+ if (mDequeueTimeout >= 0) {
+ std::cv_status result =
+ mCore->mDequeueCondition.wait_for(lock, std::chrono::nanoseconds(timeout));
+ if (result == std::cv_status::timeout) {
+ return TIMED_OUT;
+ }
+ } else {
+ mCore->mDequeueCondition.wait(lock);
+ }
+ return OK;
+}
+#endif
+
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
uint64_t usage, uint64_t* outBufferAge,
@@ -741,7 +773,11 @@
mCore->mActiveBuffers.erase(slot);
mCore->mFreeSlots.insert(slot);
mCore->clearBufferSlotLocked(slot);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mCore->notifyBufferReleased();
+#else
mCore->mDequeueCondition.notify_all();
+#endif
VALIDATE_CONSISTENCY();
}
@@ -1082,7 +1118,11 @@
}
mCore->mBufferHasBeenQueued = true;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mCore->notifyBufferReleased();
+#else
mCore->mDequeueCondition.notify_all();
+#endif
mCore->mLastQueuedSlot = slot;
output->width = mCore->mDefaultWidth;
@@ -1218,7 +1258,11 @@
bufferId = gb->getId();
}
mSlots[slot].mFence = fence;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mCore->notifyBufferReleased();
+#else
mCore->mDequeueCondition.notify_all();
+#endif
listener = mCore->mConsumerListener;
VALIDATE_CONSISTENCY();
}
@@ -1457,7 +1501,11 @@
mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
mCore->mConnectedPid = -1;
mCore->mSidebandStream.clear();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mCore->notifyBufferReleased();
+#else
mCore->mDequeueCondition.notify_all();
+#endif
mCore->mAutoPrerotation = false;
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
mCore->mAdditionalOptions.clear();
@@ -1508,7 +1556,6 @@
const bool useDefaultSize = !width && !height;
while (true) {
- size_t newBufferCount = 0;
uint32_t allocWidth = 0;
uint32_t allocHeight = 0;
PixelFormat allocFormat = PIXEL_FORMAT_UNKNOWN;
@@ -1530,8 +1577,9 @@
// Only allocate one buffer at a time to reduce risks of overlapping an allocation from
// both allocateBuffers and dequeueBuffer.
- newBufferCount = mCore->mFreeSlots.empty() ? 0 : 1;
- if (newBufferCount == 0) {
+ if (mCore->mFreeSlots.empty()) {
+ BQ_LOGV("allocateBuffers: a slot was occupied while "
+ "allocating. Dropping allocated buffer.");
return;
}
@@ -1573,27 +1621,23 @@
};
#endif
- Vector<sp<GraphicBuffer>> buffers;
- for (size_t i = 0; i < newBufferCount; ++i) {
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
#else
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
- allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
- allocUsage, allocName);
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+ allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+ allocUsage, allocName);
#endif
- status_t result = graphicBuffer->initCheck();
+ status_t result = graphicBuffer->initCheck();
- if (result != NO_ERROR) {
- BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
- " %u, usage %#" PRIx64 ")", width, height, format, usage);
- std::lock_guard<std::mutex> lock(mCore->mMutex);
- mCore->mIsAllocating = false;
- mCore->mIsAllocatingCondition.notify_all();
- return;
- }
- buffers.push_back(graphicBuffer);
+ if (result != NO_ERROR) {
+ BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
+ " %u, usage %#" PRIx64 ")", width, height, format, usage);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+ mCore->mIsAllocating = false;
+ mCore->mIsAllocatingCondition.notify_all();
+ return;
}
{ // Autolock scope
@@ -1621,15 +1665,13 @@
continue;
}
- for (size_t i = 0; i < newBufferCount; ++i) {
- if (mCore->mFreeSlots.empty()) {
- BQ_LOGV("allocateBuffers: a slot was occupied while "
- "allocating. Dropping allocated buffer.");
- continue;
- }
+ if (mCore->mFreeSlots.empty()) {
+ BQ_LOGV("allocateBuffers: a slot was occupied while "
+ "allocating. Dropping allocated buffer.");
+ } else {
auto slot = mCore->mFreeSlots.begin();
mCore->clearBufferSlotLocked(*slot); // Clean up the slot first
- mSlots[*slot].mGraphicBuffer = buffers[i];
+ mSlots[*slot].mGraphicBuffer = graphicBuffer;
mSlots[*slot].mFence = Fence::NO_FENCE;
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
mSlots[*slot].mAdditionalOptionsGenerationId = allocOptionsGenId;
diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
index 27367aa..e9c6ef3 100644
--- a/libs/gui/BufferReleaseChannel.cpp
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -136,6 +136,7 @@
status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
ReleaseCallbackId& outReleaseCallbackId, sp<Fence>& outReleaseFence,
uint32_t& outMaxAcquiredBufferCount) {
+ std::lock_guard lock{mMutex};
Message message;
mFlattenedBuffer.resize(message.getFlattenedSize());
std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
@@ -152,7 +153,7 @@
.msg_controllen = controlMessageBuffer.size(),
};
- int result;
+ ssize_t result;
do {
result = recvmsg(mFd, &msg, 0);
} while (result == -1 && errno == EINTR);
@@ -242,7 +243,7 @@
memcpy(CMSG_DATA(cmsg), &flattenedFd, sizeof(int));
}
- int result;
+ ssize_t result;
do {
result = sendmsg(mFd, &msg, 0);
} while (result == -1 && errno == EINTR);
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index b109969..422c57b 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -69,7 +69,7 @@
color(0),
bufferTransform(0),
transformToDisplayInverse(false),
- crop(Rect::INVALID_RECT),
+ crop({0, 0, -1, -1}),
dataspace(ui::Dataspace::UNKNOWN),
surfaceDamageRegion(),
api(-1),
@@ -109,7 +109,10 @@
SAFE_PARCEL(output.writeUint32, flags);
SAFE_PARCEL(output.writeUint32, mask);
SAFE_PARCEL(matrix.write, output);
- SAFE_PARCEL(output.write, crop);
+ SAFE_PARCEL(output.writeFloat, crop.top);
+ SAFE_PARCEL(output.writeFloat, crop.left);
+ SAFE_PARCEL(output.writeFloat, crop.bottom);
+ SAFE_PARCEL(output.writeFloat, crop.right);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
SAFE_PARCEL(output.writeFloat, color.r);
@@ -218,7 +221,10 @@
SAFE_PARCEL(input.readUint32, &mask);
SAFE_PARCEL(matrix.read, input);
- SAFE_PARCEL(input.read, crop);
+ SAFE_PARCEL(input.readFloat, &crop.top);
+ SAFE_PARCEL(input.readFloat, &crop.left);
+ SAFE_PARCEL(input.readFloat, &crop.bottom);
+ SAFE_PARCEL(input.readFloat, &crop.right);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b976d81..eeea80f 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1645,6 +1645,11 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
const sp<SurfaceControl>& sc, const Rect& crop) {
+ return setCrop(sc, crop.toFloatRect());
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
+ const sp<SurfaceControl>& sc, const FloatRect& crop) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1934,6 +1939,20 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts(
+ const sp<SurfaceControl>& sc, const base::unique_fd& /*lutFd*/,
+ const std::vector<int32_t>& /*offsets*/, const std::vector<int32_t>& /*dimensions*/,
+ const std::vector<int32_t>& /*sizes*/, const std::vector<int32_t>& /*samplingKeys*/) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ // TODO (b/329472856): update layer_state_t for lut(s)
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachingHint(
const sp<SurfaceControl>& sc, gui::CachingHint cachingHint) {
layer_state_t* s = getLayerState(sc);
@@ -2788,6 +2807,7 @@
outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+ outInfo->hasArrSupport = ginfo.hasArrSupport;
}
status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId,
diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
index 3114929..70873b0 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -43,4 +43,7 @@
// The boot display mode preferred by the implementation.
int preferredBootDisplayMode;
+
+ // Represents whether display supports ARR.
+ boolean hasArrSupport;
}
diff --git a/libs/gui/aidl/android/gui/Lut.aidl b/libs/gui/aidl/android/gui/Lut.aidl
new file mode 100644
index 0000000..a06e521
--- /dev/null
+++ b/libs/gui/aidl/android/gui/Lut.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.LutProperties;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * This mirrors aidl::android::hardware::graphics::composer3::Lut definition
+ * @hide
+ */
+parcelable Lut {
+ @nullable ParcelFileDescriptor pfd;
+
+ LutProperties lutProperties;
+}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/LutProperties.aidl b/libs/gui/aidl/android/gui/LutProperties.aidl
new file mode 100644
index 0000000..561e0c0
--- /dev/null
+++ b/libs/gui/aidl/android/gui/LutProperties.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/**
+ * This mirrors aidl::android::hardware::graphics::composer3::LutProperties definition.
+ * @hide
+ */
+parcelable LutProperties {
+ @Backing(type="int")
+ enum Dimension { ONE_D = 1, THREE_D = 3 }
+ Dimension dimension;
+
+ long size;
+ @Backing(type="int")
+ enum SamplingKey { RGB, MAX_RGB }
+ SamplingKey[] samplingKeys;
+}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/OverlayProperties.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl
index 5fb1a83..7f41bda 100644
--- a/libs/gui/aidl/android/gui/OverlayProperties.aidl
+++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl
@@ -16,6 +16,8 @@
package android.gui;
+import android.gui.LutProperties;
+
/** @hide */
parcelable OverlayProperties {
parcelable SupportedBufferCombinations {
@@ -27,4 +29,6 @@
SupportedBufferCombinations[] combinations;
boolean supportMixedColorSpaces;
+
+ @nullable LutProperties[] lutProperties;
}
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 8592cff..99c64da 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -150,6 +150,9 @@
private:
friend class BLASTBufferQueueHelper;
friend class BBQBufferQueueProducer;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ friend class BBQBufferQueueCore;
+#endif
// can't be copied
BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
@@ -317,48 +320,52 @@
std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to the
+ // client.
+ std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer;
+ std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer;
+
class BufferReleaseReader {
public:
- BufferReleaseReader() = default;
- BufferReleaseReader(std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint>);
- BufferReleaseReader& operator=(BufferReleaseReader&&);
+ explicit BufferReleaseReader(BLASTBufferQueue&);
+
+ BufferReleaseReader(const BufferReleaseReader&) = delete;
+ BufferReleaseReader& operator=(const BufferReleaseReader&) = delete;
// Block until we can read a buffer release message.
//
// Returns:
// * OK if a ReleaseCallbackId and Fence were successfully read.
// * WOULD_BLOCK if the blocking read was interrupted by interruptBlockingRead.
+ // * TIMED_OUT if the blocking read timed out.
// * UNKNOWN_ERROR if something went wrong.
status_t readBlocking(ReleaseCallbackId& outId, sp<Fence>& outReleaseFence,
- uint32_t& outMaxAcquiredBufferCount);
+ uint32_t& outMaxAcquiredBufferCount, nsecs_t timeout);
- // Signals the reader's eventfd to wake up any threads waiting on readBlocking.
void interruptBlockingRead();
+ void clearInterrupts();
private:
- std::mutex mMutex;
- std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mEndpoint GUARDED_BY(mMutex);
+ BLASTBufferQueue& mBbq;
+
android::base::unique_fd mEpollFd;
android::base::unique_fd mEventFd;
};
- // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to
- // the client. See BBQBufferQueueProducer::dequeueBuffer for details.
- std::shared_ptr<BufferReleaseReader> mBufferReleaseReader;
- std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer;
+ std::optional<BufferReleaseReader> mBufferReleaseReader;
+
+ std::atomic<int> mThreadsBlockingOnDequeue = 0;
class BufferReleaseThread {
public:
- BufferReleaseThread() = default;
+ BufferReleaseThread(const sp<BLASTBufferQueue>&);
~BufferReleaseThread();
- void start(const sp<BLASTBufferQueue>&);
private:
- std::shared_ptr<std::atomic_bool> mRunning;
- std::shared_ptr<BufferReleaseReader> mReader;
+ int mEventFd;
};
- BufferReleaseThread mBufferReleaseThread;
+ std::optional<BufferReleaseThread> mBufferReleaseThread;
#endif
};
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index d5dd7c8..77cdf2c 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -80,6 +80,13 @@
BufferQueueCore();
virtual ~BufferQueueCore();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+protected:
+ // Wake up any threads waiting for a buffer release. The BufferQueue mutex should always held
+ // when this method is called.
+ virtual void notifyBufferReleased() const;
+#endif
+
private:
// Dump our state in a string
void dumpState(const String8& prefix, String8* outResult) const;
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 37a9607..086ce7c 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -218,6 +218,14 @@
// total maximum buffer count for the buffer queue (dequeued AND acquired)
status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers, int* maxBufferCount);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ // Wait until a buffer has been released. The method may spuriously return OK when no buffer has
+ // been released. The BufferQueue mutex is passed in the locked state. It must be unlocked
+ // before waiting for a release and locked before returning.
+ virtual status_t waitForBufferRelease(std::unique_lock<std::mutex>& lock,
+ nsecs_t timeout) const;
+#endif
+
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
diff --git a/libs/gui/include/gui/BufferReleaseChannel.h b/libs/gui/include/gui/BufferReleaseChannel.h
index 51fe0b6..0edadec 100644
--- a/libs/gui/include/gui/BufferReleaseChannel.h
+++ b/libs/gui/include/gui/BufferReleaseChannel.h
@@ -69,7 +69,8 @@
sp<Fence>& outReleaseFence, uint32_t& maxAcquiredBufferCount);
private:
- std::vector<uint8_t> mFlattenedBuffer;
+ std::mutex mMutex;
+ std::vector<uint8_t> mFlattenedBuffer GUARDED_BY(mMutex);
};
class ProducerEndpoint : public Endpoint, public Parcelable {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 2cdde32..00065c8 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -330,7 +330,7 @@
Region transparentRegion;
uint32_t bufferTransform;
bool transformToDisplayInverse;
- Rect crop;
+ FloatRect crop;
std::shared_ptr<BufferData> bufferData = nullptr;
ui::Dataspace dataspace;
HdrMetadata hdrMetadata;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 4f9af16..5ea0c16 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -549,6 +549,7 @@
Transaction& setMatrix(const sp<SurfaceControl>& sc,
float dsdx, float dtdx, float dtdy, float dsdy);
Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
+ Transaction& setCrop(const sp<SurfaceControl>& sc, const FloatRect& crop);
Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
int backgroundBlurRadius);
@@ -601,6 +602,11 @@
Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc,
float currentBufferRatio, float desiredRatio);
Transaction& setDesiredHdrHeadroom(const sp<SurfaceControl>& sc, float desiredRatio);
+ Transaction& setLuts(const sp<SurfaceControl>& sc, const base::unique_fd& lutFd,
+ const std::vector<int32_t>& offsets,
+ const std::vector<int32_t>& dimensions,
+ const std::vector<int32_t>& sizes,
+ const std::vector<int32_t>& samplingKeys);
Transaction& setCachingHint(const sp<SurfaceControl>& sc, gui::CachingHint cachingHint);
Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index d3f2899..1c7e0e4 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -109,6 +109,14 @@
} # wb_libcameraservice
flag {
+ name: "wb_unlimited_slots"
+ namespace: "core_graphics"
+ description: "Adds APIs and updates the implementation of bufferqueues to have a user-defined slot count."
+ bug: "341359814"
+ is_fixed_read_only: true
+} # wb_unlimited_slots
+
+flag {
name: "bq_producer_throttles_only_async_mode"
namespace: "core_graphics"
description: "BufferQueueProducer only CPU throttle on queueBuffer() in async mode."
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 845a1ca..3b6a66e 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -30,6 +30,7 @@
static constexpr int kHeight = 100;
static constexpr int kMaxLockedBuffers = 3;
static constexpr int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+static constexpr int kUsage = GRALLOC_USAGE_SW_READ_RARELY;
static constexpr int kFrameSleepUs = 30 * 1000;
class BufferItemConsumerTest : public ::testing::Test {
@@ -56,7 +57,7 @@
};
void SetUp() override {
- mBIC = new BufferItemConsumer(kFormat, kMaxLockedBuffers, true);
+ mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true);
String8 name("BufferItemConsumer_Under_Test");
mBIC->setName(name);
mBFL = new BufferFreedListener(this);
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index f98437b..8fea689 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -46,7 +46,8 @@
SurfaceComposerClient::Transaction{}
.setLayer(mButton, 0x7fffffff)
- .setCrop(mButton, {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
+ .setCrop(mButton,
+ Rect{0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
.setPosition(mButton, samplingArea.left + BUTTON_PADDING,
samplingArea.top + BUTTON_PADDING)
.setColor(mButton, half3{1, 1, 1})
@@ -59,7 +60,8 @@
SurfaceComposerClient::Transaction{}
.setLayer(mButtonBlend, 0x7ffffffe)
.setCrop(mButtonBlend,
- {0, 0, width - 2 * SAMPLE_AREA_PADDING, height - 2 * SAMPLE_AREA_PADDING})
+ Rect{0, 0, width - 2 * SAMPLE_AREA_PADDING,
+ height - 2 * SAMPLE_AREA_PADDING})
.setPosition(mButtonBlend, samplingArea.left + SAMPLE_AREA_PADDING,
samplingArea.top + SAMPLE_AREA_PADDING)
.setColor(mButtonBlend, half3{1, 1, 1})
@@ -75,7 +77,7 @@
SurfaceComposerClient::Transaction{}
.setLayer(mSamplingArea, 0x7ffffffd)
- .setCrop(mSamplingArea, {0, 0, 100, 32})
+ .setCrop(mSamplingArea, Rect{0, 0, 100, 32})
.setPosition(mSamplingArea, 490, 1606)
.setColor(mSamplingArea, half3{0, 1, 0})
.setAlpha(mSamplingArea, 0.1)
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index ce8bb43..9665de7 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -193,13 +193,29 @@
InputConsumerNoResampling::~InputConsumerNoResampling() {
ensureCalledOnLooperThread(__func__);
- consumeBatchedInputEvents(/*requestedFrameTime=*/std::nullopt);
while (!mOutboundQueue.empty()) {
processOutboundEvents();
// This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
// so keep trying to send the events as long as they are present in the queue.
}
+
setFdEvents(0);
+ // If there are any remaining unread batches, send an ack for them and don't deliver
+ // them to callbacks.
+ for (auto& [_, batches] : mBatches) {
+ while (!batches.empty()) {
+ finishInputEvent(batches.front().header.seq, /*handled=*/false);
+ batches.pop();
+ }
+ }
+ // However, it is still up to the app to finish any events that have already been delivered
+ // to the callbacks. If we wanted to change that behaviour and auto-finish all unfinished events
+ // that were already sent to callbacks, we could potentially loop through "mConsumeTimes"
+ // instead. We can't use "mBatchedSequenceNumbers" for this purpose, because it only contains
+ // batchable (i.e., ACTION_MOVE) events that were sent to the callbacks.
+ const size_t unfinishedEvents = mConsumeTimes.size();
+ LOG_IF(INFO, unfinishedEvents != 0)
+ << getName() << " has " << unfinishedEvents << " unfinished event(s)";
}
int InputConsumerNoResampling::handleReceiveCallback(int events) {
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index c903031..4a6f66e 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -191,7 +191,9 @@
mKeyboardLayoutInfo(other.mKeyboardLayoutInfo),
mSources(other.mSources),
mKeyboardType(other.mKeyboardType),
- mKeyCharacterMap(other.mKeyCharacterMap),
+ mKeyCharacterMap(other.mKeyCharacterMap
+ ? std::make_unique<KeyCharacterMap>(*other.mKeyCharacterMap)
+ : nullptr),
mUsiVersion(other.mUsiVersion),
mAssociatedDisplayId(other.mAssociatedDisplayId),
mEnabled(other.mEnabled),
@@ -204,6 +206,34 @@
mLights(other.mLights),
mViewBehavior(other.mViewBehavior) {}
+InputDeviceInfo& InputDeviceInfo::operator=(const InputDeviceInfo& other) {
+ mId = other.mId;
+ mGeneration = other.mGeneration;
+ mControllerNumber = other.mControllerNumber;
+ mIdentifier = other.mIdentifier;
+ mAlias = other.mAlias;
+ mIsExternal = other.mIsExternal;
+ mHasMic = other.mHasMic;
+ mKeyboardLayoutInfo = other.mKeyboardLayoutInfo;
+ mSources = other.mSources;
+ mKeyboardType = other.mKeyboardType;
+ mKeyCharacterMap = other.mKeyCharacterMap
+ ? std::make_unique<KeyCharacterMap>(*other.mKeyCharacterMap)
+ : nullptr;
+ mUsiVersion = other.mUsiVersion;
+ mAssociatedDisplayId = other.mAssociatedDisplayId;
+ mEnabled = other.mEnabled;
+ mHasVibrator = other.mHasVibrator;
+ mHasBattery = other.mHasBattery;
+ mHasButtonUnderPad = other.mHasButtonUnderPad;
+ mHasSensor = other.mHasSensor;
+ mMotionRanges = other.mMotionRanges;
+ mSensors = other.mSensors;
+ mLights = other.mLights;
+ mViewBehavior = other.mViewBehavior;
+ return *this;
+}
+
InputDeviceInfo::~InputDeviceInfo() {
}
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index b0563ab..90d29dd 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -84,15 +84,15 @@
KeyCharacterMap::KeyCharacterMap(const std::string& filename) : mLoadFileName(filename) {}
-base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
+base::Result<std::unique_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
Format format) {
Tokenizer* tokenizer;
status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
if (status) {
return Errorf("Error {} opening key character map file {}.", status, filename.c_str());
}
- std::shared_ptr<KeyCharacterMap> map =
- std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(filename));
+ std::unique_ptr<KeyCharacterMap> map =
+ std::unique_ptr<KeyCharacterMap>(new KeyCharacterMap(filename));
if (!map.get()) {
ALOGE("Error allocating key character map.");
return Errorf("Error allocating key character map.");
@@ -365,6 +365,17 @@
return toKeyCode;
}
+std::vector<int32_t> KeyCharacterMap::findKeyCodesMappedToKeyCode(int32_t toKeyCode) const {
+ std::vector<int32_t> fromKeyCodes;
+
+ for (const auto& [from, to] : mKeyRemapping) {
+ if (toKeyCode == to) {
+ fromKeyCodes.push_back(from);
+ }
+ }
+ return fromKeyCodes;
+}
+
std::pair<int32_t, int32_t> KeyCharacterMap::applyKeyBehavior(int32_t fromKeyCode,
int32_t fromMetaState) const {
int32_t toKeyCode = fromKeyCode;
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 328fa68..e2cc6fb 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -18,6 +18,7 @@
#include <algorithm>
#include <chrono>
+#include <ostream>
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -26,10 +27,7 @@
#include <input/Resampler.h>
#include <utils/Timers.h>
-using std::chrono::nanoseconds;
-
namespace android {
-
namespace {
const bool IS_DEBUGGABLE_BUILD =
@@ -49,6 +47,8 @@
return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
}
+using std::chrono::nanoseconds;
+
constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5};
constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2};
@@ -75,6 +75,31 @@
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha));
return resampledCoords;
}
+
+bool equalXY(const PointerCoords& a, const PointerCoords& b) {
+ return (a.getX() == b.getX()) && (a.getY() == b.getY());
+}
+
+void setMotionEventPointerCoords(MotionEvent& motionEvent, size_t sampleIndex, size_t pointerIndex,
+ const PointerCoords& pointerCoords) {
+ // Ideally, we should not cast away const. In this particular case, it's safe to cast away const
+ // and dereference getHistoricalRawPointerCoords returned pointer because motionEvent is a
+ // nonconst reference to a MotionEvent object, so mutating the object should not be undefined
+ // behavior; moreover, the invoked method guarantees to return a valid pointer. Otherwise, it
+ // fatally logs. Alternatively, we could've created a new MotionEvent from scratch, but this
+ // approach is simpler and more efficient.
+ PointerCoords& motionEventCoords = const_cast<PointerCoords&>(
+ *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex)));
+ motionEventCoords.setAxisValue(AMOTION_EVENT_AXIS_X, pointerCoords.getX());
+ motionEventCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointerCoords.getY());
+ motionEventCoords.isResampled = pointerCoords.isResampled;
+}
+
+std::ostream& operator<<(std::ostream& os, const PointerCoords& pointerCoords) {
+ os << "(" << pointerCoords.getX() << ", " << pointerCoords.getY() << ")";
+ return os;
+}
+
} // namespace
void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
@@ -85,12 +110,9 @@
std::vector<Pointer> pointers;
const size_t numPointers = motionEvent.getPointerCount();
for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
- // getSamplePointerCoords is the vector representation of a getHistorySize by
- // getPointerCount matrix.
- const PointerCoords& pointerCoords =
- motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex];
- pointers.push_back(
- Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords});
+ pointers.push_back(Pointer{*(motionEvent.getPointerProperties(pointerIndex)),
+ *(motionEvent.getHistoricalRawPointerCoords(pointerIndex,
+ sampleIndex))});
}
mLatestSamples.pushBack(
Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers});
@@ -245,10 +267,56 @@
return RESAMPLE_LATENCY;
}
+void LegacyResampler::overwriteMotionEventSamples(MotionEvent& motionEvent) const {
+ const size_t numSamples = motionEvent.getHistorySize() + 1;
+ for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) {
+ overwriteStillPointers(motionEvent, sampleIndex);
+ overwriteOldPointers(motionEvent, sampleIndex);
+ }
+}
+
+void LegacyResampler::overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
+ for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); ++pointerIndex) {
+ const PointerCoords& pointerCoords =
+ *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex));
+ if (equalXY(mLastRealSample->pointers[pointerIndex].coords, pointerCoords)) {
+ LOG_IF(INFO, debugResampling())
+ << "Pointer ID: " << motionEvent.getPointerId(pointerIndex)
+ << " did not move. Overwriting its coordinates from " << pointerCoords << " to "
+ << mLastRealSample->pointers[pointerIndex].coords;
+ setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
+ mPreviousPrediction->pointers[pointerIndex].coords);
+ }
+ }
+}
+
+void LegacyResampler::overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
+ if (!mPreviousPrediction.has_value()) {
+ return;
+ }
+ if (nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)} <
+ mPreviousPrediction->eventTime) {
+ LOG_IF(INFO, debugResampling())
+ << "Motion event sample older than predicted sample. Overwriting event time from "
+ << motionEvent.getHistoricalEventTime(sampleIndex) << "ns to "
+ << mPreviousPrediction->eventTime.count() << "ns.";
+ for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
+ ++pointerIndex) {
+ setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
+ mPreviousPrediction->pointers[pointerIndex].coords);
+ }
+ }
+}
+
void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent,
const InputMessage* futureSample) {
const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY;
+ if (resampleTime.count() == motionEvent.getEventTime()) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Resample time equals motion event time.";
+ return;
+ }
+
updateLatestSamples(motionEvent);
const std::optional<Sample> sample = (futureSample != nullptr)
@@ -256,6 +324,16 @@
: (attemptExtrapolation(resampleTime));
if (sample.has_value()) {
addSampleToMotionEvent(*sample, motionEvent);
+ if (mPreviousPrediction.has_value()) {
+ overwriteMotionEventSamples(motionEvent);
+ }
+ // mPreviousPrediction is only updated whenever extrapolation occurs because extrapolation
+ // is about predicting upcoming scenarios.
+ if (futureSample == nullptr) {
+ mPreviousPrediction = sample;
+ }
}
+ mLastRealSample = *(mLatestSamples.end() - 1);
}
+
} // namespace android
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 81c6175..661c9f7 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -17,6 +17,7 @@
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
"InputConsumer_test.cpp",
+ "InputConsumerResampling_test.cpp",
"InputDevice_test.cpp",
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
diff --git a/libs/input/tests/InputConsumerResampling_test.cpp b/libs/input/tests/InputConsumerResampling_test.cpp
new file mode 100644
index 0000000..883ca82
--- /dev/null
+++ b/libs/input/tests/InputConsumerResampling_test.cpp
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2024 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 <input/InputConsumerNoResampling.h>
+
+#include <chrono>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <TestEventMatchers.h>
+#include <TestInputChannel.h>
+#include <attestation/HmacKeyManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/BlockingQueue.h>
+#include <input/InputEventBuilders.h>
+#include <input/Resampler.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace {
+
+using std::chrono::nanoseconds;
+using namespace std::chrono_literals;
+
+struct Pointer {
+ int32_t id{0};
+ float x{0.0f};
+ float y{0.0f};
+ ToolType toolType{ToolType::FINGER};
+ bool isResampled{false};
+
+ PointerBuilder asPointerBuilder() const {
+ return PointerBuilder{id, toolType}.x(x).y(y).isResampled(isResampled);
+ }
+};
+
+struct InputEventEntry {
+ std::chrono::nanoseconds eventTime{0};
+ std::vector<Pointer> pointers{};
+ int32_t action{-1};
+};
+
+} // namespace
+
+class InputConsumerResamplingTest : public ::testing::Test, public InputConsumerCallbacks {
+protected:
+ InputConsumerResamplingTest()
+ : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")},
+ mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} {
+ Looper::setForThread(mLooper);
+ mConsumer = std::make_unique<
+ InputConsumerNoResampling>(mClientTestChannel, mLooper, *this,
+ []() { return std::make_unique<LegacyResampler>(); });
+ }
+
+ void invokeLooperCallback() const {
+ sp<LooperCallback> callback;
+ ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
+ /*events=*/nullptr, &callback, /*data=*/nullptr));
+ ASSERT_NE(callback, nullptr);
+ callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+ }
+
+ InputMessage nextPointerMessage(const InputEventEntry& entry);
+
+ void assertReceivedMotionEvent(const std::vector<InputEventEntry>& expectedEntries);
+
+ std::shared_ptr<TestInputChannel> mClientTestChannel;
+ sp<Looper> mLooper;
+ std::unique_ptr<InputConsumerNoResampling> mConsumer;
+
+ BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents;
+ BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents;
+ BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents;
+ BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+ BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents;
+ BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+
+private:
+ uint32_t mLastSeq{0};
+ size_t mOnBatchedInputEventPendingInvocationCount{0};
+
+ // InputConsumerCallbacks interface
+ void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
+ mKeyEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
+ mMotionEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onBatchedInputEventPending(int32_t pendingBatchSource) override {
+ if (!mConsumer->probablyHasInput()) {
+ ADD_FAILURE() << "should deterministically have input because there is a batch";
+ }
+ ++mOnBatchedInputEventPendingInvocationCount;
+ }
+ void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
+ mFocusEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
+ mCaptureEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
+ mDragEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
+ mTouchModeEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+};
+
+InputMessage InputConsumerResamplingTest::nextPointerMessage(const InputEventEntry& entry) {
+ ++mLastSeq;
+ InputMessageBuilder messageBuilder = InputMessageBuilder{InputMessage::Type::MOTION, mLastSeq}
+ .eventTime(entry.eventTime.count())
+ .deviceId(1)
+ .action(entry.action)
+ .downTime(0);
+ for (const Pointer& pointer : entry.pointers) {
+ messageBuilder.pointer(pointer.asPointerBuilder());
+ }
+ return messageBuilder.build();
+}
+
+void InputConsumerResamplingTest::assertReceivedMotionEvent(
+ const std::vector<InputEventEntry>& expectedEntries) {
+ std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop();
+ ASSERT_NE(motionEvent, nullptr);
+
+ ASSERT_EQ(motionEvent->getHistorySize() + 1, expectedEntries.size());
+
+ for (size_t sampleIndex = 0; sampleIndex < expectedEntries.size(); ++sampleIndex) {
+ SCOPED_TRACE("sampleIndex: " + std::to_string(sampleIndex));
+ const InputEventEntry& expectedEntry = expectedEntries[sampleIndex];
+ EXPECT_EQ(motionEvent->getHistoricalEventTime(sampleIndex),
+ expectedEntry.eventTime.count());
+ EXPECT_EQ(motionEvent->getPointerCount(), expectedEntry.pointers.size());
+ EXPECT_EQ(motionEvent->getAction(), expectedEntry.action);
+
+ for (size_t pointerIndex = 0; pointerIndex < expectedEntry.pointers.size();
+ ++pointerIndex) {
+ SCOPED_TRACE("pointerIndex: " + std::to_string(pointerIndex));
+ ssize_t eventPointerIndex =
+ motionEvent->findPointerIndex(expectedEntry.pointers[pointerIndex].id);
+ EXPECT_EQ(motionEvent->getHistoricalRawX(eventPointerIndex, sampleIndex),
+ expectedEntry.pointers[pointerIndex].x);
+ EXPECT_EQ(motionEvent->getHistoricalRawY(eventPointerIndex, sampleIndex),
+ expectedEntry.pointers[pointerIndex].y);
+ EXPECT_EQ(motionEvent->getHistoricalX(eventPointerIndex, sampleIndex),
+ expectedEntry.pointers[pointerIndex].x);
+ EXPECT_EQ(motionEvent->getHistoricalY(eventPointerIndex, sampleIndex),
+ expectedEntry.pointers[pointerIndex].y);
+ EXPECT_EQ(motionEvent->isResampled(pointerIndex, sampleIndex),
+ expectedEntry.pointers[pointerIndex].isResampled);
+ }
+ }
+}
+
+/**
+ * Timeline
+ * ---------+------------------+------------------+--------+-----------------+----------------------
+ * 0 ms 10 ms 20 ms 25 ms 35 ms
+ * ACTION_DOWN ACTION_MOVE ACTION_MOVE ^ ^
+ * | |
+ * resampled value |
+ * frameTime
+ * Typically, the prediction is made for time frameTime - RESAMPLE_LATENCY, or 30 ms in this case,
+ * where RESAMPLE_LATENCY equals 5 milliseconds. However, that would be 10 ms later than the last
+ * real sample (which came in at 20 ms). Therefore, the resampling should happen at 20 ms +
+ * RESAMPLE_MAX_PREDICTION = 28 ms, where RESAMPLE_MAX_PREDICTION equals 8 milliseconds. In this
+ * situation, though, resample time is further limited by taking half of the difference between the
+ * last two real events, which would put this time at: 20 ms + (20 ms - 10 ms) / 2 = 25 ms.
+ */
+TEST_F(InputConsumerResamplingTest, EventIsResampled) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Same as above test, but use pointer id=1 instead of 0 to make sure that system does not
+ * have these hardcoded.
+ */
+TEST_F(InputConsumerResamplingTest, EventIsResampledWithDifferentId) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 1, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 1, .x = 10.0f, .y = 20.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 1, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms, {Pointer{.id = 1, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{10ms,
+ {Pointer{.id = 1, .x = 20.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 1, .x = 30.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{25ms,
+ {Pointer{.id = 1, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Stylus pointer coordinates are resampled.
+ */
+TEST_F(InputConsumerResamplingTest, StylusEventIsResampled) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0,
+ .x = 10.0f,
+ .y = 20.0f,
+ .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent({InputEventEntry{10ms,
+ {Pointer{.id = 0,
+ .x = 20.0f,
+ .y = 30.0f,
+ .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0,
+ .x = 30.0f,
+ .y = 30.0f,
+ .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0,
+ .x = 35.0f,
+ .y = 30.0f,
+ .toolType = ToolType::STYLUS,
+ .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Mouse pointer coordinates are resampled.
+ */
+TEST_F(InputConsumerResamplingTest, MouseEventIsResampled) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0,
+ .x = 10.0f,
+ .y = 20.0f,
+ .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent({InputEventEntry{10ms,
+ {Pointer{.id = 0,
+ .x = 20.0f,
+ .y = 30.0f,
+ .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0,
+ .x = 30.0f,
+ .y = 30.0f,
+ .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0,
+ .x = 35.0f,
+ .y = 30.0f,
+ .toolType = ToolType::MOUSE,
+ .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Motion events with palm tool type are not resampled.
+ */
+TEST_F(InputConsumerResamplingTest, PalmEventIsNotResampled) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent(
+ {InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Event should not be resampled when sample time is equal to event time.
+ */
+TEST_F(InputConsumerResamplingTest, SampleTimeEqualsEventTime) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+
+ // MotionEvent should not resampled because the resample time falls exactly on the existing
+ // event time.
+ assertReceivedMotionEvent({InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Once we send a resampled value to the app, we should continue to send the last predicted value if
+ * a pointer does not move. Only real values are used to determine if a pointer does not move.
+ */
+TEST_F(InputConsumerResamplingTest, ResampledValueIsUsedForIdenticalCoordinates) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // Coordinate value 30 has been resampled to 35. When a new event comes in with value 30 again,
+ // the system should still report 35.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {40ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{40ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+ InputEventEntry{45ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+}
+
+TEST_F(InputConsumerResamplingTest, OldEventReceivedAfterResampleOccurs) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // Above, the resampled event is at 25ms rather than at 30 ms = 35ms - RESAMPLE_LATENCY
+ // because we are further bound by how far we can extrapolate by the "last time delta".
+ // That's 50% of (20 ms - 10ms) => 5ms. So we can't predict more than 5 ms into the future
+ // from the event at 20ms, which is why the resampled event is at t = 25 ms.
+
+ // We resampled the event to 25 ms. Now, an older 'real' event comes in.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {24ms, {Pointer{.id = 0, .x = 40.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{50ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{24ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+ InputEventEntry{26ms,
+ {Pointer{.id = 0, .x = 45.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+}
+
+} // namespace android
diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
index cbb332e..6a3bbe5 100644
--- a/libs/input/tests/InputConsumer_test.cpp
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -43,6 +43,9 @@
using ::testing::AllOf;
using ::testing::Matcher;
+constexpr auto ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
+constexpr auto ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
+
struct Pointer {
int32_t id{0};
ToolType toolType{ToolType::FINGER};
@@ -80,10 +83,19 @@
--mOnBatchedInputEventPendingInvocationCount;
}
- void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
- std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop();
- ASSERT_NE(motionEvent, nullptr);
+ std::unique_ptr<MotionEvent> assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
+ if (mMotionEvents.empty()) {
+ ADD_FAILURE() << "No motion events received";
+ return nullptr;
+ }
+ std::unique_ptr<MotionEvent> motionEvent = std::move(mMotionEvents.front());
+ mMotionEvents.pop();
+ if (motionEvent == nullptr) {
+ ADD_FAILURE() << "The consumed motion event should never be null";
+ return nullptr;
+ }
EXPECT_THAT(*motionEvent, matcher);
+ return motionEvent;
}
InputMessage nextPointerMessage(std::chrono::nanoseconds eventTime, DeviceId deviceId,
@@ -93,12 +105,15 @@
sp<Looper> mLooper;
std::unique_ptr<InputConsumerNoResampling> mConsumer;
- BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents;
- BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents;
- BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents;
- BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
- BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents;
- BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+ std::queue<std::unique_ptr<KeyEvent>> mKeyEvents;
+ std::queue<std::unique_ptr<MotionEvent>> mMotionEvents;
+ std::queue<std::unique_ptr<FocusEvent>> mFocusEvents;
+ std::queue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+ std::queue<std::unique_ptr<DragEvent>> mDragEvents;
+ std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+
+ // Whether or not to automatically call "finish" whenever a motion event is received.
+ bool mShouldFinishMotions{true};
private:
uint32_t mLastSeq{0};
@@ -107,11 +122,13 @@
// InputConsumerCallbacks interface
void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
mKeyEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
}
void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
mMotionEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ if (mShouldFinishMotions) {
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
}
void onBatchedInputEventPending(int32_t pendingBatchSource) override {
if (!mConsumer->probablyHasInput()) {
@@ -121,19 +138,19 @@
};
void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
mFocusEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
};
void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
mCaptureEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
};
void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
mDragEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
}
void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
mTouchModeEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
};
};
@@ -153,15 +170,15 @@
TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) {
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
.eventTime(nanoseconds{0ms}.count())
- .action(AMOTION_EVENT_ACTION_DOWN)
+ .action(ACTION_DOWN)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
.eventTime(nanoseconds{5ms}.count())
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
.eventTime(nanoseconds{10ms}.count())
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->assertNoSentMessages();
@@ -172,10 +189,10 @@
mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt);
- std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
- ASSERT_NE(downMotionEvent, nullptr);
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
- std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop();
+ std::unique_ptr<MotionEvent> moveMotionEvent =
+ assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
ASSERT_NE(moveMotionEvent, nullptr);
EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL);
@@ -187,19 +204,19 @@
TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) {
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
.eventTime(nanoseconds{0ms}.count())
- .action(AMOTION_EVENT_ACTION_DOWN)
+ .action(ACTION_DOWN)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
.eventTime(nanoseconds{5ms}.count())
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
.eventTime(nanoseconds{10ms}.count())
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
.eventTime(nanoseconds{15ms}.count())
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->assertNoSentMessages();
@@ -210,56 +227,98 @@
mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/);
- std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
- ASSERT_NE(downMotionEvent, nullptr);
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
- std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop();
+ std::unique_ptr<MotionEvent> moveMotionEvent =
+ assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
ASSERT_NE(moveMotionEvent, nullptr);
const size_t numSamples = moveMotionEvent->getHistorySize() + 1;
EXPECT_LT(moveMotionEvent->getHistoricalEventTime(numSamples - 2),
moveMotionEvent->getEventTime());
- // Consume all remaining events before ending the test. Otherwise, the smart pointer that owns
- // consumer is set to null before destroying consumer. This leads to a member function call on a
- // null object.
- // TODO(b/332613662): Remove this workaround.
- mConsumer->consumeBatchedInputEvents(std::nullopt);
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ // The event with seq=3 remains unconsumed, and therefore finish will not be called for it until
+ // after the consumer is destroyed.
+ mConsumer.reset();
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/false);
+ mClientTestChannel->assertNoSentMessages();
+}
- mClientTestChannel->assertFinishMessage(/*seq=*/0, true);
- mClientTestChannel->assertFinishMessage(/*seq=*/1, true);
- mClientTestChannel->assertFinishMessage(/*seq=*/2, true);
- mClientTestChannel->assertFinishMessage(/*seq=*/3, true);
+/**
+ * During normal operation, the user of InputConsumer (callbacks) is expected to call "finish"
+ * for each input event received in InputConsumerCallbacks.
+ * If the InputConsumer is destroyed, the events that were already sent to the callbacks will not
+ * be finished automatically.
+ */
+TEST_F(InputConsumerTest, UnhandledEventsNotFinishedInDestructor) {
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
+ mShouldFinishMotions = false;
+ invokeLooperCallback();
+ assertOnBatchedInputEventPendingWasCalled();
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+ mClientTestChannel->assertNoSentMessages();
+ // The "finishInputEvent" was not called by the InputConsumerCallbacks.
+ // Now, destroy the consumer and check that the "finish" was not called automatically for the
+ // DOWN event, but was called for the undelivered MOVE event.
+ mConsumer.reset();
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
+ mClientTestChannel->assertNoSentMessages();
+}
+
+/**
+ * Send an event to the InputConsumer, but do not invoke "consumeBatchedInputEvents", thus leaving
+ * the input event unconsumed by the callbacks. Ensure that no crash occurs when the consumer is
+ * destroyed.
+ * This test is similar to the one above, but here we are calling "finish"
+ * automatically for any event received in the callbacks.
+ */
+TEST_F(InputConsumerTest, UnconsumedEventDoesNotCauseACrash) {
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
+ invokeLooperCallback();
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
+ invokeLooperCallback();
+ mConsumer.reset();
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
}
TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) {
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
.deviceId(0)
- .action(AMOTION_EVENT_ACTION_DOWN)
+ .action(ACTION_DOWN)
.build());
invokeLooperCallback();
- assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+ assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(ACTION_DOWN)));
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
.deviceId(0)
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
.deviceId(0)
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
.deviceId(0)
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/4}
.deviceId(1)
- .action(AMOTION_EVENT_ACTION_DOWN)
+ .action(ACTION_DOWN)
.build());
invokeLooperCallback();
- assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+ assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(ACTION_DOWN)));
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/5}
.deviceId(0)
@@ -267,7 +326,7 @@
.build());
invokeLooperCallback();
- assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+ assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(ACTION_MOVE)));
mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
@@ -303,60 +362,52 @@
* The first field is device ID, and the second field is event time.
*/
TEST_F(InputConsumerTest, MultiDeviceResampling) {
- mClientTestChannel->enqueueMessage(nextPointerMessage(0ms, /*deviceId=*/0,
- AMOTION_EVENT_ACTION_DOWN,
- Pointer{.x = 0, .y = 0}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(0ms, /*deviceId=*/0, ACTION_DOWN, Pointer{.x = 0, .y = 0}));
mClientTestChannel->assertNoSentMessages();
invokeLooperCallback();
- assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithSampleCount(1)));
+ assertReceivedMotionEvent(
+ AllOf(WithDeviceId(0), WithMotionAction(ACTION_DOWN), WithSampleCount(1)));
- mClientTestChannel->enqueueMessage(nextPointerMessage(5ms, /*deviceId=*/0,
- AMOTION_EVENT_ACTION_MOVE,
- Pointer{.x = 1.0f, .y = 2.0f}));
- mClientTestChannel->enqueueMessage(nextPointerMessage(10ms, /*deviceId=*/0,
- AMOTION_EVENT_ACTION_MOVE,
- Pointer{.x = 2.0f, .y = 4.0f}));
- mClientTestChannel->enqueueMessage(nextPointerMessage(15ms, /*deviceId=*/1,
- AMOTION_EVENT_ACTION_DOWN,
- Pointer{.x = 10.0f, .y = 10.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(5ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 1.0f, .y = 2.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(10ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 2.0f, .y = 4.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(15ms, /*deviceId=*/1, ACTION_DOWN, Pointer{.x = 10.0f, .y = 10.0f}));
invokeLooperCallback();
mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/);
- assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithSampleCount(1)));
assertReceivedMotionEvent(
- AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3),
+ AllOf(WithDeviceId(1), WithMotionAction(ACTION_DOWN), WithSampleCount(1)));
+ assertReceivedMotionEvent(
+ AllOf(WithDeviceId(0), WithMotionAction(ACTION_MOVE), WithSampleCount(3),
WithSample(/*sampleIndex=*/2,
Sample{11ms,
{PointerArgs{.x = 2.2f, .y = 4.4f, .isResampled = true}}})));
- mClientTestChannel->enqueueMessage(nextPointerMessage(20ms, /*deviceId=*/1,
- AMOTION_EVENT_ACTION_MOVE,
- Pointer{.x = 11.0f, .y = 12.0f}));
- mClientTestChannel->enqueueMessage(nextPointerMessage(25ms, /*deviceId=*/1,
- AMOTION_EVENT_ACTION_MOVE,
- Pointer{.x = 12.0f, .y = 14.0f}));
- mClientTestChannel->enqueueMessage(nextPointerMessage(30ms, /*deviceId=*/0,
- AMOTION_EVENT_ACTION_MOVE,
- Pointer{.x = 5.0f, .y = 6.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(20ms, /*deviceId=*/1, ACTION_MOVE, Pointer{.x = 11.0f, .y = 12.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(25ms, /*deviceId=*/1, ACTION_MOVE, Pointer{.x = 12.0f, .y = 14.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(30ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 5.0f, .y = 6.0f}));
invokeLooperCallback();
assertOnBatchedInputEventPendingWasCalled();
mConsumer->consumeBatchedInputEvents(32'000'000 /*ns*/);
assertReceivedMotionEvent(
- AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3),
+ AllOf(WithDeviceId(1), WithMotionAction(ACTION_MOVE), WithSampleCount(3),
WithSample(/*sampleIndex=*/2,
Sample{27ms,
{PointerArgs{.x = 12.4f, .y = 14.8f, .isResampled = true}}})));
- mClientTestChannel->enqueueMessage(nextPointerMessage(35ms, /*deviceId=*/0,
- AMOTION_EVENT_ACTION_MOVE,
- Pointer{.x = 8.0f, .y = 9.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(35ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 8.0f, .y = 9.0f}));
mClientTestChannel->enqueueMessage(nextPointerMessage(40ms, /*deviceId=*/1,
AMOTION_EVENT_ACTION_UP,
Pointer{.x = 12.0f, .y = 14.0f}));
@@ -371,7 +422,7 @@
AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSampleCount(1)));
assertReceivedMotionEvent(
- AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3),
+ AllOf(WithDeviceId(0), WithMotionAction(ACTION_MOVE), WithSampleCount(3),
WithSample(/*sampleIndex=*/2,
Sample{37'500'000ns,
{PointerArgs{.x = 9.5f, .y = 10.5f, .isResampled = true}}})));
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
index d68d6ba..c572ee7 100644
--- a/libs/nativewindow/rust/Android.bp
+++ b/libs/nativewindow/rust/Android.bp
@@ -26,6 +26,7 @@
source_stem: "bindings",
bindgen_flags: [
"--constified-enum-module=AHardwareBuffer_Format",
+ "--bitfield-enum=ADataSpace",
"--bitfield-enum=AHardwareBuffer_UsageFlags",
"--allowlist-file=.*/nativewindow/include/.*\\.h",
@@ -110,6 +111,7 @@
srcs: ["src/lib.rs"],
rustlibs: [
"libbinder_rs",
+ "libbitflags",
"libnativewindow_bindgen",
],
}
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 931c311..f19b908 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -19,10 +19,9 @@
mod handle;
mod surface;
-pub use handle::NativeHandle;
-pub use surface::Surface;
-
pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+pub use handle::NativeHandle;
+pub use surface::{buffer::Buffer, Surface};
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
diff --git a/libs/nativewindow/rust/src/surface.rs b/libs/nativewindow/rust/src/surface.rs
index 25fea80..ed52471 100644
--- a/libs/nativewindow/rust/src/surface.rs
+++ b/libs/nativewindow/rust/src/surface.rs
@@ -14,20 +14,27 @@
//! Rust wrapper for `ANativeWindow` and related types.
+pub(crate) mod buffer;
+
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
unstable_api::{status_result, AsNative},
StatusCode,
};
+use bitflags::bitflags;
+use buffer::Buffer;
use nativewindow_bindgen::{
- AHardwareBuffer_Format, ANativeWindow, ANativeWindow_acquire, ANativeWindow_getFormat,
- ANativeWindow_getHeight, ANativeWindow_getWidth, ANativeWindow_readFromParcel,
- ANativeWindow_release, ANativeWindow_writeToParcel,
+ ADataSpace, AHardwareBuffer_Format, ANativeWindow, ANativeWindow_acquire,
+ ANativeWindow_getBuffersDataSpace, ANativeWindow_getBuffersDefaultDataSpace,
+ ANativeWindow_getFormat, ANativeWindow_getHeight, ANativeWindow_getWidth, ANativeWindow_lock,
+ ANativeWindow_readFromParcel, ANativeWindow_release, ANativeWindow_setBuffersDataSpace,
+ ANativeWindow_setBuffersGeometry, ANativeWindow_setBuffersTransform,
+ ANativeWindow_unlockAndPost, ANativeWindow_writeToParcel, ARect,
};
use std::error::Error;
use std::fmt::{self, Debug, Display, Formatter};
-use std::ptr::{null_mut, NonNull};
+use std::ptr::{self, null_mut, NonNull};
/// Wrapper around an opaque C `ANativeWindow`.
#[derive(PartialEq, Eq)]
@@ -60,6 +67,132 @@
let format = unsafe { ANativeWindow_getFormat(self.0.as_ptr()) };
format.try_into().map_err(|_| ErrorCode(format))
}
+
+ /// Changes the format and size of the window buffers.
+ ///
+ /// The width and height control the number of pixels in the buffers, not the dimensions of the
+ /// window on screen. If these are different than the window's physical size, then its buffer
+ /// will be scaled to match that size when compositing it to the screen. The width and height
+ /// must be either both zero or both non-zero. If both are 0 then the window's base value will
+ /// come back in force.
+ pub fn set_buffers_geometry(
+ &mut self,
+ width: i32,
+ height: i32,
+ format: AHardwareBuffer_Format::Type,
+ ) -> Result<(), ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let status = unsafe {
+ ANativeWindow_setBuffersGeometry(
+ self.0.as_ptr(),
+ width,
+ height,
+ format.try_into().expect("Invalid format"),
+ )
+ };
+
+ if status == 0 {
+ Ok(())
+ } else {
+ Err(ErrorCode(status))
+ }
+ }
+
+ /// Sets a transfom that will be applied to future buffers posted to the window.
+ pub fn set_buffers_transform(&mut self, transform: Transform) -> Result<(), ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let status =
+ unsafe { ANativeWindow_setBuffersTransform(self.0.as_ptr(), transform.bits() as i32) };
+
+ if status == 0 {
+ Ok(())
+ } else {
+ Err(ErrorCode(status))
+ }
+ }
+
+ /// Sets the data space that will be applied to future buffers posted to the window.
+ pub fn set_buffers_data_space(&mut self, data_space: ADataSpace) -> Result<(), ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let status = unsafe { ANativeWindow_setBuffersDataSpace(self.0.as_ptr(), data_space.0) };
+
+ if status == 0 {
+ Ok(())
+ } else {
+ Err(ErrorCode(status))
+ }
+ }
+
+ /// Gets the data space of the buffers in the window.
+ pub fn get_buffers_data_space(&mut self) -> Result<ADataSpace, ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let data_space = unsafe { ANativeWindow_getBuffersDataSpace(self.0.as_ptr()) };
+
+ if data_space < 0 {
+ Err(ErrorCode(data_space))
+ } else {
+ Ok(ADataSpace(data_space))
+ }
+ }
+
+ /// Gets the default data space of the buffers in the window as set by the consumer.
+ pub fn get_buffers_default_data_space(&mut self) -> Result<ADataSpace, ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let data_space = unsafe { ANativeWindow_getBuffersDefaultDataSpace(self.0.as_ptr()) };
+
+ if data_space < 0 {
+ Err(ErrorCode(data_space))
+ } else {
+ Ok(ADataSpace(data_space))
+ }
+ }
+
+ /// Locks the window's next drawing surface for writing, and returns it.
+ pub fn lock(&mut self, bounds: Option<&mut ARect>) -> Result<Buffer, ErrorCode> {
+ let mut buffer = buffer::EMPTY;
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it. The other pointers must be valid because the come from
+ // references, and aren't retained after the function returns.
+ let status = unsafe {
+ ANativeWindow_lock(
+ self.0.as_ptr(),
+ &mut buffer,
+ bounds.map(ptr::from_mut).unwrap_or(null_mut()),
+ )
+ };
+ if status != 0 {
+ return Err(ErrorCode(status));
+ }
+
+ Ok(Buffer::new(buffer, self))
+ }
+
+ /// Unlocks the window's drawing surface which was previously locked, posting the new buffer to
+ /// the display.
+ ///
+ /// This shouldn't be called directly but via the [`Buffer`], hence is not public here.
+ fn unlock_and_post(&mut self) -> Result<(), ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let status = unsafe { ANativeWindow_unlockAndPost(self.0.as_ptr()) };
+ if status == 0 {
+ Ok(())
+ } else {
+ Err(ErrorCode(status))
+ }
+ }
}
impl Drop for Surface {
@@ -141,3 +274,19 @@
write!(f, "Error {}", self.0)
}
}
+
+bitflags! {
+ /// Transforms that can be applied to buffers as they are displayed to a window.
+ #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+ pub struct Transform: u32 {
+ const MIRROR_HORIZONTAL = 0x01;
+ const MIRROR_VERTICAL = 0x02;
+ const ROTATE_90 = 0x04;
+ }
+}
+
+impl Transform {
+ pub const IDENTITY: Self = Self::empty();
+ pub const ROTATE_180: Self = Self::MIRROR_HORIZONTAL.union(Self::MIRROR_VERTICAL);
+ pub const ROTATE_270: Self = Self::ROTATE_180.union(Self::ROTATE_90);
+}
diff --git a/libs/nativewindow/rust/src/surface/buffer.rs b/libs/nativewindow/rust/src/surface/buffer.rs
new file mode 100644
index 0000000..a2d74d4
--- /dev/null
+++ b/libs/nativewindow/rust/src/surface/buffer.rs
@@ -0,0 +1,68 @@
+// Copyright (C) 2024 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.
+
+use super::{ErrorCode, Surface};
+use nativewindow_bindgen::{AHardwareBuffer_Format, ANativeWindow_Buffer};
+use std::ptr::null_mut;
+
+/// An empty `ANativeWindow_Buffer`.
+pub const EMPTY: ANativeWindow_Buffer = ANativeWindow_Buffer {
+ width: 0,
+ height: 0,
+ stride: 0,
+ format: 0,
+ bits: null_mut(),
+ reserved: [0; 6],
+};
+
+/// Rust wrapper for `ANativeWindow_Buffer`, representing a locked buffer from a [`Surface`].
+pub struct Buffer<'a> {
+ /// The wrapped `ANativeWindow_Buffer`.
+ pub buffer: ANativeWindow_Buffer,
+ surface: &'a mut Surface,
+}
+
+impl<'a> Buffer<'a> {
+ pub(crate) fn new(buffer: ANativeWindow_Buffer, surface: &'a mut Surface) -> Self {
+ Self { buffer, surface }
+ }
+
+ /// Unlocks the window's drawing surface which was previously locked to create this buffer,
+ /// posting the buffer to the display.
+ pub fn unlock_and_post(self) -> Result<(), ErrorCode> {
+ self.surface.unlock_and_post()
+ }
+
+ /// The number of pixels that are shown horizontally.
+ pub fn width(&self) -> i32 {
+ self.buffer.width
+ }
+
+ /// The number of pixels that are shown vertically.
+ pub fn height(&self) -> i32 {
+ self.buffer.height
+ }
+
+ /// The number of pixels that a line in the buffer takes in memory.
+ ///
+ /// This may be greater than the width.
+ pub fn stride(&self) -> i32 {
+ self.buffer.stride
+ }
+
+ /// The pixel format of the buffer.
+ pub fn format(&self) -> Result<AHardwareBuffer_Format::Type, ErrorCode> {
+ self.buffer.format.try_into().map_err(|_| ErrorCode(self.buffer.format))
+ }
+}
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 659666d..a687a37 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -69,6 +69,7 @@
static_libs: [
"libsensor_flags_c_lib",
+ "android.permission.flags-aconfig-cc",
],
export_include_dirs: ["include"],
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index a1549ea..eddd568 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -21,6 +21,7 @@
#include <binder/AppOpsManager.h>
#include <binder/IPermissionController.h>
#include <binder/IServiceManager.h>
+#include <android_permission_flags.h>
/*
* The permission to use for activity recognition sensors (like step counter).
@@ -121,7 +122,9 @@
break;
case SENSOR_TYPE_HEART_RATE: {
mStringType = SENSOR_STRING_TYPE_HEART_RATE;
- mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS;
+ mRequiredPermission =
+ android::permission::flags::replace_body_sensor_permission_enabled() ?
+ SENSOR_PERMISSION_READ_HEART_RATE : SENSOR_PERMISSION_BODY_SENSORS;
AppOpsManager appOps;
mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission));
mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 1ebe597..a9f7ced 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -89,14 +89,14 @@
uint64_t total = 0;
result.append("GraphicBufferAllocator buffers:\n");
const size_t count = list.size();
- StringAppendF(&result, "%14s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
+ StringAppendF(&result, "%18s | %12s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
"W (Stride) x H", "Layers", "Format", "Usage", "Requestor");
for (size_t i = 0; i < count; i++) {
const alloc_rec_t& rec(list.valueAt(i));
std::string sizeStr = (rec.size)
? base::StringPrintf("%7.2f KiB", static_cast<double>(rec.size) / 1024.0)
: "unknown";
- StringAppendF(&result, "%14p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
+ StringAppendF(&result, "%18p | %12s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
list.keyAt(i), sizeStr.c_str(), rec.width, rec.stride, rec.height,
rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
total += rec.size;
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 0b77754..25a2b6e 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -53,6 +53,8 @@
ui::DisplayModeId preferredBootDisplayMode;
std::optional<ui::DisplayMode> getActiveDisplayMode() const;
+
+ bool hasArrSupport;
};
} // namespace android::ui
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 4c9c7b7..4366db5 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -51,6 +51,9 @@
float bottom = 0.0f;
constexpr bool isEmpty() const { return !(left < right && top < bottom); }
+
+ // a valid rectangle has a non negative width and height
+ inline bool isValid() const { return (getWidth() >= 0) && (getHeight() >= 0); }
};
inline bool operator==(const FloatRect& a, const FloatRect& b) {
diff --git a/libs/ultrahdr/Android.bp b/libs/ultrahdr/Android.bp
deleted file mode 100644
index eda5ea4..0000000
--- a/libs/ultrahdr/Android.bp
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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.
-
-package {
- // See: http://go/android-license-faq
- default_applicable_licenses: [
- "frameworks_native_license",
- "adobe_hdr_gain_map_license",
- ],
-}
-
-cc_library {
- name: "libultrahdr-deprecated",
- enabled: false,
- host_supported: true,
- vendor_available: true,
- export_include_dirs: ["include"],
- local_include_dirs: ["include"],
-
- srcs: [
- "icc.cpp",
- "jpegr.cpp",
- "gainmapmath.cpp",
- "jpegrutils.cpp",
- "multipictureformat.cpp",
- ],
-
- shared_libs: [
- "libimage_io",
- "libjpeg",
- "libjpegencoder",
- "libjpegdecoder",
- "liblog",
- "libutils",
- ],
-}
-
-cc_library {
- name: "libjpegencoder-deprecated",
- enabled: false,
- host_supported: true,
- vendor_available: true,
-
- shared_libs: [
- "libjpeg",
- "liblog",
- "libutils",
- ],
-
- export_include_dirs: ["include"],
-
- srcs: [
- "jpegencoderhelper.cpp",
- ],
-}
-
-cc_library {
- name: "libjpegdecoder-deprecated",
- enabled: false,
- host_supported: true,
- vendor_available: true,
-
- shared_libs: [
- "libjpeg",
- "liblog",
- "libutils",
- ],
-
- export_include_dirs: ["include"],
-
- srcs: [
- "jpegdecoderhelper.cpp",
- ],
-}
diff --git a/libs/ultrahdr/fuzzer/Android.bp b/libs/ultrahdr/fuzzer/Android.bp
deleted file mode 100644
index 8d9132f..0000000
--- a/libs/ultrahdr/fuzzer/Android.bp
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // 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_defaults {
- name: "ultrahdr_fuzzer_defaults-deprecated",
- enabled: false,
- host_supported: true,
- shared_libs: [
- "libimage_io",
- "libjpeg",
- ],
- static_libs: [
- "libjpegdecoder",
- "libjpegencoder",
- "libultrahdr",
- "libutils",
- "liblog",
- ],
- target: {
- darwin: {
- enabled: false,
- },
- },
- fuzz_config: {
- cc: [
- "android-media-fuzzing-reports@google.com",
- ],
- description: "The fuzzers target the APIs of jpeg hdr",
- service_privilege: "constrained",
- users: "multi_user",
- fuzzed_code_usage: "future_version",
- vector: "local_no_privileges_required",
- },
-}
-
-cc_fuzz {
- name: "ultrahdr_enc_fuzzer-deprecated",
- enabled: false,
- defaults: ["ultrahdr_fuzzer_defaults"],
- srcs: [
- "ultrahdr_enc_fuzzer.cpp",
- ],
-}
-
-cc_fuzz {
- name: "ultrahdr_dec_fuzzer-deprecated",
- enabled: false,
- defaults: ["ultrahdr_fuzzer_defaults"],
- srcs: [
- "ultrahdr_dec_fuzzer.cpp",
- ],
-}
diff --git a/libs/ultrahdr/tests/Android.bp b/libs/ultrahdr/tests/Android.bp
deleted file mode 100644
index 00cc797..0000000
--- a/libs/ultrahdr/tests/Android.bp
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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.
-
-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_test {
- name: "ultrahdr_unit_test-deprecated",
- enabled: false,
- test_suites: ["device-tests"],
- srcs: [
- "gainmapmath_test.cpp",
- "icchelper_test.cpp",
- "jpegr_test.cpp",
- "jpegencoderhelper_test.cpp",
- "jpegdecoderhelper_test.cpp",
- ],
- shared_libs: [
- "libimage_io",
- "libjpeg",
- "liblog",
- ],
- static_libs: [
- "libgmock",
- "libgtest",
- "libjpegdecoder",
- "libjpegencoder",
- "libultrahdr",
- "libutils",
- ],
- data: [
- "./data/*.*",
- ],
-}
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index bf0e38e..7012df2 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -548,6 +548,10 @@
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = ns,
};
+ auto prop = base::GetProperty("debug.angle.libs.suffix", "");
+ if (!prop.empty()) {
+ name = std::string("lib") + kind + "_" + prop + ".so";
+ }
so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
}
@@ -599,6 +603,9 @@
driver_t* hnd = nullptr;
// ANGLE doesn't ship with GLES library, and thus we skip GLES driver.
+ // b/370113081: if there is no libEGL_angle.so in namespace ns, libEGL_angle.so in system
+ // partition will be loaded instead. If there is no libEGL_angle.so in system partition, no
+ // angle libs are loaded, and app that sets to use ANGLE will crash.
void* dso = load_angle("EGL", ns);
if (dso) {
initialize_api(dso, cnx, EGL);
diff --git a/services/gpuservice/vts/Android.bp b/services/gpuservice/vts/Android.bp
index a24822a..6e0a9f7 100644
--- a/services/gpuservice/vts/Android.bp
+++ b/services/gpuservice/vts/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_gpu",
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/services/inputflinger/InputDeviceMetricsSource.cpp b/services/inputflinger/InputDeviceMetricsSource.cpp
index dee4cb8..70262fb 100644
--- a/services/inputflinger/InputDeviceMetricsSource.cpp
+++ b/services/inputflinger/InputDeviceMetricsSource.cpp
@@ -50,6 +50,18 @@
return InputDeviceUsageSource::BUTTONS;
}
+std::set<InputDeviceUsageSource> getUsageSourcesForKeyArgs(
+ const NotifyKeyArgs& args, const std::vector<InputDeviceInfo>& inputDevices) {
+ int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NONE;
+ for (const InputDeviceInfo& inputDevice : inputDevices) {
+ if (args.deviceId == inputDevice.getId()) {
+ keyboardType = inputDevice.getKeyboardType();
+ break;
+ }
+ }
+ return std::set{getUsageSourceForKeyArgs(keyboardType, args)};
+}
+
std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
std::set<InputDeviceUsageSource> sources;
diff --git a/services/inputflinger/InputDeviceMetricsSource.h b/services/inputflinger/InputDeviceMetricsSource.h
index a6be8f4..702badd 100644
--- a/services/inputflinger/InputDeviceMetricsSource.h
+++ b/services/inputflinger/InputDeviceMetricsSource.h
@@ -54,6 +54,10 @@
/** Returns the InputDeviceUsageSource that corresponds to the key event. */
InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType, const NotifyKeyArgs&);
+/** Returns the InputDeviceUsageSources that correspond to the key event. */
+std::set<InputDeviceUsageSource> getUsageSourcesForKeyArgs(
+ const NotifyKeyArgs&, const std::vector<InputDeviceInfo>& inputDevices);
+
/** Returns the InputDeviceUsageSources that correspond to the motion event. */
std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&);
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
index 9809148..1ed6c29 100644
--- a/services/inputflinger/dispatcher/DragState.h
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -17,6 +17,7 @@
#pragma once
#include <gui/WindowInfo.h>
+#include <input/Input.h>
#include <utils/StrongPointer.h>
#include <string>
@@ -25,8 +26,9 @@
namespace inputdispatcher {
struct DragState {
- DragState(const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t pointerId)
- : dragWindow(windowHandle), pointerId(pointerId) {}
+ DragState(const sp<android::gui::WindowInfoHandle>& windowHandle, DeviceId deviceId,
+ int32_t pointerId)
+ : dragWindow(windowHandle), deviceId(deviceId), pointerId(pointerId) {}
void dump(std::string& dump, const char* prefix = "");
// The window being dragged.
@@ -37,6 +39,8 @@
bool isStartDrag = false;
// Indicate if the stylus button is down at the start of the drag.
bool isStylusButtonDownAtStart = false;
+ // Indicate which device started this drag and drop.
+ const DeviceId deviceId;
// Indicate which pointer id is tracked by the drag and drop.
const int32_t pointerId;
};
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4b43c27..4f9d9e4 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1031,8 +1031,7 @@
nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
if (mPerDeviceInputLatencyMetricsFlag) {
- const nsecs_t nextStatisticsPush = processLatencyStatisticsLocked();
- nextWakeupTime = std::min(nextWakeupTime, nextStatisticsPush);
+ processLatencyStatisticsLocked();
}
// We are about to enter an infinitely long sleep, because we have no commands or
@@ -1117,9 +1116,8 @@
/**
* Check if enough time has passed since the last latency statistics push.
- * Return the time at which we should wake up next.
*/
-nsecs_t InputDispatcher::processLatencyStatisticsLocked() {
+void InputDispatcher::processLatencyStatisticsLocked() {
const nsecs_t currentTime = now();
// Log the atom recording latency statistics if more than 6 hours passed from the last
// push
@@ -1127,7 +1125,6 @@
mInputEventTimelineProcessor->pushLatencyStatistics();
mLastStatisticPushTime = currentTime;
}
- return mLastStatisticPushTime + LATENCY_STATISTICS_PUSH_INTERVAL;
}
std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(
@@ -2898,7 +2895,8 @@
}
void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
- if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId) {
+ if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId ||
+ mDragState->deviceId != entry.deviceId) {
return;
}
@@ -4553,7 +4551,7 @@
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
!mInputFilterEnabled) {
- mLatencyTracker.trackNotifyKey(args);
+ mLatencyTracker.trackListener(args);
}
}
@@ -4689,7 +4687,7 @@
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
!mInputFilterEnabled) {
- mLatencyTracker.trackNotifyMotion(args);
+ mLatencyTracker.trackListener(args);
}
needWake = enqueueInboundEventLocked(std::move(newEntry));
@@ -4796,6 +4794,39 @@
}
}
+bool InputDispatcher::shouldRejectInjectedMotionLocked(const MotionEvent& motionEvent,
+ DeviceId deviceId,
+ ui::LogicalDisplayId displayId,
+ std::optional<gui::Uid> targetUid,
+ int32_t flags) {
+ // Don't verify targeted injection, since it will only affect the caller's
+ // window, and the windows are typically destroyed at the end of the test.
+ if (targetUid.has_value()) {
+ return false;
+ }
+
+ // Verify all other injected streams, whether the injection is coming from apps or from
+ // input filter. Print an error if the stream becomes inconsistent with this event.
+ // An inconsistent injected event sent could cause a crash in the later stages of
+ // dispatching pipeline.
+ auto [it, _] = mInputFilterVerifiersByDisplay.try_emplace(displayId,
+ std::string("Injection on ") +
+ displayId.toString());
+ InputVerifier& verifier = it->second;
+
+ Result<void> result =
+ verifier.processMovement(deviceId, motionEvent.getSource(), motionEvent.getAction(),
+ motionEvent.getPointerCount(),
+ motionEvent.getPointerProperties(),
+ motionEvent.getSamplePointerCoords(), flags);
+ if (!result.ok()) {
+ logDispatchStateLocked();
+ LOG(ERROR) << "Inconsistent event: " << motionEvent << ", reason: " << result.error();
+ return true;
+ }
+ return false;
+}
+
InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* event,
std::optional<gui::Uid> targetUid,
InputEventInjectionSync syncMode,
@@ -4906,32 +4937,10 @@
mLock.lock();
- {
- // Verify all injected streams, whether the injection is coming from apps or from
- // input filter. Print an error if the stream becomes inconsistent with this event.
- // An inconsistent injected event sent could cause a crash in the later stages of
- // dispatching pipeline.
- auto [it, _] =
- mInputFilterVerifiersByDisplay.try_emplace(displayId,
- std::string("Injection on ") +
- displayId.toString());
- InputVerifier& verifier = it->second;
-
- Result<void> result =
- verifier.processMovement(resolvedDeviceId, motionEvent.getSource(),
- motionEvent.getAction(),
- motionEvent.getPointerCount(),
- motionEvent.getPointerProperties(),
- motionEvent.getSamplePointerCoords(), flags);
- if (!result.ok()) {
- logDispatchStateLocked();
- LOG(ERROR) << "Inconsistent event: " << motionEvent
- << ", reason: " << result.error();
- if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
- mLock.unlock();
- return InputEventInjectionResult::FAILED;
- }
- }
+ if (shouldRejectInjectedMotionLocked(motionEvent, resolvedDeviceId, displayId,
+ targetUid, flags)) {
+ mLock.unlock();
+ return InputEventInjectionResult::FAILED;
}
const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
@@ -5823,7 +5832,7 @@
}
// Track the pointer id for drag window and generate the drag state.
const size_t id = pointers.begin()->id;
- mDragState = std::make_unique<DragState>(toWindowHandle, id);
+ mDragState = std::make_unique<DragState>(toWindowHandle, deviceId, id);
}
// Synthesize cancel for old window and down for new window.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 24e36ae..fade853 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -299,6 +299,10 @@
// Event injection and synchronization.
std::condition_variable mInjectionResultAvailable;
+ bool shouldRejectInjectedMotionLocked(const MotionEvent& motion, DeviceId deviceId,
+ ui::LogicalDisplayId displayId,
+ std::optional<gui::Uid> targetUid, int32_t flags)
+ REQUIRES(mLock);
void setInjectionResult(const EventEntry& entry,
android::os::InputEventInjectionResult injectionResult);
void transformMotionEntryForInjectionLocked(MotionEntry&,
@@ -329,7 +333,7 @@
std::chrono::nanoseconds mMonitorDispatchingTimeout GUARDED_BY(mLock);
nsecs_t processAnrsLocked() REQUIRES(mLock);
- nsecs_t processLatencyStatisticsLocked() REQUIRES(mLock);
+ void processLatencyStatisticsLocked() REQUIRES(mLock);
std::chrono::nanoseconds getDispatchingTimeoutLocked(
const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index 0852026..0921e37 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -20,6 +20,7 @@
#include <inttypes.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android/os/IInputConstants.h>
@@ -32,6 +33,8 @@
namespace android::inputdispatcher {
+namespace {
+
/**
* Events that are older than this time will be considered mature, at which point we will stop
* waiting for the apps to provide further information about them.
@@ -62,27 +65,25 @@
}
}
+} // namespace
+
LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor)
: mTimelineProcessor(&processor) {}
-void LatencyTracker::trackNotifyMotion(const NotifyMotionArgs& args) {
- std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args);
- trackListener(args.id, args.eventTime, args.readTime, args.deviceId, sources, args.action,
- InputEventType::MOTION);
-}
+void LatencyTracker::trackListener(const NotifyArgs& args) {
+ if (const NotifyKeyArgs* keyArgs = std::get_if<NotifyKeyArgs>(&args)) {
+ std::set<InputDeviceUsageSource> sources =
+ getUsageSourcesForKeyArgs(*keyArgs, mInputDevices);
+ trackListener(keyArgs->id, keyArgs->eventTime, keyArgs->readTime, keyArgs->deviceId,
+ sources, keyArgs->action, InputEventType::KEY);
-void LatencyTracker::trackNotifyKey(const NotifyKeyArgs& args) {
- int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NONE;
- for (auto& inputDevice : mInputDevices) {
- if (args.deviceId == inputDevice.getId()) {
- keyboardType = inputDevice.getKeyboardType();
- break;
- }
+ } else if (const NotifyMotionArgs* motionArgs = std::get_if<NotifyMotionArgs>(&args)) {
+ std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(*motionArgs);
+ trackListener(motionArgs->id, motionArgs->eventTime, motionArgs->readTime,
+ motionArgs->deviceId, sources, motionArgs->action, InputEventType::MOTION);
+ } else {
+ LOG(FATAL) << "Unexpected NotifyArgs type: " << args.index();
}
- std::set<InputDeviceUsageSource> sources =
- std::set{getUsageSourceForKeyArgs(keyboardType, args)};
- trackListener(args.id, args.eventTime, args.readTime, args.deviceId, sources, args.action,
- InputEventType::KEY);
}
void LatencyTracker::trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime,
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index eb58222..79ea14c 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -44,28 +44,20 @@
*/
LatencyTracker(InputEventTimelineProcessor& processor);
/**
- * Start keeping track of an event identified by inputEventId. This must be called first.
+ * Start keeping track of an event identified by the args. This must be called first.
* If duplicate events are encountered (events that have the same eventId), none of them will be
- * tracked. This is because there is not enough information to correctly track them. The api's
- * 'trackFinishedEvent' and 'trackGraphicsLatency' only contain the inputEventId, and not the
- * eventTime. Even if eventTime was provided, there would still be a possibility of having
- * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we
- * must drop all duplicate data.
+ * tracked. This is because there is not enough information to correctly track them. It is
+ * always possible that two different events are generated with the same inputEventId and the
+ * same eventTime, so there aren't ways to distinguish those. Therefore, we must drop all
+ * duplicate data.
+ * For that reason, the APIs 'trackFinishedEvent' and 'trackGraphicsLatency' only receive the
+ * inputEventId as input.
*/
- void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId,
- const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction,
- InputEventType inputEventType);
+ void trackListener(const NotifyArgs& args);
void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime);
void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
- /**
- * trackNotifyMotion and trackNotifyKeys are intermediates between InputDispatcher and
- * trackListener. They compute the InputDeviceUsageSource set and call trackListener with
- * the relevant parameters for latency computation.
- */
- void trackNotifyMotion(const NotifyMotionArgs& args);
- void trackNotifyKey(const NotifyKeyArgs& args);
std::string dump(const char* prefix) const;
void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices);
@@ -90,6 +82,10 @@
InputEventTimelineProcessor* mTimelineProcessor;
std::vector<InputDeviceInfo> mInputDevices;
+
+ void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId,
+ const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction,
+ InputEventType inputEventType);
void reportAndPruneMatureRecords(nsecs_t newEventTime);
};
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 2f6c6d7..756a29b 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -96,6 +96,10 @@
// The key remapping has changed.
KEY_REMAPPING = 1u << 14,
+ // The mouse settings changed, this includes mouse reverse vertical scrolling and swap
+ // primary button.
+ MOUSE_SETTINGS = 1u << 15,
+
// All devices must be reopened.
MUST_REOPEN = 1u << 31,
};
@@ -252,6 +256,15 @@
// Keycodes to be remapped.
std::map<int32_t /* fromKeyCode */, int32_t /* toKeyCode */> keyRemapping;
+ // True if the external mouse should have its vertical scrolling reversed, so that rotating the
+ // wheel downwards scrolls the content upwards.
+ bool mouseReverseVerticalScrollingEnabled;
+
+ // True if the connected mouse should have its primary button (default: left click) swapped,
+ // so that the right click will be the primary action button and the left click will be the
+ // secondary action.
+ bool mouseSwapPrimaryButtonEnabled;
+
InputReaderConfiguration()
: virtualKeyQuietTime(0),
defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT),
@@ -282,7 +295,9 @@
shouldNotifyTouchpadHardwareState(false),
touchpadRightClickZoneEnabled(false),
stylusButtonMotionEventsEnabled(true),
- stylusPointerIconEnabled(false) {}
+ stylusPointerIconEnabled(false),
+ mouseReverseVerticalScrollingEnabled(false),
+ mouseSwapPrimaryButtonEnabled(false) {}
std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
diff --git a/services/inputflinger/include/NotifyArgsBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h
index 5b94d57..13eaaf3 100644
--- a/services/inputflinger/include/NotifyArgsBuilders.h
+++ b/services/inputflinger/include/NotifyArgsBuilders.h
@@ -24,13 +24,15 @@
#include <input/Keyboard.h>
#include <utils/Timers.h> // for nsecs_t, systemTime
+#include <cstdint>
#include <vector>
namespace android {
class MotionArgsBuilder {
public:
- MotionArgsBuilder(int32_t action, int32_t source) : mEventId(InputEvent::nextId()) {
+ MotionArgsBuilder(int32_t action, int32_t source, int32_t eventId = InputEvent::nextId())
+ : mEventId(eventId) {
mAction = action;
if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
addFlag(AMOTION_EVENT_FLAG_CANCELED);
@@ -55,6 +57,11 @@
return *this;
}
+ MotionArgsBuilder& readTime(nsecs_t readTime) {
+ mReadTime = readTime;
+ return *this;
+ }
+
MotionArgsBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
@@ -121,7 +128,7 @@
return {mEventId,
mEventTime,
- /*readTime=*/mEventTime,
+ mReadTime.value_or(mEventTime),
mDeviceId,
mSource,
mDisplayId,
@@ -151,6 +158,7 @@
uint32_t mSource;
nsecs_t mDownTime;
nsecs_t mEventTime;
+ std::optional<nsecs_t> mReadTime;
ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT};
uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
int32_t mActionButton{0};
@@ -165,7 +173,8 @@
class KeyArgsBuilder {
public:
- KeyArgsBuilder(int32_t action, int32_t source) : mEventId(InputEvent::nextId()) {
+ KeyArgsBuilder(int32_t action, int32_t source, int32_t eventId = InputEvent::nextId())
+ : mEventId(eventId) {
mAction = action;
mSource = source;
mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -187,6 +196,11 @@
return *this;
}
+ KeyArgsBuilder& readTime(nsecs_t readTime) {
+ mReadTime = readTime;
+ return *this;
+ }
+
KeyArgsBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
@@ -214,18 +228,10 @@
}
NotifyKeyArgs build() const {
- return {mEventId,
- mEventTime,
- /*readTime=*/mEventTime,
- mDeviceId,
- mSource,
- mDisplayId,
- mPolicyFlags,
- mAction,
- mFlags,
- mKeyCode,
- mScanCode,
- mMetaState,
+ return {mEventId, mEventTime, mReadTime.value_or(mEventTime),
+ mDeviceId, mSource, mDisplayId,
+ mPolicyFlags, mAction, mFlags,
+ mKeyCode, mScanCode, mMetaState,
mDownTime};
}
@@ -236,6 +242,7 @@
uint32_t mSource;
nsecs_t mDownTime;
nsecs_t mEventTime;
+ std::optional<nsecs_t> mReadTime;
ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT};
uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
int32_t mFlags{0};
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0865eed..f8cd973 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -659,6 +659,21 @@
}
bool EventHub::Device::hasKeycodeLocked(int keycode) const {
+ if (hasKeycodeInternalLocked(keycode)) {
+ return true;
+ }
+ if (!keyMap.haveKeyCharacterMap()) {
+ return false;
+ }
+ for (auto& fromKey : getKeyCharacterMap()->findKeyCodesMappedToKeyCode(keycode)) {
+ if (hasKeycodeInternalLocked(fromKey)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool EventHub::Device::hasKeycodeInternalLocked(int keycode) const {
if (!keyMap.haveKeyLayout()) {
return false;
}
@@ -676,7 +691,6 @@
if (usageCodes.size() > 0 && mscBitmask.test(MSC_SCAN)) {
return true;
}
-
return false;
}
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 6185f1a..02eeb0a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -691,16 +691,6 @@
return result;
}
-void InputDevice::updateMetaState(int32_t keyCode) {
- first_in_mappers<bool>([keyCode](InputMapper& mapper) {
- if (sourcesMatchMask(mapper.getSources(), AINPUT_SOURCE_KEYBOARD) &&
- mapper.updateMetaState(keyCode)) {
- return std::make_optional(true);
- }
- return std::optional<bool>();
- });
-}
-
void InputDevice::bumpGeneration() {
mGeneration = mContext->bumpGeneration();
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 8b664d5..ab27042 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -584,18 +584,9 @@
void InputReader::toggleCapsLockState(int32_t deviceId) {
std::scoped_lock _l(mLock);
- InputDevice* device = findInputDeviceLocked(deviceId);
- if (!device) {
- ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
- return;
+ if (mKeyboardClassifier->getKeyboardType(deviceId) == KeyboardType::ALPHABETIC) {
+ updateLedMetaStateLocked(mLedMetaState ^ AMETA_CAPS_LOCK_ON);
}
-
- if (device->isIgnored()) {
- ALOGW("Ignoring toggleCapsLock for ignored deviceId %" PRId32 ".", deviceId);
- return;
- }
-
- device->updateMetaState(AKEYCODE_CAPS_LOCK);
}
bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index edc3037..dffd8e3 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -680,6 +680,7 @@
void configureFd();
void populateAbsoluteAxisStates();
bool hasKeycodeLocked(int keycode) const;
+ bool hasKeycodeInternalLocked(int keycode) const;
void loadConfigurationLocked();
bool loadVirtualKeyMapLocked();
status_t loadKeyMapLocked();
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 62cc4da..8958d9e 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -122,8 +122,6 @@
std::optional<int32_t> getLightPlayerId(int32_t lightId);
int32_t getMetaState();
- void updateMetaState(int32_t keyCode);
-
void setKeyboardType(KeyboardType keyboardType);
void bumpGeneration();
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 20cdb59..630bd9b 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -164,6 +164,10 @@
changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) {
configureOnChangePointerSpeed(readerConfig);
}
+
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::MOUSE_SETTINGS)) {
+ configureOnChangeMouseSettings(readerConfig);
+ }
return out;
}
@@ -275,7 +279,12 @@
PointerCoords pointerCoords;
pointerCoords.clear();
- float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
+ // A negative value represents inverted scrolling direction.
+ // Applies only if the source is a mouse.
+ const bool isMouse =
+ (mSource == AINPUT_SOURCE_MOUSE) || (mSource == AINPUT_SOURCE_MOUSE_RELATIVE);
+ const int scrollingDirection = (mMouseReverseVerticalScrolling && isMouse) ? -1 : 1;
+ float vscroll = scrollingDirection * mCursorScrollAccumulator.getRelativeVWheel();
float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
bool scrolled = vscroll != 0 || hscroll != 0;
@@ -537,4 +546,9 @@
bumpGeneration();
}
+void CursorInputMapper::configureOnChangeMouseSettings(const InputReaderConfiguration& config) {
+ mMouseReverseVerticalScrolling = config.mouseReverseVerticalScrollingEnabled;
+ mCursorButtonAccumulator.setSwapLeftRightButtons(config.mouseSwapPrimaryButtonEnabled);
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 3fc370c..403e96d 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -121,6 +121,7 @@
nsecs_t mLastEventTime;
const bool mEnableNewMousePointerBallistics;
+ bool mMouseReverseVerticalScrolling = false;
explicit CursorInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
@@ -129,6 +130,7 @@
void configureOnPointerCapture(const InputReaderConfiguration& config);
void configureOnChangePointerSpeed(const InputReaderConfiguration& config);
void configureOnChangeDisplayInfo(const InputReaderConfiguration& config);
+ void configureOnChangeMouseSettings(const InputReaderConfiguration& config);
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 627df7f..9e9ed2d 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -109,10 +109,6 @@
return 0;
}
-bool InputMapper::updateMetaState(int32_t keyCode) {
- return false;
-}
-
std::list<NotifyArgs> InputMapper::updateExternalStylusState(const StylusState& state) {
return {};
}
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 75cc4bb..d4a86ac 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -111,11 +111,6 @@
virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) { return std::nullopt; }
virtual int32_t getMetaState();
- /**
- * Process the meta key and update the global meta state when changed.
- * Return true if the meta key could be handled by the InputMapper.
- */
- virtual bool updateMetaState(int32_t keyCode);
[[nodiscard]] virtual std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 38dcd65..585f61a 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -132,7 +132,9 @@
void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
- info.setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
+ if (const auto kcm = getDeviceContext().getKeyCharacterMap(); kcm != nullptr) {
+ info.setKeyCharacterMap(std::make_unique<KeyCharacterMap>(*kcm));
+ }
std::optional keyboardLayoutInfo = getKeyboardLayoutInfo();
if (keyboardLayoutInfo) {
@@ -390,15 +392,6 @@
return mMetaState;
}
-bool KeyboardInputMapper::updateMetaState(int32_t keyCode) {
- if (!android::isMetaKey(keyCode) || !getDeviceContext().hasKeyCode(keyCode)) {
- return false;
- }
-
- updateMetaStateIfNeeded(keyCode, false);
- return true;
-}
-
bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
int32_t oldMetaState = mMetaState;
int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
@@ -434,17 +427,21 @@
mMetaState &= ~(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON);
mMetaState |= getContext()->getLedMetaState();
- constexpr int32_t META_NUM = 3;
- const std::vector<int32_t> keyCodes{AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK,
- AKEYCODE_SCROLL_LOCK};
- const std::array<int32_t, META_NUM> metaCodes = {AMETA_CAPS_LOCK_ON, AMETA_NUM_LOCK_ON,
- AMETA_SCROLL_LOCK_ON};
- std::array<uint8_t, META_NUM> flags = {0, 0, 0};
- bool hasKeyLayout = getDeviceContext().markSupportedKeyCodes(keyCodes, flags.data());
+ std::vector<int32_t> keyCodesToCheck{AKEYCODE_NUM_LOCK, AKEYCODE_SCROLL_LOCK};
+ std::vector<int32_t> metaCodes{AMETA_NUM_LOCK_ON, AMETA_SCROLL_LOCK_ON};
+ // Check for physical CapsLock key only for non-alphabetic keyboards. For Alphabetic
+ // keyboards, we will allow Caps Lock even if there is no physical CapsLock key.
+ if (getDeviceContext().getKeyboardType() != KeyboardType::ALPHABETIC) {
+ keyCodesToCheck.push_back(AKEYCODE_CAPS_LOCK);
+ metaCodes.push_back(AMETA_CAPS_LOCK_ON);
+ }
+ size_t size = keyCodesToCheck.size();
+ std::vector<uint8_t> flags(size, 0);
+ bool hasKeyLayout = getDeviceContext().markSupportedKeyCodes(keyCodesToCheck, flags.data());
// If the device doesn't have the physical meta key it shouldn't generate the corresponding
// meta state.
if (hasKeyLayout) {
- for (int i = 0; i < META_NUM; i++) {
+ for (size_t i = 0; i < size; i++) {
if (!flags[i]) {
mMetaState &= ~metaCodes[i];
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 2df0b85..10bd424 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -45,7 +45,6 @@
int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
int32_t getMetaState() override;
- bool updateMetaState(int32_t keyCode) override;
std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
void updateLedState(bool reset) override;
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
index 9e722d4..456562c 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
@@ -47,6 +47,10 @@
mBtnTask = 0;
}
+void CursorButtonAccumulator::setSwapLeftRightButtons(bool shouldSwap) {
+ mSwapLeftRightButtons = shouldSwap;
+}
+
void CursorButtonAccumulator::process(const RawEvent& rawEvent) {
if (rawEvent.type == EV_KEY) {
switch (rawEvent.code) {
@@ -81,10 +85,12 @@
uint32_t CursorButtonAccumulator::getButtonState() const {
uint32_t result = 0;
if (mBtnLeft) {
- result |= AMOTION_EVENT_BUTTON_PRIMARY;
+ result |= mSwapLeftRightButtons ? AMOTION_EVENT_BUTTON_SECONDARY
+ : AMOTION_EVENT_BUTTON_PRIMARY;
}
if (mBtnRight) {
- result |= AMOTION_EVENT_BUTTON_SECONDARY;
+ result |= mSwapLeftRightButtons ? AMOTION_EVENT_BUTTON_PRIMARY
+ : AMOTION_EVENT_BUTTON_SECONDARY;
}
if (mBtnMiddle) {
result |= AMOTION_EVENT_BUTTON_TERTIARY;
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index 256b2bb..6990030 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -41,6 +41,8 @@
inline bool isExtraPressed() const { return mBtnExtra; }
inline bool isTaskPressed() const { return mBtnTask; }
+ void setSwapLeftRightButtons(bool shouldSwap);
+
private:
bool mBtnLeft;
bool mBtnRight;
@@ -51,6 +53,8 @@
bool mBtnExtra;
bool mBtnTask;
+ bool mSwapLeftRightButtons = false;
+
void clearButtons();
};
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index b27d02d..1762a45 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -205,9 +205,14 @@
args.clear();
args += process(EV_KEY, BTN_LEFT, 1);
args += process(EV_SYN, SYN_REPORT, 0);
+
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY)))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))));
// Move some more.
args.clear();
@@ -221,9 +226,76 @@
args += process(EV_KEY, BTN_LEFT, 0);
args += process(EV_SYN, SYN_REPORT, 0);
ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+}
+
+/**
+ * Test that enabling mouse swap primary button will have the left click result in a
+ * `SECONDARY_BUTTON` event and a right click will result in a `PRIMARY_BUTTON` event.
+ */
+TEST_F(CursorInputMapperUnitTest, SwappedPrimaryButtonPress) {
+ mReaderConfiguration.mouseSwapPrimaryButtonEnabled = true;
+ createMapper();
+ std::list<NotifyArgs> args;
+
+ // Now click the left mouse button , expect a `SECONDARY_BUTTON` button state.
+ args.clear();
+ args += process(EV_KEY, BTN_LEFT, 1);
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY)))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY))));
+
+ // Release the left button.
+ args.clear();
+ args += process(EV_KEY, BTN_LEFT, 0);
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY))),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+
+ // Now click the right mouse button , expect a `PRIMARY_BUTTON` button state.
+ args.clear();
+ args += process(EV_KEY, BTN_RIGHT, 1);
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY)))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))));
+
+ // Release the right button.
+ args.clear();
+ args += process(EV_KEY, BTN_RIGHT, 0);
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
}
/**
@@ -882,6 +954,51 @@
WithScroll(0.5f, 0.5f)))));
}
+TEST_F(CursorInputMapperUnitTest, ProcessReversedVerticalScroll) {
+ mReaderConfiguration.mouseReverseVerticalScrollingEnabled = true;
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ // Reversed vertical scrolling only affects the y-axis, expect it to be -1.0f to indicate the
+ // inverted scroll direction.
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(1.0f, -1.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessHighResReversedVerticalScroll) {
+ mReaderConfiguration.mouseReverseVerticalScrollingEnabled = true;
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(0.5f, -0.5f)))));
+}
+
/**
* When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
* pointer acceleration or speed processing should not be applied.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c5702e9..3413caa 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -5105,9 +5105,7 @@
/**
* Test that invalid HOVER events sent by accessibility do not cause a fatal crash.
*/
-TEST_F_WITH_FLAGS(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash,
- REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(com::android::input::flags,
- a11y_crash_on_inconsistent_event_stream))) {
+TEST_F(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
ui::LogicalDisplayId::DEFAULT);
@@ -5123,10 +5121,11 @@
.addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, hoverEnterBuilder.build()));
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ // Another HOVER_ENTER would be inconsistent, and should therefore fail to
+ // get injected.
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(*mDispatcher, hoverEnterBuilder.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
}
/**
@@ -12889,6 +12888,22 @@
// Remove drag window
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
+ // Complete the first event stream, even though the injection will fail because there aren't any
+ // valid targets to dispatch this event to. This is still needed to make the input stream
+ // consistent
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER)
+ .x(150)
+ .y(50))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(50)
+ .y(50))
+ .build(),
+ INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT));
+
// Inject a simple gesture, ensure dispatcher not crashed
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
@@ -12927,6 +12942,87 @@
<< "Drag and drop should not work with a hovering pointer";
}
+/**
+ * Two devices, we use the second pointer of Device A to start the drag, during the drag process, if
+ * we perform a click using Device B, the dispatcher should work well.
+ */
+TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouchAndMultiDevice) {
+ const DeviceId deviceA = 1;
+ const DeviceId deviceB = 2;
+ // First down on second window with deviceA.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .build());
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Second down on first window with deviceA
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Perform drag and drop from first window.
+ ASSERT_TRUE(startDrag(/*sendDown=*/false));
+
+ // Click first window with device B, we should ensure dispatcher work well.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(deviceB)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .deviceId(deviceB)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceB),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Move with device A.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(51).y(51))
+ .build());
+
+ mDragWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+ mWindow->consumeDragEvent(false, 51, 51);
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Releasing the drag pointer should cause drop.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(51).y(51))
+ .build());
+ mDragWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Release all pointers.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .build());
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+ mWindow->assertNoEvents();
+}
+
class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 17c37d5..6c8b65c 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -3671,40 +3671,6 @@
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
}
-TEST_F(KeyboardInputMapperTest, NoMetaStateWhenMetaKeysNotPresent) {
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_BUTTON_A, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_B, 0, AKEYCODE_BUTTON_B, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_X, 0, AKEYCODE_BUTTON_X, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- // Meta state should be AMETA_NONE after reset
- std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
- // Meta state should be AMETA_NONE with update, as device doesn't have the keys.
- mapper.updateMetaState(AKEYCODE_NUM_LOCK);
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- NotifyKeyArgs args;
- // Press button "A"
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_A, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
-
- // Button up.
- process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, BTN_A, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
-}
-
TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
// keyboard 1.
mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index f41b39a..a43e4e4 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -246,8 +246,6 @@
MOCK_METHOD(std::optional<int32_t>, getLightPlayerId, (int32_t lightId), ());
MOCK_METHOD(int32_t, getMetaState, (), ());
- MOCK_METHOD(void, updateMetaState, (int32_t keyCode), ());
-
MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ());
MOCK_METHOD(void, bumpGeneration, (), ());
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index 3f14c23..ca0f1e8 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -16,10 +16,14 @@
#include "../dispatcher/LatencyTracker.h"
#include "../InputDeviceMetricsSource.h"
+#include "NotifyArgsBuilders.h"
+#include "android/input.h"
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <binder/Binder.h>
#include <gtest/gtest.h>
+#include <input/PrintTools.h>
#include <inttypes.h>
#include <linux/input.h>
#include <log/log.h>
@@ -48,11 +52,44 @@
}
void setDefaultInputDeviceInfo(LatencyTracker& tracker) {
- InputDeviceInfo deviceInfo = generateTestDeviceInfo(
- /*vendorId=*/0, /*productId=*/0, DEVICE_ID);
+ InputDeviceInfo deviceInfo = generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID);
tracker.setInputDevices({deviceInfo});
}
+const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200);
+
+/**
+ * This is a convenience method for comparing timelines that also prints the difference between
+ * the two structures. This helps debugging when the timelines don't match.
+ * @param received the timeline that was actually received
+ * @param expected the timeline that we expected to receive
+ * @return true if the two timelines match, false otherwise.
+ */
+bool timelinesAreEqual(const InputEventTimeline& received, const InputEventTimeline& expected) {
+ LOG_IF(ERROR, expected.eventTime != received.eventTime)
+ << "Received timeline with eventTime=" << received.eventTime
+ << " instead of expected eventTime=" << expected.eventTime;
+ LOG_IF(ERROR, expected.readTime != received.readTime)
+ << "Received timeline with readTime=" << received.readTime
+ << " instead of expected readTime=" << expected.readTime;
+ LOG_IF(ERROR, expected.vendorId != received.vendorId)
+ << "Received timeline with vendorId=" << received.vendorId
+ << " instead of expected vendorId=" << expected.vendorId;
+ LOG_IF(ERROR, expected.productId != received.productId)
+ << "Received timeline with productId=" << received.productId
+ << " instead of expected productId=" << expected.productId;
+ LOG_IF(ERROR, expected.sources != received.sources)
+ << "Received timeline with sources=" << dumpSet(received.sources, ftl::enum_string)
+ << " instead of expected sources=" << dumpSet(expected.sources, ftl::enum_string);
+ LOG_IF(ERROR, expected.inputEventActionType != received.inputEventActionType)
+ << "Received timeline with inputEventActionType="
+ << ftl::enum_string(received.inputEventActionType)
+ << " instead of expected inputEventActionType="
+ << ftl::enum_string(expected.inputEventActionType);
+
+ return received == expected;
+}
+
} // namespace
const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds(
@@ -64,15 +101,14 @@
/*eventTime=*/2,
/*readTime=*/3,
/*vendorId=*/0,
- /*productId=*/0,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+ /*productId=*/0, {InputDeviceUsageSource::TOUCHSCREEN},
/*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
ConnectionTimeline expectedCT(/*deliveryTime=*/6, /*consumeTime=*/7, /*finishTime=*/8);
- std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline{};
graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9;
graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 10;
- expectedCT.setGraphicsTimeline(std::move(graphicsTimeline));
- t.connectionTimelines.emplace(sp<BBinder>::make(), std::move(expectedCT));
+ expectedCT.setGraphicsTimeline(graphicsTimeline);
+ t.connectionTimelines.emplace(sp<BBinder>::make(), expectedCT);
return t;
}
@@ -118,16 +154,19 @@
void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) {
const nsecs_t triggerEventTime =
lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1;
- mTracker->trackListener(/*inputEventId=*/1, triggerEventTime,
- /*readTime=*/3, DEVICE_ID,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN},
- AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
+ mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL,
+ AINPUT_SOURCE_TOUCHSCREEN, /*inputEventId=*/1)
+ .eventTime(triggerEventTime)
+ .readTime(3)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
}
-void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) {
+void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& expectedTimeline) {
ASSERT_FALSE(mReceivedTimelines.empty());
- const InputEventTimeline& t = mReceivedTimelines.front();
- ASSERT_EQ(timeline, t);
+ const InputEventTimeline& received = mReceivedTimelines.front();
+ ASSERT_TRUE(timelinesAreEqual(received, expectedTimeline));
mReceivedTimelines.pop_front();
}
@@ -148,6 +187,11 @@
break;
}
}
+ if (!found) {
+ for (const InputEventTimeline& receivedTimeline : mReceivedTimelines) {
+ LOG(ERROR) << "Received timeline with eventTime=" << receivedTimeline.eventTime;
+ }
+ }
ASSERT_TRUE(found) << "Could not find expected timeline with eventTime="
<< expectedTimeline.eventTime;
}
@@ -170,14 +214,20 @@
* any additional ConnectionTimeline's.
*/
TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) {
- mTracker->trackListener(/*inputEventId=*/1, /*eventTime=*/2,
- /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN},
- AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
+ mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL,
+ AINPUT_SOURCE_TOUCHSCREEN, /*inputEventId=*/1)
+ .eventTime(2)
+ .readTime(3)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
triggerEventReporting(/*eventTime=*/2);
assertReceivedTimeline(
InputEventTimeline{/*eventTime=*/2,
- /*readTime=*/3, /*vendorId=*/0, /*productID=*/0,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+ /*readTime=*/3,
+ /*vendorId=*/0,
+ /*productID=*/0,
+ {InputDeviceUsageSource::TOUCHSCREEN},
/*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT});
}
@@ -209,9 +259,13 @@
const auto& [connectionToken, expectedCT] = *expected.connectionTimelines.begin();
- mTracker->trackListener(inputEventId, expected.eventTime, expected.readTime, DEVICE_ID,
- {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
- InputEventType::MOTION);
+ mTracker->trackListener(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId)
+ .eventTime(expected.eventTime)
+ .readTime(expected.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
mTracker->trackFinishedEvent(inputEventId, connectionToken, expectedCT.deliveryTime,
expectedCT.consumeTime, expectedCT.finishTime);
mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline);
@@ -230,12 +284,20 @@
// In the following 2 calls to trackListener, the inputEventId's are the same, but event times
// are different.
- mTracker->trackListener(inputEventId, /*eventTime=*/1, readTime, DEVICE_ID,
- {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
- InputEventType::MOTION);
- mTracker->trackListener(inputEventId, /*eventTime=*/2, readTime, DEVICE_ID,
- {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
- InputEventType::MOTION);
+ mTracker->trackListener(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId)
+ .eventTime(1)
+ .readTime(readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
+ mTracker->trackListener(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId)
+ .eventTime(2)
+ .readTime(readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
triggerEventReporting(/*eventTime=*/2);
// Since we sent duplicate input events, the tracker should just delete all of them, because it
@@ -249,8 +311,7 @@
/*eventTime*/ 2,
/*readTime*/ 3,
/*vendorId=*/0,
- /*productId=*/0,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+ /*productId=*/0, {InputDeviceUsageSource::TOUCHSCREEN},
/*inputEventType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
timeline1.connectionTimelines.emplace(connection1,
ConnectionTimeline(/*deliveryTime*/ 6, /*consumeTime*/ 7,
@@ -266,8 +327,7 @@
/*eventTime=*/20,
/*readTime=*/30,
/*vendorId=*/0,
- /*productId=*/0,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+ /*productId=*/0, {InputDeviceUsageSource::TOUCHSCREEN},
/*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
timeline2.connectionTimelines.emplace(connection2,
ConnectionTimeline(/*deliveryTime=*/60,
@@ -280,13 +340,21 @@
connectionTimeline2.setGraphicsTimeline(std::move(graphicsTimeline2));
// Start processing first event
- mTracker->trackListener(inputEventId1, timeline1.eventTime, timeline1.readTime, DEVICE_ID,
- {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
- InputEventType::MOTION);
+ mTracker->trackListener(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId1)
+ .eventTime(timeline1.eventTime)
+ .readTime(timeline1.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
// Start processing second event
- mTracker->trackListener(inputEventId2, timeline2.eventTime, timeline2.readTime, DEVICE_ID,
- {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
- InputEventType::MOTION);
+ mTracker->trackListener(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId2)
+ .eventTime(timeline2.eventTime)
+ .readTime(timeline2.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
mTracker->trackFinishedEvent(inputEventId1, connection1, connectionTimeline1.deliveryTime,
connectionTimeline1.consumeTime, connectionTimeline1.finishTime);
@@ -311,10 +379,13 @@
const sp<IBinder>& token = timeline.connectionTimelines.begin()->first;
for (size_t i = 1; i <= 100; i++) {
- mTracker->trackListener(/*inputEventId=*/i, timeline.eventTime, timeline.readTime,
- /*deviceId=*/DEVICE_ID,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN},
- AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
+ mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL,
+ AINPUT_SOURCE_TOUCHSCREEN, /*inputEventId=*/i)
+ .eventTime(timeline.eventTime)
+ .readTime(timeline.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
expectedTimelines.push_back(InputEventTimeline{timeline.eventTime, timeline.readTime,
timeline.vendorId, timeline.productId,
timeline.sources,
@@ -344,9 +415,13 @@
expectedCT.consumeTime, expectedCT.finishTime);
mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline);
- mTracker->trackListener(inputEventId, expected.eventTime, expected.readTime, DEVICE_ID,
- {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
- InputEventType::MOTION);
+ mTracker->trackListener(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN, inputEventId)
+ .eventTime(expected.eventTime)
+ .readTime(expected.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
triggerEventReporting(expected.eventTime);
assertReceivedTimeline(InputEventTimeline{expected.eventTime, expected.readTime,
expected.vendorId, expected.productId,
@@ -362,20 +437,25 @@
constexpr int32_t inputEventId = 1;
InputEventTimeline timeline(
/*eventTime*/ 2, /*readTime*/ 3,
- /*vendorId=*/50, /*productId=*/60,
- /*sources=*/
- {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT},
+ /*vendorId=*/50, /*productId=*/60, {InputDeviceUsageSource::STYLUS_DIRECT},
/*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
InputDeviceInfo deviceInfo1 = generateTestDeviceInfo(
/*vendorId=*/5, /*productId=*/6, /*deviceId=*/DEVICE_ID + 1);
InputDeviceInfo deviceInfo2 = generateTestDeviceInfo(
/*vendorId=*/50, /*productId=*/60, /*deviceId=*/DEVICE_ID);
+ deviceInfo2.addSource(AINPUT_SOURCE_TOUCHSCREEN);
+ deviceInfo2.addSource(AINPUT_SOURCE_STYLUS);
mTracker->setInputDevices({deviceInfo1, deviceInfo2});
- mTracker->trackListener(inputEventId, timeline.eventTime, timeline.readTime, DEVICE_ID,
- {InputDeviceUsageSource::TOUCHSCREEN,
- InputDeviceUsageSource::STYLUS_DIRECT},
- AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
+ mTracker->trackListener(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, inputEventId)
+
+ .eventTime(timeline.eventTime)
+ .readTime(timeline.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(100).y(200))
+ .build());
triggerEventReporting(timeline.eventTime);
assertReceivedTimeline(timeline);
}
@@ -388,58 +468,74 @@
// Create timelines for different event types (Motion, Key)
InputEventTimeline motionDownTimeline(
/*eventTime*/ 2, /*readTime*/ 3,
- /*vendorId*/ 0, /*productId*/ 0,
- /*sources*/ {InputDeviceUsageSource::UNKNOWN},
- /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_DOWN);
+ /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::TOUCHSCREEN},
+ InputEventActionType::MOTION_ACTION_DOWN);
InputEventTimeline motionMoveTimeline(
/*eventTime*/ 4, /*readTime*/ 5,
- /*vendorId*/ 0, /*productId*/ 0,
- /*sources*/ {InputDeviceUsageSource::UNKNOWN},
- /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_MOVE);
+ /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::TOUCHSCREEN},
+ InputEventActionType::MOTION_ACTION_MOVE);
InputEventTimeline motionUpTimeline(
/*eventTime*/ 6, /*readTime*/ 7,
- /*vendorId*/ 0, /*productId*/ 0,
- /*sources*/ {InputDeviceUsageSource::UNKNOWN},
- /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_UP);
+ /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::TOUCHSCREEN},
+ InputEventActionType::MOTION_ACTION_UP);
InputEventTimeline keyDownTimeline(
/*eventTime*/ 8, /*readTime*/ 9,
- /*vendorId*/ 0, /*productId*/ 0,
- /*sources*/ {InputDeviceUsageSource::UNKNOWN},
- /*inputEventActionType*/ InputEventActionType::KEY);
+ /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::BUTTONS},
+ InputEventActionType::KEY);
InputEventTimeline keyUpTimeline(
/*eventTime*/ 10, /*readTime*/ 11,
- /*vendorId*/ 0, /*productId*/ 0,
- /*sources*/ {InputDeviceUsageSource::UNKNOWN},
- /*inputEventActionType*/ InputEventActionType::KEY);
+ /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::BUTTONS},
+ InputEventActionType::KEY);
InputEventTimeline unknownTimeline(
/*eventTime*/ 12, /*readTime*/ 13,
- /*vendorId*/ 0, /*productId*/ 0,
- /*sources*/ {InputDeviceUsageSource::UNKNOWN},
- /*inputEventActionType*/ InputEventActionType::UNKNOWN_INPUT_EVENT);
+ /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::TOUCHSCREEN},
+ InputEventActionType::UNKNOWN_INPUT_EVENT);
- mTracker->trackListener(inputEventId, motionDownTimeline.eventTime, motionDownTimeline.readTime,
- DEVICE_ID, motionDownTimeline.sources, AMOTION_EVENT_ACTION_DOWN,
- InputEventType::MOTION);
- mTracker->trackListener(inputEventId + 1, motionMoveTimeline.eventTime,
- motionMoveTimeline.readTime, DEVICE_ID, motionMoveTimeline.sources,
- AMOTION_EVENT_ACTION_MOVE, InputEventType::MOTION);
- mTracker->trackListener(inputEventId + 2, motionUpTimeline.eventTime, motionUpTimeline.readTime,
- DEVICE_ID, motionUpTimeline.sources, AMOTION_EVENT_ACTION_UP,
- InputEventType::MOTION);
- mTracker->trackListener(inputEventId + 3, keyDownTimeline.eventTime, keyDownTimeline.readTime,
- DEVICE_ID, keyDownTimeline.sources, AKEY_EVENT_ACTION_DOWN,
- InputEventType::KEY);
- mTracker->trackListener(inputEventId + 4, keyUpTimeline.eventTime, keyUpTimeline.readTime,
- DEVICE_ID, keyUpTimeline.sources, AKEY_EVENT_ACTION_UP,
- InputEventType::KEY);
- mTracker->trackListener(inputEventId + 5, unknownTimeline.eventTime, unknownTimeline.readTime,
- DEVICE_ID, unknownTimeline.sources, AMOTION_EVENT_ACTION_POINTER_DOWN,
- InputEventType::MOTION);
+ mTracker->trackListener(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, inputEventId)
+ .eventTime(motionDownTimeline.eventTime)
+ .readTime(motionDownTimeline.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
+ mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ inputEventId + 1)
+ .eventTime(motionMoveTimeline.eventTime)
+ .readTime(motionMoveTimeline.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
+ mTracker->trackListener(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, inputEventId + 2)
+ .eventTime(motionUpTimeline.eventTime)
+ .readTime(motionUpTimeline.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
+ mTracker->trackListener(
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD, inputEventId + 3)
+ .eventTime(keyDownTimeline.eventTime)
+ .readTime(keyDownTimeline.readTime)
+ .deviceId(DEVICE_ID)
+ .build());
+ mTracker->trackListener(
+ KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD, inputEventId + 4)
+ .eventTime(keyUpTimeline.eventTime)
+ .readTime(keyUpTimeline.readTime)
+ .deviceId(DEVICE_ID)
+ .build());
+ mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN, inputEventId + 5)
+ .eventTime(unknownTimeline.eventTime)
+ .readTime(unknownTimeline.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_TOUCH_POINTER)
+ .build());
triggerEventReporting(unknownTimeline.eventTime);
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 9e02502..11b038b 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -99,7 +99,6 @@
nullptr);
},
[&]() -> void { mapper.getMetaState(); },
- [&]() -> void { mapper.updateMetaState(fdp->ConsumeIntegral<int32_t>()); },
[&]() -> void { mapper.getAssociatedDisplayId(); },
})();
}
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
index 908fa40..157a333 100644
--- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -19,6 +19,7 @@
#include "../../InputDeviceMetricsSource.h"
#include "../InputEventTimeline.h"
+#include "NotifyArgsBuilders.h"
#include "dispatcher/LatencyTracker.h"
namespace android {
@@ -61,40 +62,49 @@
// Make some pre-defined tokens to ensure that some timelines are complete.
std::array<sp<IBinder> /*token*/, 10> predefinedTokens;
- for (size_t i = 0; i < predefinedTokens.size(); i++) {
- predefinedTokens[i] = sp<BBinder>::make();
+ for (sp<IBinder>& token : predefinedTokens) {
+ token = sp<BBinder>::make();
}
// Randomly invoke LatencyTracker api's until randomness is exhausted.
while (fdp.remaining_bytes() > 0) {
fdp.PickValueInArray<std::function<void()>>({
[&]() -> void {
- int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
- nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
- nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
+ const int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+ const nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
+ const nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
const DeviceId deviceId = fdp.ConsumeIntegral<int32_t>();
+ const int32_t source = fdp.ConsumeIntegral<int32_t>();
std::set<InputDeviceUsageSource> sources = {
fdp.ConsumeEnum<InputDeviceUsageSource>()};
const int32_t inputEventActionType = fdp.ConsumeIntegral<int32_t>();
const InputEventType inputEventType = fdp.ConsumeEnum<InputEventType>();
- tracker.trackListener(inputEventId, eventTime, readTime, deviceId, sources,
- inputEventActionType, inputEventType);
+ const NotifyMotionArgs args =
+ MotionArgsBuilder(inputEventActionType, source, inputEventId)
+ .eventTime(eventTime)
+ .readTime(readTime)
+ .deviceId(deviceId)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER)
+ .x(100)
+ .y(200))
+ .build();
+ tracker.trackListener(args);
},
[&]() -> void {
- int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+ const int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens);
- nsecs_t deliveryTime = fdp.ConsumeIntegral<nsecs_t>();
- nsecs_t consumeTime = fdp.ConsumeIntegral<nsecs_t>();
- nsecs_t finishTime = fdp.ConsumeIntegral<nsecs_t>();
+ const nsecs_t deliveryTime = fdp.ConsumeIntegral<nsecs_t>();
+ const nsecs_t consumeTime = fdp.ConsumeIntegral<nsecs_t>();
+ const nsecs_t finishTime = fdp.ConsumeIntegral<nsecs_t>();
tracker.trackFinishedEvent(inputEventId, connectionToken, deliveryTime,
consumeTime, finishTime);
},
[&]() -> void {
- int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+ const int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens);
- std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
- for (size_t i = 0; i < graphicsTimeline.size(); i++) {
- graphicsTimeline[i] = fdp.ConsumeIntegral<nsecs_t>();
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline{};
+ for (nsecs_t& t : graphicsTimeline) {
+ t = fdp.ConsumeIntegral<nsecs_t>();
}
tracker.trackGraphicsLatency(inputEventId, connectionToken, graphicsTimeline);
},
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 060508c..eabbb39 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2049,9 +2049,10 @@
}
ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
- if (mCurrentOperatingMode != NORMAL && mCurrentOperatingMode != REPLAY_DATA_INJECTION &&
- !isAllowListedPackage(connection->getPackageName())) {
- return INVALID_OPERATION;
+ if (mCurrentOperatingMode != NORMAL &&
+ !isInjectionMode(mCurrentOperatingMode) &&
+ !isAllowListedPackage(connection->getPackageName())) {
+ return INVALID_OPERATION;
}
SensorRecord* rec = mActiveSensors.valueFor(handle);
diff --git a/services/stats/Android.bp b/services/stats/Android.bp
index 6b99627..f698515 100644
--- a/services/stats/Android.bp
+++ b/services/stats/Android.bp
@@ -7,6 +7,11 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+vintf_fragment {
+ name: "android.frameworks.stats-service.xml",
+ src: "android.frameworks.stats-service.xml",
+}
+
cc_library_shared {
name: "libstatshidl",
srcs: [
@@ -38,7 +43,7 @@
local_include_dirs: [
"include/stats",
],
- vintf_fragments: [
+ vintf_fragment_modules: [
"android.frameworks.stats-service.xml",
],
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 041db1e..d500ae8 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -174,7 +174,6 @@
"DisplayHardware/VirtualDisplaySurface.cpp",
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
- "EventLog/EventLog.cpp",
"FrontEnd/LayerCreationArgs.cpp",
"FrontEnd/LayerHandle.cpp",
"FrontEnd/LayerSnapshot.cpp",
@@ -280,7 +279,7 @@
"libSurfaceFlingerProp",
],
- logtags: ["EventLog/EventLogTags.logtags"],
+ logtags: ["surfaceflinger.logtags"],
}
subdirs = [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index d1429a2..14a8fd6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -155,7 +155,7 @@
uint32_t geomBufferTransform{0};
Rect geomBufferSize;
Rect geomContentCrop;
- Rect geomCrop;
+ FloatRect geomCrop;
GenericLayerMetadataMap metadata;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 4dbf8d2..dcfe21a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -128,6 +128,10 @@
// Applies a HWC device layer request
virtual void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) = 0;
+ // Applies a HWC device layer lut
+ virtual void applyDeviceLayerLut(aidl::android::hardware::graphics::composer3::LutProperties,
+ ndk::ScopedFileDescriptor) = 0;
+
// Returns true if the composition settings scale pixels
virtual bool needsFiltering() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index d1eff24..a39abb4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -82,11 +82,13 @@
using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty;
+ using LayerLuts = android::HWComposer::DeviceRequestedChanges::LayerLuts;
virtual bool allLayersRequireClientComposition() const;
virtual void applyChangedTypesToLayers(const ChangedTypes&);
virtual void applyDisplayRequests(const DisplayRequests&);
virtual void applyLayerRequestsToLayers(const LayerRequests&);
virtual void applyClientTargetRequests(const ClientTargetProperty&);
+ virtual void applyLayerLutsToLayers(const LayerLuts&);
// Internal
virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index f383392..354a441 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -60,6 +60,8 @@
aidl::android::hardware::graphics::composer3::Composition) override;
void prepareForDeviceLayerRequests() override;
void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
+ void applyDeviceLayerLut(aidl::android::hardware::graphics::composer3::LutProperties,
+ ndk::ScopedFileDescriptor) override;
bool needsFiltering() const override;
std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 5fef63a..48c2f9c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -56,6 +56,9 @@
MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
MOCK_CONST_METHOD0(needsFiltering, bool());
MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>());
+ MOCK_METHOD(void, applyDeviceLayerLut,
+ (aidl::android::hardware::graphics::composer3::LutProperties,
+ ndk::ScopedFileDescriptor));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 5c5d0cd..cfcce47 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -198,25 +198,23 @@
// these buffers and fire a NO_FENCE to release it. This ensures that all
// promises for buffer releases are fulfilled at the end of composition.
void CompositionEngine::postComposition(CompositionRefreshArgs& args) {
- if (FlagManager::getInstance().ce_fence_promise()) {
- SFTRACE_CALL();
- ALOGV(__FUNCTION__);
+ SFTRACE_CALL();
+ ALOGV(__FUNCTION__);
- for (auto& layerFE : args.layers) {
- if (layerFE->getReleaseFencePromiseStatus() ==
- LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
- layerFE->setReleaseFence(Fence::NO_FENCE);
- }
+ for (auto& layerFE : args.layers) {
+ if (layerFE->getReleaseFencePromiseStatus() ==
+ LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
+ layerFE->setReleaseFence(Fence::NO_FENCE);
}
+ }
- // List of layersWithQueuedFrames does not necessarily overlap with
- // list of layers, so those layersWithQueuedFrames also need any
- // unfulfilled promises to be resolved for completeness.
- for (auto& layerFE : args.layersWithQueuedFrames) {
- if (layerFE->getReleaseFencePromiseStatus() ==
- LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
- layerFE->setReleaseFence(Fence::NO_FENCE);
- }
+ // List of layersWithQueuedFrames does not necessarily overlap with
+ // list of layers, so those layersWithQueuedFrames also need any
+ // unfulfilled promises to be resolved for completeness.
+ for (auto& layerFE : args.layersWithQueuedFrames) {
+ if (layerFE->getReleaseFencePromiseStatus() ==
+ LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
+ layerFE->setReleaseFence(Fence::NO_FENCE);
}
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 77b1940..b0164b7 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -278,6 +278,7 @@
applyDisplayRequests(changes->displayRequests);
applyLayerRequestsToLayers(changes->layerRequests);
applyClientTargetRequests(changes->clientTargetProperty);
+ applyLayerLutsToLayers(changes->layerLuts);
}
// Determine what type of composition we are doing from the final state
@@ -359,6 +360,25 @@
static_cast<ui::PixelFormat>(clientTargetProperty.clientTargetProperty.pixelFormat));
}
+void Display::applyLayerLutsToLayers(const LayerLuts& layerLuts) {
+ auto& mapper = getCompositionEngine().getHwComposer().getLutFileDescriptorMapper();
+ for (auto* layer : getOutputLayersOrderedByZ()) {
+ auto hwcLayer = layer->getHwcLayer();
+ if (!hwcLayer) {
+ continue;
+ }
+
+ if (auto lutsIt = layerLuts.find(hwcLayer); lutsIt != layerLuts.end()) {
+ if (auto mapperIt = mapper.find(hwcLayer); mapperIt != mapper.end()) {
+ layer->applyDeviceLayerLut(lutsIt->second,
+ ndk::ScopedFileDescriptor(mapperIt->second.release()));
+ }
+ }
+ }
+
+ mapper.clear();
+}
+
void Display::executeCommands() {
const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
if (mIsDisconnected || !halDisplayIdOpt) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 2d8f98f..22ab3d9 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1610,13 +1610,7 @@
releaseFence =
Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
}
- if (FlagManager::getInstance().ce_fence_promise()) {
- layer->getLayerFE().setReleaseFence(releaseFence);
- } else {
- layer->getLayerFE()
- .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(),
- outputState.layerFilter.layerStack);
- }
+ layer->getLayerFE().setReleaseFence(releaseFence);
}
// We've got a list of layers needing fences, that are disjoint with
@@ -1624,12 +1618,7 @@
// supply them with the present fence.
for (auto& weakLayer : mReleasedLayers) {
if (const auto layer = weakLayer.promote()) {
- if (FlagManager::getInstance().ce_fence_promise()) {
- layer->setReleaseFence(frame.presentFence);
- } else {
- layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(),
- outputState.layerFilter.layerStack);
- }
+ layer->setReleaseFence(frame.presentFence);
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 9b05084..2d46dc0 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -24,6 +24,7 @@
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <cstdint>
#include "system/graphics-base-v1.0.h"
+#include "ui/FloatRect.h"
#include <ui/HdrRenderTypeUtils.h>
@@ -37,6 +38,7 @@
#pragma clang diagnostic pop // ignored "-Wconversion"
using aidl::android::hardware::graphics::composer3::Composition;
+using aidl::android::hardware::graphics::composer3::LutProperties;
namespace android::compositionengine {
@@ -185,35 +187,35 @@
const auto& layerState = *getLayerFE().getCompositionState();
const auto& outputState = getOutput().getState();
+ // Convert from layer space to layerStackSpace
// apply the layer's transform, followed by the display's global transform
// here we're guaranteed that the layer's transform preserves rects
- Region activeTransparentRegion = layerState.transparentRegionHint;
const ui::Transform& layerTransform = layerState.geomLayerTransform;
- const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
- const Rect& bufferSize = layerState.geomBufferSize;
- Rect activeCrop = layerState.geomCrop;
- if (!activeCrop.isEmpty() && bufferSize.isValid()) {
- activeCrop = layerTransform.transform(activeCrop);
- if (!activeCrop.intersect(outputState.layerStackSpace.getContent(), &activeCrop)) {
- activeCrop.clear();
- }
- activeCrop = inverseLayerTransform.transform(activeCrop, true);
- // This needs to be here as transform.transform(Rect) computes the
- // transformed rect and then takes the bounding box of the result before
- // returning. This means
- // transform.inverse().transform(transform.transform(Rect)) != Rect
- // in which case we need to make sure the final rect is clipped to the
- // display bounds.
- if (!activeCrop.intersect(bufferSize, &activeCrop)) {
- activeCrop.clear();
- }
+ Region activeTransparentRegion = layerTransform.transform(layerState.transparentRegionHint);
+ if (!layerState.geomCrop.isEmpty() && layerState.geomBufferSize.isValid()) {
+ FloatRect activeCrop = layerTransform.transform(layerState.geomCrop);
+ activeCrop = activeCrop.intersect(outputState.layerStackSpace.getContent().toFloatRect());
+ const FloatRect& bufferSize =
+ layerTransform.transform(layerState.geomBufferSize.toFloatRect());
+ activeCrop = activeCrop.intersect(bufferSize);
+
// mark regions outside the crop as transparent
- activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top));
- activeTransparentRegion.orSelf(
- Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight()));
- activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom));
- activeTransparentRegion.orSelf(
- Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom));
+ Rect topRegion = Rect(layerTransform.transform(
+ FloatRect(0, 0, layerState.geomBufferSize.getWidth(), layerState.geomCrop.top)));
+ Rect bottomRegion = Rect(layerTransform.transform(
+ FloatRect(0, layerState.geomCrop.bottom, layerState.geomBufferSize.getWidth(),
+ layerState.geomBufferSize.getHeight())));
+ Rect leftRegion = Rect(layerTransform.transform(FloatRect(0, layerState.geomCrop.top,
+ layerState.geomCrop.left,
+ layerState.geomCrop.bottom)));
+ Rect rightRegion = Rect(layerTransform.transform(
+ FloatRect(layerState.geomCrop.right, layerState.geomCrop.top,
+ layerState.geomBufferSize.getWidth(), layerState.geomCrop.bottom)));
+
+ activeTransparentRegion.orSelf(topRegion);
+ activeTransparentRegion.orSelf(bottomRegion);
+ activeTransparentRegion.orSelf(leftRegion);
+ activeTransparentRegion.orSelf(rightRegion);
}
// reduce uses a FloatRect to provide more accuracy during the
@@ -231,13 +233,14 @@
geomLayerBounds.right += outset;
geomLayerBounds.bottom += outset;
}
- Rect frame{layerTransform.transform(reduce(geomLayerBounds, activeTransparentRegion))};
- if (!frame.intersect(outputState.layerStackSpace.getContent(), &frame)) {
- frame.clear();
- }
- const ui::Transform displayTransform{outputState.transform};
- return displayTransform.transform(frame);
+ geomLayerBounds = layerTransform.transform(geomLayerBounds);
+ FloatRect frame = reduce(geomLayerBounds, activeTransparentRegion);
+ frame = frame.intersect(outputState.layerStackSpace.getContent().toFloatRect());
+
+ // convert from layerStackSpace to displaySpace
+ const ui::Transform displayTransform{outputState.transform};
+ return Rect(displayTransform.transform(frame));
}
uint32_t OutputLayer::calculateOutputRelativeBufferTransform(
@@ -846,6 +849,12 @@
}
}
+void OutputLayer::applyDeviceLayerLut(LutProperties /*lutProperties*/,
+ ndk::ScopedFileDescriptor /*lutPfd*/) {
+ // TODO(b/329472856): decode the shared memory of the pfd, and store the lut data into
+ // OutputLayerCompositionState#hwc struct
+}
+
bool OutputLayer::needsFiltering() const {
const auto& state = getState();
const auto& sourceCrop = state.sourceCrop;
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 639164d..48ebc32 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -30,8 +30,6 @@
#include "TimeStats/TimeStats.h"
#include "gmock/gmock.h"
-#include <variant>
-
using namespace com::android::graphics::surfaceflinger;
namespace android::compositionengine {
@@ -494,9 +492,6 @@
};
TEST_F(CompositionEnginePostCompositionTest, postCompositionReleasesAllFences) {
- SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
- ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
-
EXPECT_CALL(*mLayer1FE, getReleaseFencePromiseStatus)
.WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::FULFILLED));
EXPECT_CALL(*mLayer2FE, getReleaseFencePromiseStatus)
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 39163ea..9c0e62c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -133,6 +133,7 @@
MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&));
MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&));
MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&));
+ MOCK_METHOD1(applyLayerLutsToLayers, void(const impl::Display::LayerLuts&));
const compositionengine::CompositionEngine& mCompositionEngine;
impl::OutputCompositionState mState;
@@ -212,6 +213,7 @@
aidl::android::hardware::graphics::common::Dataspace::UNKNOWN},
-1.f,
DimmingStage::NONE},
+ {},
};
void chooseCompositionStrategy(Display* display) {
@@ -615,6 +617,7 @@
EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
.Times(1);
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mDisplay, applyLayerLutsToLayers(mDeviceRequestedChanges.layerLuts)).Times(1);
chooseCompositionStrategy(mDisplay.get());
@@ -667,6 +670,7 @@
EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
.Times(1);
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mDisplay, applyLayerLutsToLayers(mDeviceRequestedChanges.layerLuts)).Times(1);
chooseCompositionStrategy(mDisplay.get());
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index e910c72..5c55ce7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -152,10 +152,7 @@
getOverlaySupport, (), (const, override));
MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
- MOCK_METHOD(status_t, getRequestedLuts,
- (PhysicalDisplayId,
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*),
- (override));
+ MOCK_METHOD((HWC2::Display::LutFileDescriptorMapper&), getLutFileDescriptorMapper, (), ());
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 04df0f7..b21533a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -30,6 +30,7 @@
#include "MockHWC2.h"
#include "MockHWComposer.h"
#include "RegionMatcher.h"
+#include "ui/FloatRect.h"
#include <aidl/android/hardware/graphics/composer3/Composition.h>
@@ -270,7 +271,7 @@
mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
- mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
+ mLayerFEState.geomCrop = FloatRect{0, 0, 1920, 1080};
mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
@@ -296,20 +297,20 @@
}
TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) {
- mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
+ mLayerFEState.geomCrop = FloatRect{100, 200, 300, 500};
const Rect expected{100, 200, 300, 500};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) {
- mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
+ mLayerFEState.geomCrop = FloatRect{100, 200, 300, 500};
mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
const Rect expected{1420, 100, 1720, 300};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) {
- mLayerFEState.geomCrop = Rect{};
+ mLayerFEState.geomCrop = FloatRect{};
const Rect expected{0, 0, 1920, 1080};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c34168d..1c18cd2 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -34,7 +34,6 @@
#include <ui/Rect.h>
#include <ui/Region.h>
-#include <cmath>
#include <cstdint>
#include <variant>
@@ -3263,57 +3262,9 @@
mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
-TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
- SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
- ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
- // Simulate getting release fences from each layer, and ensure they are passed to the
- // front-end layer interface for each layer correctly.
-
- mOutput.mState.isEnabled = true;
-
- // Create three unique fence instances
- sp<Fence> layer1Fence = sp<Fence>::make();
- sp<Fence> layer2Fence = sp<Fence>::make();
- sp<Fence> layer3Fence = sp<Fence>::make();
-
- Output::FrameFences frameFences;
- frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
- frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
- frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
-
- EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
- EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
-
- // Compare the pointers values of each fence to make sure the correct ones
- // are passed. This happens to work with the current implementation, but
- // would not survive certain calls like Fence::merge() which would return a
- // new instance.
- EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(_, _))
- .WillOnce([&layer1Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack) {
- EXPECT_EQ(FenceResult(layer1Fence), futureFenceResult.get());
- });
- EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(_, _))
- .WillOnce([&layer2Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack) {
- EXPECT_EQ(FenceResult(layer2Fence), futureFenceResult.get());
- });
- EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(_, _))
- .WillOnce([&layer3Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack) {
- EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
- });
-
- constexpr bool kFlushEvenWhenDisabled = false;
- mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
-}
-
TEST_F(OutputPostFramebufferTest, releaseFencesAreSetInLayerFE) {
- SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
- ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
// Simulate getting release fences from each layer, and ensure they are passed to the
// front-end layer interface for each layer correctly.
-
mOutput.mState.isEnabled = true;
// Create three unique fence instances
@@ -3350,37 +3301,7 @@
mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
-TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
- SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
- ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
-
- mOutput.mState.isEnabled = true;
- mOutput.mState.usesClientComposition = true;
-
- Output::FrameFences frameFences;
- frameFences.clientTargetAcquireFence = sp<Fence>::make();
- frameFences.layerFences.emplace(&mLayer1.hwc2Layer, sp<Fence>::make());
- frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
- frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
-
- EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
- EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
-
- // Fence::merge is called, and since none of the fences are actually valid,
- // Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
- // This is the best we can do without creating a real kernel fence object.
- EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed).WillOnce(Return());
- EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed).WillOnce(Return());
- EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed).WillOnce(Return());
-
- constexpr bool kFlushEvenWhenDisabled = false;
- mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
-}
-
TEST_F(OutputPostFramebufferTest, setReleaseFencesIncludeClientTargetAcquireFence) {
- SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
- ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
-
mOutput.mState.isEnabled = true;
mOutput.mState.usesClientComposition = true;
@@ -3403,62 +3324,7 @@
mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
-TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
- SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
- ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
-
- mOutput.mState.isEnabled = true;
- mOutput.mState.usesClientComposition = true;
-
- // This should happen even if there are no (current) output layers.
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
-
- // Load up the released layers with some mock instances
- sp<StrictMock<mock::LayerFE>> releasedLayer1 = sp<StrictMock<mock::LayerFE>>::make();
- sp<StrictMock<mock::LayerFE>> releasedLayer2 = sp<StrictMock<mock::LayerFE>>::make();
- sp<StrictMock<mock::LayerFE>> releasedLayer3 = sp<StrictMock<mock::LayerFE>>::make();
- Output::ReleasedLayers layers;
- layers.push_back(releasedLayer1);
- layers.push_back(releasedLayer2);
- layers.push_back(releasedLayer3);
- mOutput.setReleasedLayers(std::move(layers));
-
- // Set up a fake present fence
- sp<Fence> presentFence = sp<Fence>::make();
- Output::FrameFences frameFences;
- frameFences.presentFence = presentFence;
-
- EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
- EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
-
- // Each released layer should be given the presentFence.
- EXPECT_CALL(*releasedLayer1, onLayerDisplayed(_, _))
- .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack) {
- EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
- });
- EXPECT_CALL(*releasedLayer2, onLayerDisplayed(_, _))
- .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack) {
- EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
- });
- EXPECT_CALL(*releasedLayer3, onLayerDisplayed(_, _))
- .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack) {
- EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
- });
-
- constexpr bool kFlushEvenWhenDisabled = false;
- mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
-
- // After the call the list of released layers should have been cleared.
- EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
-}
-
TEST_F(OutputPostFramebufferTest, setReleasedLayersSentPresentFence) {
- SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
- ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
-
mOutput.mState.isEnabled = true;
mOutput.mState.usesClientComposition = true;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 66237b9..77bd804 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1547,7 +1547,8 @@
return error;
}
-Error AidlComposer::getRequestedLuts(Display display, std::vector<DisplayLuts::LayerLut>* outLuts) {
+Error AidlComposer::getRequestedLuts(Display display, std::vector<Layer>* outLayers,
+ std::vector<DisplayLuts::LayerLut>* outLuts) {
Error error = Error::NONE;
mMutex.lock_shared();
if (auto reader = getReader(display)) {
@@ -1556,6 +1557,11 @@
error = Error::BAD_DISPLAY;
}
mMutex.unlock_shared();
+
+ outLayers->reserve(outLuts->size());
+ for (const auto& layerLut : *outLuts) {
+ outLayers->emplace_back(translate<Layer>(layerLut.layer));
+ }
return error;
}
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 246223a..cdb67e4 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -245,7 +245,7 @@
Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
int32_t frameIntervalNs) override;
Error getRequestedLuts(
- Display display,
+ Display display, std::vector<Layer>* outLayers,
std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
outLuts) override;
Error setLayerLuts(
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 7db9a94..0905663 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -305,7 +305,7 @@
virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
int32_t frameIntervalNs) = 0;
- virtual Error getRequestedLuts(Display display,
+ virtual Error getRequestedLuts(Display display, std::vector<Layer>* outLayers,
std::vector<V3_0::DisplayLuts::LayerLut>* outLuts) = 0;
virtual Error setLayerLuts(Display display, Layer layer, std::vector<V3_0::Lut>& luts) = 0;
};
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index f1fa938..1df2ab1 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -609,17 +609,29 @@
return static_cast<Error>(error);
}
-Error Display::getRequestedLuts(std::vector<DisplayLuts::LayerLut>* outLayerLuts) {
- std::vector<DisplayLuts::LayerLut> tmpLayerLuts;
- const auto error = mComposer.getRequestedLuts(mId, &tmpLayerLuts);
- for (DisplayLuts::LayerLut& layerLut : tmpLayerLuts) {
- if (layerLut.lut.pfd.get() >= 0) {
- outLayerLuts->push_back({layerLut.layer,
- Lut{ndk::ScopedFileDescriptor(layerLut.lut.pfd.release()),
- layerLut.lut.lutProperties}});
+Error Display::getRequestedLuts(LayerLuts* outLuts,
+ LutFileDescriptorMapper& lutFileDescriptorMapper) {
+ std::vector<Hwc2::Layer> layerIds;
+ std::vector<DisplayLuts::LayerLut> tmpLuts;
+ const auto error = static_cast<Error>(mComposer.getRequestedLuts(mId, &layerIds, &tmpLuts));
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ uint32_t numElements = layerIds.size();
+ outLuts->clear();
+ for (uint32_t i = 0; i < numElements; ++i) {
+ auto layer = getLayerById(layerIds[i]);
+ if (layer) {
+ auto& layerLut = tmpLuts[i];
+ outLuts->emplace_or_replace(layer.get(), layerLut.lut.lutProperties);
+ lutFileDescriptorMapper.emplace_or_replace(layer.get(),
+ ndk::ScopedFileDescriptor(
+ layerLut.lut.pfd.release()));
}
}
- return static_cast<Error>(error);
+
+ return Error::NONE;
}
Error Display::getDisplayDecorationSupport(
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 8e2aeaf..61f92f4 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -20,6 +20,7 @@
#include <android-base/thread_annotations.h>
#include <ftl/expected.h>
#include <ftl/future.h>
+#include <ftl/small_map.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/HdrCapabilities.h>
@@ -107,6 +108,13 @@
virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
virtual std::optional<ui::Size> getPhysicalSizeInMm() const = 0;
+ static const int kLutFileDescriptorMapperSize = 20;
+ using LayerLuts =
+ ftl::SmallMap<HWC2::Layer*, aidl::android::hardware::graphics::composer3::LutProperties,
+ kLutFileDescriptorMapperSize>;
+ using LutFileDescriptorMapper =
+ ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, kLutFileDescriptorMapperSize>;
+
[[nodiscard]] virtual hal::Error acceptChanges() = 0;
[[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
createLayer() = 0;
@@ -183,8 +191,7 @@
aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
outClientTargetProperty) = 0;
[[nodiscard]] virtual hal::Error getRequestedLuts(
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
- outLuts) = 0;
+ LayerLuts* outLuts, LutFileDescriptorMapper& lutFileDescriptorMapper) = 0;
[[nodiscard]] virtual hal::Error getDisplayDecorationSupport(
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) = 0;
@@ -268,9 +275,8 @@
hal::Error getClientTargetProperty(
aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
outClientTargetProperty) override;
- hal::Error getRequestedLuts(
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
- outLuts) override;
+ hal::Error getRequestedLuts(LayerLuts* outLuts,
+ LutFileDescriptorMapper& lutFileDescriptorMapper) override;
hal::Error getDisplayDecorationSupport(
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) override;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index d08e261..7d77634 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -379,7 +379,7 @@
const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X);
const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y);
const DisplayConfiguration::Dpi hwcDpi =
- DisplayConfiguration::Dpi{dpiX == -1 ? dpiY : dpiX / 1000.f,
+ DisplayConfiguration::Dpi{dpiX == -1 ? dpiX : dpiX / 1000.f,
dpiY == -1 ? dpiY : dpiY / 1000.f};
const DisplayConfiguration::Dpi estimatedDPI =
getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode);
@@ -587,9 +587,14 @@
error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
RETURN_IF_HWC_ERROR_FOR("getClientTargetProperty", error, displayId, BAD_INDEX);
+ DeviceRequestedChanges::LayerLuts layerLuts;
+ error = hwcDisplay->getRequestedLuts(&layerLuts, mLutFileDescriptorMapper);
+ RETURN_IF_HWC_ERROR_FOR("getRequestedLuts", error, displayId, BAD_INDEX);
+
outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
std::move(layerRequests),
- std::move(clientTargetProperty)});
+ std::move(clientTargetProperty),
+ std::move(layerLuts)});
error = hwcDisplay->acceptChanges();
RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
@@ -978,21 +983,6 @@
return NO_ERROR;
}
-status_t HWComposer::getRequestedLuts(
- PhysicalDisplayId displayId,
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* outLuts) {
- RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
- const auto error = mDisplayData[displayId].hwcDisplay->getRequestedLuts(outLuts);
- if (error == hal::Error::UNSUPPORTED) {
- RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
- }
- if (error == hal::Error::BAD_PARAMETER) {
- RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
- }
- RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
- return NO_ERROR;
-}
-
status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
@@ -1036,6 +1026,11 @@
return mSupportedLayerGenericMetadata;
}
+ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, 20>&
+HWComposer::getLutFileDescriptorMapper() {
+ return mLutFileDescriptorMapper;
+}
+
void HWComposer::dumpOverlayProperties(std::string& result) const {
// dump overlay properties
result.append("OverlayProperties:\n");
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b95c619..7b04d67 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -53,6 +53,7 @@
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
+#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
namespace android {
@@ -90,11 +91,14 @@
aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
using DisplayRequests = hal::DisplayRequest;
using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
+ using LutProperties = aidl::android::hardware::graphics::composer3::LutProperties;
+ using LayerLuts = HWC2::Display::LayerLuts;
ChangedTypes changedTypes;
DisplayRequests displayRequests;
LayerRequests layerRequests;
ClientTargetProperty clientTargetProperty;
+ LayerLuts layerLuts;
};
struct HWCDisplayMode {
@@ -311,18 +315,15 @@
virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
Fps frameInterval) = 0;
-
- // Composer 4.0
- virtual status_t getRequestedLuts(
- PhysicalDisplayId,
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) = 0;
+ // mapper
+ virtual HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
const android::HWComposer::DeviceRequestedChanges& rhs) {
return lhs.changedTypes == rhs.changedTypes && lhs.displayRequests == rhs.displayRequests &&
lhs.layerRequests == rhs.layerRequests &&
- lhs.clientTargetProperty == rhs.clientTargetProperty;
+ lhs.clientTargetProperty == rhs.clientTargetProperty && lhs.layerLuts == rhs.layerLuts;
}
namespace impl {
@@ -480,11 +481,8 @@
status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
Fps frameInterval) override;
- // Composer 4.0
- status_t getRequestedLuts(
- PhysicalDisplayId,
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)
- override;
+ // get a mapper
+ HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
@@ -571,6 +569,8 @@
const size_t mMaxVirtualDisplayDimension;
const bool mUpdateDeviceProductInfoOnHotplugReconnect;
bool mEnableVrrTimeout;
+
+ HWC2::Display::LutFileDescriptorMapper mLutFileDescriptorMapper;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index ee1e07a..056ecd7 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1410,7 +1410,8 @@
return Error::NONE;
}
-Error HidlComposer::getRequestedLuts(Display, std::vector<DisplayLuts::LayerLut>*) {
+Error HidlComposer::getRequestedLuts(Display, std::vector<Layer>*,
+ std::vector<DisplayLuts::LayerLut>*) {
return Error::NONE;
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 701a54b..1cc23d1 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -352,7 +352,7 @@
Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
Error notifyExpectedPresent(Display, nsecs_t, int32_t) override;
Error getRequestedLuts(
- Display,
+ Display, std::vector<Layer>*,
std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)
override;
Error setLayerLuts(Display, Layer,
diff --git a/services/surfaceflinger/EventLog/EventLog.cpp b/services/surfaceflinger/EventLog/EventLog.cpp
deleted file mode 100644
index 3b60952..0000000
--- a/services/surfaceflinger/EventLog/EventLog.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <log/log.h>
-
-#include "EventLog.h"
-
-namespace android {
-
-ANDROID_SINGLETON_STATIC_INSTANCE(EventLog)
-
-
-EventLog::EventLog() {
-}
-
-void EventLog::doLogFrameDurations(const std::string_view& name, const int32_t* durations,
- size_t numDurations) {
- EventLog::TagBuffer buffer(LOGTAG_SF_FRAME_DUR);
- buffer.startList(1 + numDurations);
- buffer.writeString(name);
- for (size_t i = 0; i < numDurations; i++) {
- buffer.writeInt32(durations[i]);
- }
- buffer.endList();
- buffer.log();
-}
-
-void EventLog::logFrameDurations(const std::string_view& name, const int32_t* durations,
- size_t numDurations) {
- EventLog::getInstance().doLogFrameDurations(name, durations, numDurations);
-}
-
-// ---------------------------------------------------------------------------
-
-EventLog::TagBuffer::TagBuffer(int32_t tag)
- : mPos(0), mTag(tag), mOverflow(false) {
-}
-
-void EventLog::TagBuffer::log() {
- if (mOverflow) {
- ALOGW("couldn't log to binary event log: overflow.");
- } else if (android_bWriteLog(mTag, mStorage, mPos) < 0) {
- ALOGE("couldn't log to EventLog: %s", strerror(errno));
- }
- // purge the buffer
- mPos = 0;
- mOverflow = false;
-}
-
-void EventLog::TagBuffer::startList(int8_t count) {
- if (mOverflow) return;
- const size_t needed = 1 + sizeof(count);
- if (mPos + needed > STORAGE_MAX_SIZE) {
- mOverflow = true;
- return;
- }
- mStorage[mPos + 0] = EVENT_TYPE_LIST;
- mStorage[mPos + 1] = count;
- mPos += needed;
-}
-
-void EventLog::TagBuffer::endList() {
- if (mOverflow) return;
- const size_t needed = 1;
- if (mPos + needed > STORAGE_MAX_SIZE) {
- mOverflow = true;
- return;
- }
- mStorage[mPos + 0] = '\n';
- mPos += needed;
-}
-
-void EventLog::TagBuffer::writeInt32(int32_t value) {
- if (mOverflow) return;
- const size_t needed = 1 + sizeof(value);
- if (mPos + needed > STORAGE_MAX_SIZE) {
- mOverflow = true;
- return;
- }
- mStorage[mPos + 0] = EVENT_TYPE_INT;
- memcpy(&mStorage[mPos + 1], &value, sizeof(value));
- mPos += needed;
-}
-
-void EventLog::TagBuffer::writeInt64(int64_t value) {
- if (mOverflow) return;
- const size_t needed = 1 + sizeof(value);
- if (mPos + needed > STORAGE_MAX_SIZE) {
- mOverflow = true;
- return;
- }
- mStorage[mPos + 0] = EVENT_TYPE_LONG;
- memcpy(&mStorage[mPos + 1], &value, sizeof(value));
- mPos += needed;
-}
-
-void EventLog::TagBuffer::writeString(const std::string_view& value) {
- if (mOverflow) return;
- const size_t stringLen = value.length();
- const size_t needed = 1 + sizeof(int32_t) + stringLen;
- if (mPos + needed > STORAGE_MAX_SIZE) {
- mOverflow = true;
- return;
- }
- mStorage[mPos + 0] = EVENT_TYPE_STRING;
- memcpy(&mStorage[mPos + 1], &stringLen, sizeof(int32_t));
- memcpy(&mStorage[mPos + 5], value.data(), stringLen);
- mPos += needed;
-}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EventLog/EventLog.h b/services/surfaceflinger/EventLog/EventLog.h
deleted file mode 100644
index ee3587e..0000000
--- a/services/surfaceflinger/EventLog/EventLog.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2013 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 <utils/Errors.h>
-#include <utils/Singleton.h>
-
-#include <cstdint>
-#include <string_view>
-
-namespace android {
-
-class EventLog : public Singleton<EventLog> {
-
-public:
- static void logFrameDurations(const std::string_view& name, const int32_t* durations,
- size_t numDurations);
-
-protected:
- EventLog();
-
-private:
- /*
- * EventLogBuffer is a helper class to construct an in-memory event log
- * tag. In this version the buffer is not dynamic, so write operation can
- * fail if there is not enough space in the temporary buffer.
- * Once constructed, the buffer can be logger by calling the log()
- * method.
- */
-
- class TagBuffer {
- enum { STORAGE_MAX_SIZE = 128 };
- int32_t mPos;
- int32_t mTag;
- bool mOverflow;
- char mStorage[STORAGE_MAX_SIZE];
- public:
- explicit TagBuffer(int32_t tag);
-
- void startList(int8_t count);
- void endList();
-
- void writeInt32(int32_t);
- void writeInt64(int64_t);
- void writeString(const std::string_view&);
-
- void log();
- };
-
- friend class Singleton<EventLog>;
- EventLog(const EventLog&);
- EventLog& operator =(const EventLog&);
-
- enum { LOGTAG_SF_FRAME_DUR = 60100 };
- void doLogFrameDurations(const std::string_view& name, const int32_t* durations,
- size_t numDurations);
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index ca8cdc3..93d0313 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -26,16 +26,10 @@
#include <ui/FrameStats.h>
#include "FrameTracker.h"
-#include "EventLog/EventLog.h"
namespace android {
-FrameTracker::FrameTracker() :
- mOffset(0),
- mNumFences(0),
- mDisplayPeriod(0) {
- resetFrameCountersLocked();
-}
+FrameTracker::FrameTracker() : mOffset(0), mNumFences(0), mDisplayPeriod(0) {}
void FrameTracker::setDesiredPresentTime(nsecs_t presentTime) {
Mutex::Autolock lock(mMutex);
@@ -73,9 +67,6 @@
void FrameTracker::advanceFrame() {
Mutex::Autolock lock(mMutex);
- // Update the statistic to include the frame we just finished.
- updateStatsLocked(mOffset);
-
// Advance to the next frame.
mOffset = (mOffset+1) % NUM_FRAME_RECORDS;
mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
@@ -138,19 +129,12 @@
}
}
-void FrameTracker::logAndResetStats(const std::string_view& name) {
- Mutex::Autolock lock(mMutex);
- logStatsLocked(name);
- resetFrameCountersLocked();
-}
-
void FrameTracker::processFencesLocked() const {
FrameRecord* records = const_cast<FrameRecord*>(mFrameRecords);
int& numFences = const_cast<int&>(mNumFences);
for (int i = 1; i < NUM_FRAME_RECORDS && numFences > 0; i++) {
- size_t idx = (mOffset+NUM_FRAME_RECORDS-i) % NUM_FRAME_RECORDS;
- bool updated = false;
+ size_t idx = (mOffset + NUM_FRAME_RECORDS - i) % NUM_FRAME_RECORDS;
const std::shared_ptr<FenceTime>& rfence = records[idx].frameReadyFence;
if (rfence != nullptr) {
@@ -158,7 +142,6 @@
if (records[idx].frameReadyTime < INT64_MAX) {
records[idx].frameReadyFence = nullptr;
numFences--;
- updated = true;
}
}
@@ -169,59 +152,8 @@
if (records[idx].actualPresentTime < INT64_MAX) {
records[idx].actualPresentFence = nullptr;
numFences--;
- updated = true;
}
}
-
- if (updated) {
- updateStatsLocked(idx);
- }
- }
-}
-
-void FrameTracker::updateStatsLocked(size_t newFrameIdx) const {
- int* numFrames = const_cast<int*>(mNumFrames);
-
- if (mDisplayPeriod > 0 && isFrameValidLocked(newFrameIdx)) {
- size_t prevFrameIdx = (newFrameIdx+NUM_FRAME_RECORDS-1) %
- NUM_FRAME_RECORDS;
-
- if (isFrameValidLocked(prevFrameIdx)) {
- nsecs_t newPresentTime =
- mFrameRecords[newFrameIdx].actualPresentTime;
- nsecs_t prevPresentTime =
- mFrameRecords[prevFrameIdx].actualPresentTime;
-
- nsecs_t duration = newPresentTime - prevPresentTime;
- int numPeriods = int((duration + mDisplayPeriod/2) /
- mDisplayPeriod);
-
- for (int i = 0; i < NUM_FRAME_BUCKETS-1; i++) {
- int nextBucket = 1 << (i+1);
- if (numPeriods < nextBucket) {
- numFrames[i]++;
- return;
- }
- }
-
- // The last duration bucket is a catch-all.
- numFrames[NUM_FRAME_BUCKETS-1]++;
- }
- }
-}
-
-void FrameTracker::resetFrameCountersLocked() {
- for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
- mNumFrames[i] = 0;
- }
-}
-
-void FrameTracker::logStatsLocked(const std::string_view& name) const {
- for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
- if (mNumFrames[i] > 0) {
- EventLog::logFrameDurations(name, mNumFrames, NUM_FRAME_BUCKETS);
- return;
- }
}
}
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index bc412ae..fd6fadc 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -41,8 +41,6 @@
// frame time history.
enum { NUM_FRAME_RECORDS = 128 };
- enum { NUM_FRAME_BUCKETS = 7 };
-
FrameTracker();
// setDesiredPresentTime sets the time at which the current frame
@@ -142,13 +140,6 @@
// doesn't grow with NUM_FRAME_RECORDS.
int mNumFences;
- // mNumFrames keeps a count of the number of frames with a duration in a
- // particular range of vsync periods. Element n of the array stores the
- // number of frames with duration in the half-inclusive range
- // [2^n, 2^(n+1)). The last element of the array contains the count for
- // all frames with duration greater than 2^(NUM_FRAME_BUCKETS-1).
- int32_t mNumFrames[NUM_FRAME_BUCKETS];
-
// mDisplayPeriod is the display refresh period of the display for which
// this FrameTracker is gathering information.
nsecs_t mDisplayPeriod;
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 0788d1a..07a5724 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -61,6 +61,7 @@
ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
uint32_t parentId = UNASSIGNED_LAYER_ID;
uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
+ std::atomic<int32_t>* pendingBuffers = 0;
};
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 398e64a..b7d4cc5 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -72,7 +72,7 @@
bool premultipliedAlpha;
ui::Transform parentTransform;
Rect bufferSize;
- Rect croppedBufferSize;
+ FloatRect croppedBufferSize;
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
gui::LayerMetadata layerMetadata;
gui::LayerMetadata relativeLayerMetadata;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index ee605b7..10e212e 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -116,7 +116,7 @@
* that's already included.
*/
std::pair<FloatRect, bool> getInputBounds(const LayerSnapshot& snapshot, bool fillParentBounds) {
- FloatRect inputBounds = snapshot.croppedBufferSize.toFloatRect();
+ FloatRect inputBounds = snapshot.croppedBufferSize;
if (snapshot.hasBufferOrSidebandStream() && snapshot.croppedBufferSize.isValid() &&
snapshot.localTransform.getType() != ui::Transform::IDENTITY) {
inputBounds = snapshot.localTransform.transform(inputBounds);
@@ -220,7 +220,7 @@
}
// Check if the parent has cropped the buffer
- Rect bufferSize = snapshot.croppedBufferSize;
+ FloatRect bufferSize = snapshot.croppedBufferSize;
if (!bufferSize.isValid()) {
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
return;
@@ -970,7 +970,7 @@
parentRoundedCorner.radius.y *= t.getScaleY();
}
- FloatRect layerCropRect = snapshot.croppedBufferSize.toFloatRect();
+ FloatRect layerCropRect = snapshot.croppedBufferSize;
const vec2 radius(requested.cornerRadius, requested.cornerRadius);
RoundedCornerState layerSettings(layerCropRect, radius);
const bool layerSettingsValid = layerSettings.hasRoundedCorners() && !layerCropRect.isEmpty();
@@ -1061,7 +1061,7 @@
requested.externalTexture ? snapshot.bufferSize.toFloatRect() : parentBounds;
snapshot.geomLayerCrop = parentBounds;
if (!requested.crop.isEmpty()) {
- snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop.toFloatRect());
+ snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop);
}
snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(snapshot.geomLayerCrop);
snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
@@ -1072,10 +1072,10 @@
snapshot.geomLayerTransform.transform(geomLayerBoundsWithoutTransparentRegion);
snapshot.parentTransform = parentSnapshot.geomLayerTransform;
- // Subtract the transparent region and snap to the bounds
- const Rect bounds =
- RequestedLayerState::reduce(snapshot.croppedBufferSize, requested.transparentRegion);
if (requested.potentialCursor) {
+ // Subtract the transparent region and snap to the bounds
+ const Rect bounds = RequestedLayerState::reduce(Rect(snapshot.croppedBufferSize),
+ requested.transparentRegion);
snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
}
}
@@ -1192,7 +1192,8 @@
snapshot.inputInfo.inputConfig |= InputConfig::TRUSTED_OVERLAY;
}
- snapshot.inputInfo.contentSize = snapshot.croppedBufferSize.getSize();
+ snapshot.inputInfo.contentSize = {snapshot.croppedBufferSize.getHeight(),
+ snapshot.croppedBufferSize.getWidth()};
// If the layer is a clone, we need to crop the input region to cloned root to prevent
// touches from going outside the cloned area.
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 5734ccf..713a5c5 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -56,7 +56,8 @@
ownerUid(args.ownerUid),
ownerPid(args.ownerPid),
parentId(args.parentId),
- layerIdToMirror(args.layerIdToMirror) {
+ layerIdToMirror(args.layerIdToMirror),
+ pendingBuffers(args.pendingBuffers) {
layerId = static_cast<int32_t>(args.sequence);
changes |= RequestedLayerState::Changes::Created;
metadata.merge(args.metadata);
@@ -96,7 +97,7 @@
LLOGV(layerId, "Created %s flags=%d", getDebugString().c_str(), flags);
color.a = 1.0f;
- crop.makeInvalid();
+ crop = {0, 0, -1, -1};
z = 0;
layerStack = ui::DEFAULT_LAYER_STACK;
transformToDisplayInverse = false;
@@ -473,10 +474,10 @@
return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
}
-Rect RequestedLayerState::getCroppedBufferSize(const Rect& bufferSize) const {
- Rect size = bufferSize;
+FloatRect RequestedLayerState::getCroppedBufferSize(const Rect& bufferSize) const {
+ FloatRect size = bufferSize.toFloatRect();
if (!crop.isEmpty() && size.isValid()) {
- size.intersect(crop, &size);
+ size = size.intersect(crop);
} else if (!crop.isEmpty()) {
size = crop;
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 1d96dff..7ddd7ba 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -79,7 +79,7 @@
bool isHiddenByPolicy() const;
half4 getColor() const;
Rect getBufferSize(uint32_t displayRotationFlags) const;
- Rect getCroppedBufferSize(const Rect& bufferSize) const;
+ FloatRect getCroppedBufferSize(const Rect& bufferSize) const;
Rect getBufferCrop() const;
std::string getDebugString() const;
std::string getDebugStringShort() const;
@@ -131,6 +131,7 @@
uint64_t barrierFrameNumber = 0;
uint32_t barrierProducerId = 0;
std::string debugName;
+ std::atomic<int32_t>* pendingBuffers = 0;
// book keeping states
bool handleAlive = true;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d132652..c88092b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -138,7 +138,7 @@
args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))) {
ALOGV("Creating Layer %s", getDebugName());
- mDrawingState.crop.makeInvalid();
+ mDrawingState.crop = {0, 0, -1, -1};
mDrawingState.sequence = 0;
mDrawingState.transform.set(0, 0);
mDrawingState.frameNumber = 0;
@@ -183,7 +183,6 @@
mFlinger->mTimeStats->onDestroy(layerId);
mFlinger->mFrameTracer->onDestroy(layerId);
- mFrameTracker.logAndResetStats(mName);
mFlinger->onLayerDestroyed(this);
const auto currentTime = std::chrono::steady_clock::now();
@@ -324,7 +323,7 @@
Rect Layer::getCroppedBufferSize(const State& s) const {
Rect size = getBufferSize(s);
- Rect crop = getCrop(s);
+ Rect crop = Rect(getCrop(s));
if (!crop.isEmpty() && size.isValid()) {
size.intersect(crop, &size);
} else if (!crop.isEmpty()) {
@@ -381,7 +380,7 @@
mTransactionFlags |= mask;
}
-bool Layer::setCrop(const Rect& crop) {
+bool Layer::setCrop(const FloatRect& crop) {
if (mDrawingState.crop == crop) return false;
mDrawingState.sequence++;
mDrawingState.crop = crop;
@@ -613,10 +612,6 @@
mFrameTracker.clearStats();
}
-void Layer::logFrameStats() {
- mFrameTracker.logAndResetStats(mName);
-}
-
void Layer::getFrameStats(FrameStats* outStats) const {
mFrameTracker.getStats(outStats);
}
@@ -764,54 +759,6 @@
}
}
-void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack layerStack,
- std::function<FenceResult(FenceResult)>&& continuation) {
- sp<CallbackHandle> ch = findCallbackHandle();
-
- if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) {
- futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share();
- }
-
- if (ch != nullptr) {
- ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
- ch->previousSharedReleaseFences.emplace_back(std::move(futureFenceResult));
- ch->name = mName;
- } else if (FlagManager::getInstance().screenshot_fence_preservation()) {
- // If we didn't get a release callback yet, e.g. some scenarios when capturing screenshots
- // asynchronously, then make sure we don't drop the fence.
- mPreviousReleaseFenceAndContinuations.emplace_back(std::move(futureFenceResult),
- std::move(continuation));
- std::vector<FenceAndContinuation> mergedFences;
- sp<Fence> prevFence = nullptr;
- // For a layer that's frequently screenshotted, try to merge fences to make sure we don't
- // grow unbounded.
- for (const auto& futureAndContinuation : mPreviousReleaseFenceAndContinuations) {
- auto result = futureAndContinuation.future.wait_for(0s);
- if (result != std::future_status::ready) {
- mergedFences.emplace_back(futureAndContinuation);
- continue;
- }
-
- mergeFence(getDebugName(),
- futureAndContinuation.chain().get().value_or(Fence::NO_FENCE), prevFence);
- }
- if (prevFence != nullptr) {
- mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share());
- }
-
- mPreviousReleaseFenceAndContinuations.swap(mergedFences);
- }
-
- if (mBufferInfo.mBuffer) {
- mPreviouslyPresentedLayerStacks.push_back(layerStack);
- }
-
- if (mDrawingState.frameNumber > 0) {
- mDrawingState.previousFrameNumber = mDrawingState.frameNumber;
- }
-}
-
void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
handle->bufferReleaseChannel = mBufferReleaseChannel;
@@ -1124,22 +1071,13 @@
handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
handle->frameNumber = mDrawingState.frameNumber;
handle->previousFrameNumber = mDrawingState.previousFrameNumber;
- if (FlagManager::getInstance().ce_fence_promise() &&
- mPreviousReleaseBufferEndpoint == handle->listener) {
+ if (mPreviousReleaseBufferEndpoint == handle->listener) {
// Add fence from previous screenshot now so that it can be dispatched to the
// client.
for (auto& [_, future] : mAdditionalPreviousReleaseFences) {
handle->previousReleaseFences.emplace_back(std::move(future));
}
mAdditionalPreviousReleaseFences.clear();
- } else if (FlagManager::getInstance().screenshot_fence_preservation() &&
- mPreviousReleaseBufferEndpoint == handle->listener) {
- // Add fences from previous screenshots now so that they can be dispatched to the
- // client.
- for (const auto& futureAndContinution : mPreviousReleaseFenceAndContinuations) {
- handle->previousSharedReleaseFences.emplace_back(futureAndContinution.chain());
- }
- mPreviousReleaseFenceAndContinuations.clear();
}
// Store so latched time and release fence can be set
mDrawingState.callbackHandles.push_back(handle);
@@ -1364,7 +1302,7 @@
}
void Layer::decrementPendingBufferCount() {
- int32_t pendingBuffers = --mPendingBufferTransactions;
+ int32_t pendingBuffers = --mPendingBuffers;
tracePendingBufferCount(pendingBuffers);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 7fd77f5..a2716c6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -95,7 +95,7 @@
struct State {
int32_t sequence; // changes when visible regions can change
// Crop is expressed in layer space coordinate.
- Rect crop;
+ FloatRect crop;
LayerMetadata metadata;
ui::Dataspace dataspace;
@@ -172,7 +172,7 @@
// be delayed until the resize completes.
// Buffer space
- bool setCrop(const Rect& crop);
+ bool setCrop(const FloatRect& crop);
bool setTransform(uint32_t /*transform*/);
bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
@@ -198,7 +198,7 @@
Region getVisibleRegion(const DisplayDevice*) const;
void updateLastLatchTime(nsecs_t latchtime);
- Rect getCrop(const Layer::State& s) const { return s.crop; }
+ Rect getCrop(const Layer::State& s) const { return Rect(s.crop); }
// from graphics API
static ui::Dataspace translateDataspace(ui::Dataspace dataspace);
@@ -259,8 +259,6 @@
bool fenceHasSignaled() const;
void onPreComposition(nsecs_t refreshStartTime);
- void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack,
- std::function<FenceResult(FenceResult)>&& continuation = nullptr);
// Tracks mLastClientCompositionFence and gets the callback handle for this layer.
sp<CallbackHandle> findCallbackHandle();
@@ -371,7 +369,7 @@
// See mPendingBufferTransactions
void decrementPendingBufferCount();
- std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; }
+ std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBuffers; }
std::string getPendingBufferCounterName() { return mBlastTransactionName; }
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
@@ -389,20 +387,6 @@
// from the layer.
std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks;
- struct FenceAndContinuation {
- ftl::SharedFuture<FenceResult> future;
- std::function<FenceResult(FenceResult)> continuation;
-
- ftl::SharedFuture<FenceResult> chain() const {
- if (continuation) {
- return ftl::Future(future).then(continuation).share();
- } else {
- return future;
- }
- }
- };
- std::vector<FenceAndContinuation> mPreviousReleaseFenceAndContinuations;
-
// Release fences for buffers that have not yet received a release
// callback. A release callback may not be given when capturing
// screenshots asynchronously. There may be no buffer update for the
@@ -564,7 +548,7 @@
// - If the integer increases, a buffer arrived at the server.
// - If the integer decreases in latchBuffer, that buffer was latched
// - If the integer decreases in setBuffer, a buffer was dropped
- std::atomic<int32_t> mPendingBufferTransactions{0};
+ std::atomic<int32_t> mPendingBuffers{0};
// Contains requested position and matrix updates. This will be applied if the client does
// not specify a destination frame.
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index b05f0ee..f64ba9e 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -26,7 +26,6 @@
#include "LayerFE.h"
#include "SurfaceFlinger.h"
-#include "common/FlagManager.h"
#include "ui/FenceResult.h"
#include "ui/LayerStack.h"
@@ -84,8 +83,7 @@
// Ensures that no promise is left unfulfilled before the LayerFE is destroyed.
// An unfulfilled promise could occur when a screenshot is attempted, but the
// render area is invalid and there is no memory for the capture result.
- if (FlagManager::getInstance().ce_fence_promise() &&
- mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) {
+ if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) {
setReleaseFence(Fence::NO_FENCE);
}
}
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 5eea45b..44cd319 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -106,6 +106,13 @@
outRect.right = proto.right();
}
+void LayerProtoHelper::readFromProto(const perfetto::protos::RectProto& proto, FloatRect& outRect) {
+ outRect.left = proto.left();
+ outRect.top = proto.top();
+ outRect.bottom = proto.bottom();
+ outRect.right = proto.right();
+}
+
void LayerProtoHelper::writeToProto(
const FloatRect& rect,
std::function<perfetto::protos::FloatRectProto*()> getFloatRectProto) {
@@ -180,10 +187,6 @@
void LayerProtoHelper::writeToProto(
const WindowInfo& inputInfo,
std::function<perfetto::protos::InputWindowInfoProto*()> getInputWindowInfoProto) {
- if (inputInfo.token == nullptr) {
- return;
- }
-
perfetto::protos::InputWindowInfoProto* proto = getInputWindowInfoProto();
proto->set_layout_params_flags(inputInfo.layoutParamsFlags.get());
proto->set_input_config(inputInfo.inputConfig.get());
@@ -427,7 +430,7 @@
layerInfo->mutable_color_transform());
}
- LayerProtoHelper::writeToProto(snapshot.croppedBufferSize.toFloatRect(),
+ LayerProtoHelper::writeToProto(snapshot.croppedBufferSize,
[&]() { return layerInfo->mutable_source_bounds(); });
LayerProtoHelper::writeToProto(snapshot.transformedBounds,
[&]() { return layerInfo->mutable_screen_bounds(); });
@@ -455,7 +458,7 @@
return layerInfo->mutable_requested_position();
});
- LayerProtoHelper::writeToProto(requestedState.crop,
+ LayerProtoHelper::writeToProto(Rect(requestedState.crop),
[&]() { return layerInfo->mutable_crop(); });
layerInfo->set_is_opaque(snapshot.contentOpaque);
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 41ea684..3ca553a 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -44,6 +44,7 @@
std::function<perfetto::protos::RectProto*()> getRectProto);
static void writeToProto(const Rect& rect, perfetto::protos::RectProto* rectProto);
static void readFromProto(const perfetto::protos::RectProto& proto, Rect& outRect);
+ static void readFromProto(const perfetto::protos::RectProto& proto, FloatRect& outRect);
static void writeToProto(const FloatRect& rect,
std::function<perfetto::protos::FloatRectProto*()> getFloatRectProto);
static void writeToProto(const Region& region,
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 06c2f26..011fd9e 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -355,7 +355,7 @@
FenceResult fenceResult;
if (FlagManager::getInstance().single_hop_screenshot() &&
- FlagManager::getInstance().ce_fence_promise() && mFlinger.mRenderEngine->isThreaded()) {
+ mFlinger.mRenderEngine->isThreaded()) {
std::vector<sp<LayerFE>> layerFEs;
auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder,
getLayerSnapshotsFn, layerFEs);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5e13154..b83ff19 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -426,6 +426,8 @@
eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) {
const bool isPacesetter =
FTL_FAKE_GUARD(kMainThreadContext,
@@ -446,6 +448,7 @@
return isPacesetter;
}
+#pragma clang diagnostic pop
void Scheduler::emitModeChangeIfNeeded() {
if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
@@ -483,6 +486,8 @@
}
}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
void Scheduler::updatePhaseConfiguration(PhysicalDisplayId displayId, Fps refreshRate) {
const bool isPacesetter =
FTL_FAKE_GUARD(kMainThreadContext,
@@ -494,6 +499,7 @@
setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
refreshRate.getPeriod());
}
+#pragma clang diagnostic pop
void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) {
mRefreshRateStats->setPowerMode(powerMode);
@@ -909,6 +915,8 @@
}
}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
void Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
const bool changed = (std::scoped_lock(mPolicyLock),
updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate));
@@ -917,6 +925,7 @@
onFrameRateOverridesChanged();
}
}
+#pragma clang diagnostic pop
bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals,
Fps displayRefreshRate) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bbdfc12..09dbc59 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1216,6 +1216,7 @@
const auto mode = display->refreshRateSelector().getActiveMode();
info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId());
info->renderFrameRate = mode.fps.getValue();
+ info->hasArrSupport = mode.modePtr->getVrrConfig() && FlagManager::getInstance().vrr_config();
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
@@ -1655,6 +1656,22 @@
outProperties->combinations.emplace_back(outCombination);
}
outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces;
+ if (aidlProperties.lutProperties.has_value()) {
+ std::vector<gui::LutProperties> outLutProperties;
+ for (const auto& properties : aidlProperties.lutProperties.value()) {
+ gui::LutProperties currentProperties;
+ currentProperties.dimension =
+ static_cast<gui::LutProperties::Dimension>(properties->dimension);
+ currentProperties.size = properties->size;
+ currentProperties.samplingKeys.reserve(properties->samplingKeys.size());
+ std::transform(properties->samplingKeys.cbegin(), properties->samplingKeys.cend(),
+ std::back_inserter(currentProperties.samplingKeys), [](const auto& val) {
+ return static_cast<gui::LutProperties::SamplingKey>(val);
+ });
+ outLutProperties.push_back(std::move(currentProperties));
+ }
+ outProperties->lutProperties.emplace(outLutProperties.begin(), outLutProperties.end());
+ }
return NO_ERROR;
}
@@ -2742,16 +2759,6 @@
compositionengine::Feature::kSnapshotLayerMetadata);
refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
-
- if (!FlagManager::getInstance().ce_fence_promise()) {
- refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
- for (auto& [layer, _] : mLayersWithQueuedFrames) {
- if (const auto& layerFE = layer->getCompositionEngineLayerFE(
- {static_cast<uint32_t>(layer->sequence)}))
- refreshArgs.layersWithQueuedFrames.push_back(layerFE);
- }
- }
-
refreshArgs.outputColorSetting = mDisplayColorSetting;
refreshArgs.forceOutputColorMode = mForceColorMode;
@@ -2815,51 +2822,35 @@
layer->onPreComposition(refreshArgs.refreshStartTime);
}
- if (FlagManager::getInstance().ce_fence_promise()) {
- for (auto& [layer, layerFE] : layers) {
- attachReleaseFenceFutureToLayer(layer, layerFE,
- layerFE->mSnapshot->outputFilter.layerStack);
- }
+ for (auto& [layer, layerFE] : layers) {
+ attachReleaseFenceFutureToLayer(layer, layerFE,
+ layerFE->mSnapshot->outputFilter.layerStack);
+ }
- refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
- for (auto& [layer, _] : mLayersWithQueuedFrames) {
- if (const auto& layerFE = layer->getCompositionEngineLayerFE(
- {static_cast<uint32_t>(layer->sequence)})) {
- refreshArgs.layersWithQueuedFrames.push_back(layerFE);
- // Some layers are not displayed and do not yet have a future release fence
- if (layerFE->getReleaseFencePromiseStatus() ==
- LayerFE::ReleaseFencePromiseStatus::UNINITIALIZED ||
- layerFE->getReleaseFencePromiseStatus() ==
- LayerFE::ReleaseFencePromiseStatus::FULFILLED) {
- // layerStack is invalid because layer is not on a display
- attachReleaseFenceFutureToLayer(layer.get(), layerFE.get(),
- ui::INVALID_LAYER_STACK);
- }
+ refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
+ for (auto& [layer, _] : mLayersWithQueuedFrames) {
+ if (const auto& layerFE =
+ layer->getCompositionEngineLayerFE({static_cast<uint32_t>(layer->sequence)})) {
+ refreshArgs.layersWithQueuedFrames.push_back(layerFE);
+ // Some layers are not displayed and do not yet have a future release fence
+ if (layerFE->getReleaseFencePromiseStatus() ==
+ LayerFE::ReleaseFencePromiseStatus::UNINITIALIZED ||
+ layerFE->getReleaseFencePromiseStatus() ==
+ LayerFE::ReleaseFencePromiseStatus::FULFILLED) {
+ // layerStack is invalid because layer is not on a display
+ attachReleaseFenceFutureToLayer(layer.get(), layerFE.get(),
+ ui::INVALID_LAYER_STACK);
}
}
+ }
- mCompositionEngine->present(refreshArgs);
- moveSnapshotsFromCompositionArgs(refreshArgs, layers);
+ mCompositionEngine->present(refreshArgs);
+ moveSnapshotsFromCompositionArgs(refreshArgs, layers);
- for (auto& [layer, layerFE] : layers) {
- CompositionResult compositionResult{layerFE->stealCompositionResult()};
- if (compositionResult.lastClientCompositionFence) {
- layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
- }
- }
-
- } else {
- mCompositionEngine->present(refreshArgs);
- moveSnapshotsFromCompositionArgs(refreshArgs, layers);
-
- for (auto [layer, layerFE] : layers) {
- CompositionResult compositionResult{layerFE->stealCompositionResult()};
- for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) {
- layer->onLayerDisplayed(std::move(releaseFence), layerStack);
- }
- if (compositionResult.lastClientCompositionFence) {
- layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
- }
+ for (auto& [layer, layerFE] : layers) {
+ CompositionResult compositionResult{layerFE->stealCompositionResult()};
+ if (compositionResult.lastClientCompositionFence) {
+ layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
}
}
@@ -3114,13 +3105,8 @@
auto optDisplay = layerStackToDisplay.get(layerStack);
if (optDisplay && !optDisplay->get()->isVirtual()) {
auto fence = getHwComposer().getPresentFence(optDisplay->get()->getPhysicalId());
- if (FlagManager::getInstance().ce_fence_promise()) {
- layer->prepareReleaseCallbacks(ftl::yield<FenceResult>(fence),
- ui::INVALID_LAYER_STACK);
- } else {
- layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
- ui::INVALID_LAYER_STACK);
- }
+ layer->prepareReleaseCallbacks(ftl::yield<FenceResult>(fence),
+ ui::INVALID_LAYER_STACK);
}
}
layer->releasePendingBuffer(presentTime.ns());
@@ -3282,8 +3268,6 @@
// getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
SFTRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
}
-
- logFrameStats(presentTime);
}
void SurfaceFlinger::commitTransactions() {
@@ -5195,6 +5179,7 @@
std::string counterName = layer->getPendingBufferCounterName();
mBufferCountTracker.add(LayerHandle::getLayerId(outResult.handle), counterName,
pendingBufferCounter);
+ args.pendingBuffers = pendingBufferCounter;
}
} break;
default:
@@ -5755,7 +5740,7 @@
void SurfaceFlinger::dumpFrontEnd(std::string& result) {
std::ostringstream out;
- out << "\nComposition list\n";
+ out << "\nComposition list (bottom to top)\n";
ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
@@ -5783,7 +5768,7 @@
void SurfaceFlinger::dumpVisibleFrontEnd(std::string& result) {
std::ostringstream out;
- out << "\nComposition list\n";
+ out << "\nComposition list (bottom to top)\n";
ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
mLayerSnapshotBuilder.forEachVisibleSnapshot(
[&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
@@ -7217,8 +7202,7 @@
return;
}
- if (FlagManager::getInstance().single_hop_screenshot() &&
- FlagManager::getInstance().ce_fence_promise() && mRenderEngine->isThreaded()) {
+ if (FlagManager::getInstance().single_hop_screenshot() && mRenderEngine->isThreaded()) {
std::vector<sp<LayerFE>> layerFEs;
auto displayState =
getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layerFEs);
@@ -7459,10 +7443,8 @@
auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
auto layers = getLayerSnapshotsFn();
- if (FlagManager::getInstance().ce_fence_promise()) {
- for (auto& [layer, layerFE] : layers) {
- attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
- }
+ for (auto& [layer, layerFE] : layers) {
+ attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
}
auto displayState = getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
@@ -7659,36 +7641,13 @@
// TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call
// to CompositionEngine::present.
ftl::SharedFuture<FenceResult> presentFuture;
- if (FlagManager::getInstance().single_hop_screenshot() &&
- FlagManager::getInstance().ce_fence_promise() && mRenderEngine->isThreaded()) {
+ if (FlagManager::getInstance().single_hop_screenshot() && mRenderEngine->isThreaded()) {
presentFuture = ftl::yield(present()).share();
} else {
presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share()
: ftl::yield(present()).share();
}
- if (!FlagManager::getInstance().ce_fence_promise()) {
- for (auto& [layer, layerFE] : layers) {
- layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK,
- [layerFE = std::move(layerFE)](FenceResult) {
- if (FlagManager::getInstance()
- .screenshot_fence_preservation()) {
- const auto compositionResult =
- layerFE->stealCompositionResult();
- const auto& fences = compositionResult.releaseFences;
- // CompositionEngine may choose to cull layers that
- // aren't visible, so pass a non-fence.
- return fences.empty() ? Fence::NO_FENCE
- : fences.back().first.get();
- } else {
- return layerFE->stealCompositionResult()
- .releaseFences.back()
- .first.get();
- }
- });
- }
- }
-
return presentFuture;
}
@@ -8638,6 +8597,7 @@
outInfo->activeDisplayModeId = info.activeDisplayModeId;
outInfo->renderFrameRate = info.renderFrameRate;
+ outInfo->hasArrSupport = info.hasArrSupport;
outInfo->supportedColorModes.clear();
outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index b189598..f39a4d2 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -147,7 +147,7 @@
proto.set_transform_to_display_inverse(layer.transformToDisplayInverse);
}
if (layer.what & layer_state_t::eCropChanged) {
- LayerProtoHelper::writeToProto(layer.crop, proto.mutable_crop());
+ LayerProtoHelper::writeToProto(Rect(layer.crop), proto.mutable_crop());
}
if (layer.what & layer_state_t::eBufferChanged) {
perfetto::protos::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index c6856ae..de4825b 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -28,7 +28,6 @@
#include "Utils/FenceUtils.h"
#include <binder/IInterface.h>
-#include <common/FlagManager.h>
#include <common/trace.h>
#include <utils/RefBase.h>
@@ -127,14 +126,8 @@
if (surfaceControl) {
sp<Fence> prevFence = nullptr;
- if (FlagManager::getInstance().ce_fence_promise()) {
- for (auto& future : handle->previousReleaseFences) {
- mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
- }
- } else {
- for (const auto& future : handle->previousSharedReleaseFences) {
- mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
- }
+ for (auto& future : handle->previousReleaseFences) {
+ mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
}
handle->previousReleaseFence = prevFence;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 14a7487..d81d8d0 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -43,7 +43,6 @@
std::string name;
sp<Fence> previousReleaseFence;
std::vector<ftl::Future<FenceResult>> previousReleaseFences;
- std::vector<ftl::SharedFuture<FenceResult>> previousSharedReleaseFences;
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
nsecs_t latchTime = -1;
std::optional<uint32_t> transformHint = std::nullopt;
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 12d6138..a331491 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -132,7 +132,6 @@
DUMP_READ_ONLY_FLAG(fp16_client_target);
DUMP_READ_ONLY_FLAG(game_default_frame_rate);
DUMP_READ_ONLY_FLAG(enable_layer_command_batching);
- DUMP_READ_ONLY_FLAG(screenshot_fence_preservation);
DUMP_READ_ONLY_FLAG(vulkan_renderengine);
DUMP_READ_ONLY_FLAG(renderable_buffer_usage);
DUMP_READ_ONLY_FLAG(vrr_bugfix_24q4);
@@ -140,7 +139,6 @@
DUMP_READ_ONLY_FLAG(restore_blur_step);
DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro);
DUMP_READ_ONLY_FLAG(protected_if_client);
- DUMP_READ_ONLY_FLAG(ce_fence_promise);
DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout);
DUMP_READ_ONLY_FLAG(graphite_renderengine);
DUMP_READ_ONLY_FLAG(filter_frames_before_trace_starts);
@@ -239,7 +237,6 @@
FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "")
FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "debug.sf.enable_layer_command_batching")
-FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation")
FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "")
FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
@@ -247,7 +244,6 @@
FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "")
FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, "");
FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "")
-FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, "");
FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite")
FLAG_MANAGER_READ_ONLY_FLAG(filter_frames_before_trace_starts, "")
FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index a1be194..daaf338 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -70,7 +70,6 @@
bool fp16_client_target() const;
bool game_default_frame_rate() const;
bool enable_layer_command_batching() const;
- bool screenshot_fence_preservation() const;
bool vulkan_renderengine() const;
bool vrr_bugfix_24q4() const;
bool vrr_bugfix_dropped_frame() const;
@@ -78,7 +77,6 @@
bool restore_blur_step() const;
bool dont_skip_on_early_ro() const;
bool protected_if_client() const;
- bool ce_fence_promise() const;
bool idle_screen_refresh_rate_timeout() const;
bool graphite_renderengine() const;
bool filter_frames_before_trace_starts() const;
diff --git a/services/surfaceflinger/EventLog/EventLogTags.logtags b/services/surfaceflinger/surfaceflinger.logtags
similarity index 93%
rename from services/surfaceflinger/EventLog/EventLogTags.logtags
rename to services/surfaceflinger/surfaceflinger.logtags
index 6c851dd..e68d9f5 100644
--- a/services/surfaceflinger/EventLog/EventLogTags.logtags
+++ b/services/surfaceflinger/surfaceflinger.logtags
@@ -35,7 +35,6 @@
# 60100 - 60199 reserved for surfaceflinger
-60100 sf_frame_dur (window|3),(dur0|1),(dur1|1),(dur2|1),(dur3|1),(dur4|1),(dur5|1),(dur6|1)
60110 sf_stop_bootanim (time|2|3)
# NOTE - the range 1000000-2000000 is reserved for partners and others who
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 102e2b6..e40be51 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -11,6 +11,14 @@
} # adpf_gpu_sf
flag {
+ name: "arr_setframerate_api"
+ namespace: "core_graphics"
+ description: "New setFrameRate API for Android 16"
+ bug: "356987016"
+ is_fixed_read_only: true
+} # arr_setframerate_api
+
+flag {
name: "ce_fence_promise"
namespace: "window_surfaces"
description: "Moves logic for buffer release fences into LayerFE"
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 67a5247..bf5957a 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -17,7 +17,6 @@
#define ANDROID_TRANSACTION_TEST_HARNESSES
#include <com_android_graphics_libgui_flags.h>
-#include <common/FlagManager.h>
#include <ui/DisplayState.h>
#include "LayerTransactionTest.h"
@@ -96,12 +95,8 @@
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
t.setDisplayProjection(vDisplay, displayState.orientation,
Rect(displayState.layerStackSpaceRect), Rect(resolution));
- if (FlagManager::getInstance().ce_fence_promise()) {
- t.setDisplayLayerStack(vDisplay, layerStack);
- t.setLayerStack(mirrorSc, layerStack);
- } else {
- t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK);
- }
+ t.setDisplayLayerStack(vDisplay, layerStack);
+ t.setLayerStack(mirrorSc, layerStack);
t.apply();
SurfaceComposerClient::Transaction().apply(true);
@@ -121,10 +116,8 @@
// CompositionEngine::present may attempt to be called on the same
// display multiple times. The layerStack is set to invalid here so
// that the display is ignored if that scenario occurs.
- if (FlagManager::getInstance().ce_fence_promise()) {
- t.setLayerStack(mirrorSc, ui::INVALID_LAYER_STACK);
- t.apply(true);
- }
+ t.setLayerStack(mirrorSc, ui::INVALID_LAYER_STACK);
+ t.apply(true);
SurfaceComposerClient::destroyVirtualDisplay(vDisplay);
return sc;
}
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index ae380ad..b472047 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -182,7 +182,7 @@
mLifecycleManager.applyTransactions(setZTransaction(id, z));
}
- void setCrop(uint32_t id, const Rect& crop) {
+ void setCrop(uint32_t id, const FloatRect& crop) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -193,6 +193,8 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setCrop(uint32_t id, const Rect& crop) { setCrop(id, crop.toFloatRect()); }
+
void setFlags(uint32_t id, uint32_t mask, uint32_t flags) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 23d3c16..4f72424 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -467,7 +467,7 @@
LayerProperties::FORMAT,
LayerProperties::USAGE |
GraphicBuffer::USAGE_HW_TEXTURE);
- layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+ layer.crop = FloatRect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
layer.externalTexture = buffer;
layer.bufferData->acquireFence = Fence::NO_FENCE;
layer.dataspace = ui::Dataspace::UNKNOWN;
@@ -664,7 +664,8 @@
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
layer.sidebandStream = stream;
- layer.crop = Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
+ layer.crop =
+ FloatRect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
}
static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
@@ -828,7 +829,7 @@
return frontend::RequestedLayerState(args);
});
- layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+ layer.crop = FloatRect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
return layer;
}
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 75d2fa3..a35ae15 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -162,12 +162,12 @@
info.info.logicalHeight = 100;
info.info.logicalWidth = 200;
mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
- Rect layerCrop(0, 0, 10, 20);
+ FloatRect layerCrop(0, 0, 10, 20);
setCrop(11, layerCrop);
EXPECT_TRUE(mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Geometry));
UPDATE_AND_VERIFY_WITH_DISPLAY_CHANGES(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(11)->geomCrop, layerCrop);
- EXPECT_EQ(getSnapshot(111)->geomLayerBounds, layerCrop.toFloatRect());
+ EXPECT_EQ(getSnapshot(111)->geomLayerBounds, layerCrop);
float maxHeight = static_cast<float>(info.info.logicalHeight * 10);
float maxWidth = static_cast<float>(info.info.logicalWidth * 10);
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 9efe73d..adbd868 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -100,7 +100,9 @@
const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
using RefreshRateSelector::GetRankedFrameRatesCache;
- auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
+ auto& mutableGetRankedRefreshRatesCache() NO_THREAD_SAFETY_ANALYSIS {
+ return mGetRankedFrameRatesCache;
+ }
auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
GlobalSignals signals = {}, Fps pacesetterFps = {}) const {
@@ -138,7 +140,9 @@
return setPolicy(policy);
}
- const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; }
+ const auto& getPrimaryFrameRates() const NO_THREAD_SAFETY_ANALYSIS {
+ return mPrimaryFrameRates;
+ }
};
class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> {
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index fab1f6d..1e8cd0a 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -387,7 +387,7 @@
state.state.what = what;
if (what & layer_state_t::eCropChanged) {
- state.state.crop = Rect(1, 2, 3, 4);
+ state.state.crop = FloatRect(1, 2, 3, 4);
}
if (what & layer_state_t::eFlagsChanged) {
state.state.flags = layer_state_t::eEnableBackpressure;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index f472d8f..615cc94 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -182,7 +182,7 @@
MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t));
MOCK_METHOD(
Error, getRequestedLuts,
- (Display,
+ (Display, std::vector<Layer>*,
std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*));
MOCK_METHOD(Error, setLayerLuts,
(Display, Layer, std::vector<aidl::android::hardware::graphics::composer3::Lut>&));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 5edd2cd..53ed2e1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -111,7 +111,7 @@
(aidl::android::hardware::graphics::composer3::OverlayProperties *),
(const override));
MOCK_METHOD(hal::Error, getRequestedLuts,
- (std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*),
+ ((HWC2::Display::LayerLuts*), (HWC2::Display::LutFileDescriptorMapper&)),
(override));
};
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 4ac1618..b06ee3b 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -107,6 +107,10 @@
mInfoCache.mMaxEnvelopeEffectControlPointDuration =
getMaxEnvelopeEffectControlPointDurationInternal();
}
+ if (mInfoCache.mFrequencyToOutputAccelerationMap.isFailed()) {
+ mInfoCache.mFrequencyToOutputAccelerationMap =
+ getFrequencyToOutputAccelerationMapInternal();
+ }
return mInfoCache.get();
}
@@ -239,6 +243,13 @@
return HalResult<milliseconds>::unsupported();
}
+HalResult<std::vector<PwleV2OutputMapEntry>>
+HalWrapper::getFrequencyToOutputAccelerationMapInternal() {
+ ALOGV("Skipped getFrequencyToOutputAccelerationMapInternal because it's not "
+ "available in Vibrator HAL");
+ return HalResult<std::vector<PwleV2OutputMapEntry>>::unsupported();
+}
+
// -------------------------------------------------------------------------------------------------
HalResult<void> AidlHalWrapper::ping() {
@@ -487,6 +498,15 @@
return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs));
}
+HalResult<std::vector<PwleV2OutputMapEntry>>
+AidlHalWrapper::getFrequencyToOutputAccelerationMapInternal() {
+ std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
+ auto status =
+ getHal()->getPwleV2FrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
+ return HalResultFactory::fromStatus<
+ std::vector<PwleV2OutputMapEntry>>(std::move(status), frequencyToOutputAccelerationMap);
+}
+
std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 4938b15..b2bfffc 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -243,6 +243,7 @@
using EffectStrength = aidl::android::hardware::vibrator::EffectStrength;
using CompositePrimitive = aidl::android::hardware::vibrator::CompositePrimitive;
using Braking = aidl::android::hardware::vibrator::Braking;
+ using PwleV2OutputMapEntry = aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
const HalResult<Capabilities> capabilities;
const HalResult<std::vector<Effect>> supportedEffects;
@@ -261,6 +262,7 @@
const HalResult<int32_t> maxEnvelopeEffectSize;
const HalResult<std::chrono::milliseconds> minEnvelopeEffectControlPointDuration;
const HalResult<std::chrono::milliseconds> maxEnvelopeEffectControlPointDuration;
+ const HalResult<std::vector<PwleV2OutputMapEntry>> frequencyToOutputAccelerationMap;
void logFailures() const {
logFailure<Capabilities>(capabilities, "getCapabilities");
@@ -284,6 +286,8 @@
"getMinEnvelopeEffectControlPointDuration");
logFailure<std::chrono::milliseconds>(maxEnvelopeEffectControlPointDuration,
"getMaxEnvelopeEffectControlPointDuration");
+ logFailure<std::vector<PwleV2OutputMapEntry>>(frequencyToOutputAccelerationMap,
+ "getfrequencyToOutputAccelerationMap");
}
bool shouldRetry() const {
@@ -296,7 +300,8 @@
qFactor.shouldRetry() || maxAmplitudes.shouldRetry() ||
maxEnvelopeEffectSize.shouldRetry() ||
minEnvelopeEffectControlPointDuration.shouldRetry() ||
- maxEnvelopeEffectControlPointDuration.shouldRetry();
+ maxEnvelopeEffectControlPointDuration.shouldRetry() ||
+ frequencyToOutputAccelerationMap.shouldRetry();
}
private:
@@ -327,7 +332,8 @@
mMaxAmplitudes,
mMaxEnvelopeEffectSize,
mMinEnvelopeEffectControlPointDuration,
- mMaxEnvelopeEffectControlPointDuration};
+ mMaxEnvelopeEffectControlPointDuration,
+ mFrequencyToOutputAccelerationMap};
}
private:
@@ -359,6 +365,8 @@
HalResult<std::chrono::milliseconds>::transactionFailed(MSG);
HalResult<std::chrono::milliseconds> mMaxEnvelopeEffectControlPointDuration =
HalResult<std::chrono::milliseconds>::transactionFailed(MSG);
+ HalResult<std::vector<Info::PwleV2OutputMapEntry>> mFrequencyToOutputAccelerationMap =
+ HalResult<std::vector<Info::PwleV2OutputMapEntry>>::transactionFailed(MSG);
friend class HalWrapper;
};
@@ -442,6 +450,8 @@
virtual HalResult<int32_t> getMaxEnvelopeEffectSizeInternal();
virtual HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal();
virtual HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal();
+ virtual HalResult<std::vector<PwleV2OutputMapEntry>>
+ getFrequencyToOutputAccelerationMapInternal();
private:
std::mutex mInfoMutex;
@@ -518,12 +528,12 @@
HalResult<float> getQFactorInternal() override final;
HalResult<std::vector<float>> getMaxAmplitudesInternal() override final;
HalResult<int32_t> getMaxEnvelopeEffectSizeInternal() override final;
-
HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal()
override final;
-
HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal()
override final;
+ HalResult<std::vector<PwleV2OutputMapEntry>> getFrequencyToOutputAccelerationMapInternal()
+ override final;
private:
const reconnect_fn mReconnectFn;
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 17f384d..d42aa56 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -39,6 +39,7 @@
using aidl::android::hardware::vibrator::IVibrator;
using aidl::android::hardware::vibrator::IVibratorCallback;
using aidl::android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
using aidl::android::hardware::vibrator::PwleV2Primitive;
using aidl::android::hardware::vibrator::VendorEffect;
using aidl::android::os::PersistableBundle;
@@ -242,6 +243,11 @@
std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK};
std::vector<Braking> supportedBraking = {Braking::CLAB};
std::vector<float> amplitudes = {0.f, 1.f, 0.f};
+ std::vector<PwleV2OutputMapEntry>
+ frequencyToOutputAccelerationMap{PwleV2OutputMapEntry(/*frequency=*/30.0f,
+ /*maxOutputAcceleration=*/0.2),
+ PwleV2OutputMapEntry(/*frequency=*/60.0f,
+ /*maxOutputAcceleration=*/0.8)};
std::vector<std::chrono::milliseconds> primitiveDurations;
constexpr auto primitiveRange = ndk::enum_range<CompositePrimitive>();
@@ -323,6 +329,11 @@
.WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
.WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2FrequencyToOutputAccelerationMap(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(frequencyToOutputAccelerationMap),
+ Return(ndk::ScopedAStatus::ok())));
vibrator::Info failed = mWrapper->getInfo();
ASSERT_TRUE(failed.capabilities.isFailed());
@@ -342,6 +353,7 @@
ASSERT_TRUE(failed.maxEnvelopeEffectSize.isFailed());
ASSERT_TRUE(failed.minEnvelopeEffectControlPointDuration.isFailed());
ASSERT_TRUE(failed.maxEnvelopeEffectControlPointDuration.isFailed());
+ ASSERT_TRUE(failed.frequencyToOutputAccelerationMap.isFailed());
vibrator::Info successful = mWrapper->getInfo();
ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, successful.capabilities.value());
@@ -364,6 +376,8 @@
successful.minEnvelopeEffectControlPointDuration.value());
ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
successful.maxEnvelopeEffectControlPointDuration.value());
+ ASSERT_EQ(frequencyToOutputAccelerationMap,
+ successful.frequencyToOutputAccelerationMap.value());
}
TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) {
@@ -377,6 +391,11 @@
constexpr int32_t PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20;
constexpr int32_t PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000;
std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
+ std::vector<PwleV2OutputMapEntry>
+ frequencyToOutputAccelerationMap{PwleV2OutputMapEntry(/*frequency=*/30.0f,
+ /*maxOutputAcceleration=*/0.2),
+ PwleV2OutputMapEntry(/*frequency=*/60.0f,
+ /*maxOutputAcceleration=*/0.8)};
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(1))
@@ -432,6 +451,10 @@
.Times(Exactly(1))
.WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2FrequencyToOutputAccelerationMap(_))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<0>(frequencyToOutputAccelerationMap),
+ Return(ndk::ScopedAStatus::ok())));
std::vector<std::thread> threads;
for (int i = 0; i < 10; i++) {
@@ -460,6 +483,7 @@
info.minEnvelopeEffectControlPointDuration.value());
ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
info.maxEnvelopeEffectControlPointDuration.value());
+ ASSERT_EQ(frequencyToOutputAccelerationMap, info.frequencyToOutputAccelerationMap.value());
}
TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index a09ddec..d6dab8d 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -223,6 +223,7 @@
ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
+ ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
}
TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
@@ -259,6 +260,7 @@
ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
+ ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
}
TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 0284192..bfb7bd6 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -1169,7 +1169,7 @@
return array;
}
-template <typename T, unsigned int N>
+template <typename T, size_t N>
inline Json::Value ToJsonValue(const T (&value)[N]) {
return ArrayToJsonValue(N, value);
}
@@ -1293,7 +1293,7 @@
return true;
}
-template <typename T, unsigned int N>
+template <typename T, size_t N>
inline bool AsValue(Json::Value* json_value, T (*value)[N]) {
return AsArray(json_value, N, *value);
}