Merge "Revert "Add API support for GPU work duration report in ADPF."" into main
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index d7c9b40..e14af77 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -471,6 +471,49 @@
return NO_ERROR;
}
+constexpr const char kXattrRestoreconInProgress[] = "user.restorecon_in_progress";
+
+static std::string lgetfilecon(const std::string& path) {
+ char* context;
+ if (::lgetfilecon(path.c_str(), &context) < 0) {
+ PLOG(ERROR) << "Failed to lgetfilecon for " << path;
+ return {};
+ }
+ std::string result{context};
+ free(context);
+ return result;
+}
+
+static bool getRestoreconInProgress(const std::string& path) {
+ bool inProgress = false;
+ if (getxattr(path.c_str(), kXattrRestoreconInProgress, &inProgress, sizeof(inProgress)) !=
+ sizeof(inProgress)) {
+ if (errno != ENODATA) {
+ PLOG(ERROR) << "Failed to check in-progress restorecon for " << path;
+ }
+ return false;
+ }
+ return inProgress;
+}
+
+struct RestoreconInProgress {
+ explicit RestoreconInProgress(const std::string& path) : mPath(path) {
+ bool inProgress = true;
+ if (setxattr(mPath.c_str(), kXattrRestoreconInProgress, &inProgress, sizeof(inProgress),
+ 0) != 0) {
+ PLOG(ERROR) << "Failed to set in-progress restorecon for " << path;
+ }
+ }
+ ~RestoreconInProgress() {
+ if (removexattr(mPath.c_str(), kXattrRestoreconInProgress) < 0) {
+ PLOG(ERROR) << "Failed to clear in-progress restorecon for " << mPath;
+ }
+ }
+
+private:
+ const std::string& mPath;
+};
+
/**
* Perform restorecon of the given path, but only perform recursive restorecon
* if the label of that top-level file actually changed. This can save us
@@ -479,56 +522,56 @@
static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid,
bool existing) {
ScopedTrace tracer("restorecon-lazy");
- int res = 0;
- char* before = nullptr;
- char* after = nullptr;
if (!existing) {
ScopedTrace tracer("new-path");
if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
PLOG(ERROR) << "Failed recursive restorecon for " << path;
- goto fail;
+ return -1;
}
- return res;
+ return 0;
}
- // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
- // libselinux. Not needed here.
- if (lgetfilecon(path.c_str(), &before) < 0) {
- PLOG(ERROR) << "Failed before getfilecon for " << path;
- goto fail;
- }
- if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) {
- PLOG(ERROR) << "Failed top-level restorecon for " << path;
- goto fail;
- }
- if (lgetfilecon(path.c_str(), &after) < 0) {
- PLOG(ERROR) << "Failed after getfilecon for " << path;
- goto fail;
+ // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
+
+ // Check to see if there was an interrupted operation.
+ bool inProgress = getRestoreconInProgress(path);
+ std::string before, after;
+ if (!inProgress) {
+ if (before = lgetfilecon(path); before.empty()) {
+ PLOG(ERROR) << "Failed before getfilecon for " << path;
+ return -1;
+ }
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) {
+ PLOG(ERROR) << "Failed top-level restorecon for " << path;
+ return -1;
+ }
+ if (after = lgetfilecon(path); after.empty()) {
+ PLOG(ERROR) << "Failed after getfilecon for " << path;
+ return -1;
+ }
}
// If the initial top-level restorecon above changed the label, then go
// back and restorecon everything recursively
- if (strcmp(before, after)) {
+ if (inProgress || before != after) {
ScopedTrace tracer("label-change");
if (existing) {
LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at "
<< path << "; running recursive restorecon";
}
+
+ // Temporary mark the folder as "in-progress" to resume in case of reboot/other failure.
+ RestoreconInProgress fence(path);
+
if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
PLOG(ERROR) << "Failed recursive restorecon for " << path;
- goto fail;
+ return -1;
}
}
- goto done;
-fail:
- res = -1;
-done:
- free(before);
- free(after);
- return res;
+ return 0;
}
static bool internal_storage_has_project_id() {
// The following path is populated in setFirstBoot, so if this file is present
@@ -3283,7 +3326,7 @@
}
char *con = nullptr;
- if (lgetfilecon(pkgdir, &con) < 0) {
+ if (::lgetfilecon(pkgdir, &con) < 0) {
return error("Failed to lgetfilecon " + _pkgdir);
}
diff --git a/cmds/sfdo/sfdo.cpp b/cmds/sfdo/sfdo.cpp
index 55326ea..de0e171 100644
--- a/cmds/sfdo/sfdo.cpp
+++ b/cmds/sfdo/sfdo.cpp
@@ -16,7 +16,7 @@
#include <inttypes.h>
#include <stdint.h>
#include <any>
-#include <unordered_map>
+#include <map>
#include <cutils/properties.h>
#include <sys/resource.h>
@@ -29,18 +29,28 @@
using namespace android;
-std::unordered_map<std::string, std::any> g_functions;
+std::map<std::string, std::any> g_functions;
-const std::unordered_map<std::string, std::string> g_function_details = {
- {"DebugFlash", "[optional(delay)] Perform a debug flash."},
- {"FrameRateIndicator", "[hide | show] displays the framerate in the top left corner."},
- {"scheduleComposite", "Force composite ahead of next VSYNC."},
- {"scheduleCommit", "Force commit ahead of next VSYNC."},
- {"scheduleComposite", "PENDING - if you have a good understanding let me know!"},
+enum class ParseToggleResult {
+ kError,
+ kFalse,
+ kTrue,
+};
+
+const std::map<std::string, std::string> g_function_details = {
+ {"debugFlash", "[optional(delay)] Perform a debug flash."},
+ {"frameRateIndicator", "[hide | show] displays the framerate in the top left corner."},
+ {"scheduleComposite", "Force composite ahead of next VSYNC."},
+ {"scheduleCommit", "Force commit ahead of next VSYNC."},
+ {"scheduleComposite", "PENDING - if you have a good understanding let me know!"},
+ {"forceClientComposition",
+ "[enabled | disabled] When enabled, it disables "
+ "Hardware Overlays, and routes all window composition to the GPU. This can "
+ "help check if there is a bug in HW Composer."},
};
static void ShowUsage() {
- std::cout << "usage: sfdo [help, FrameRateIndicator show, DebugFlash enabled, ...]\n\n";
+ std::cout << "usage: sfdo [help, frameRateIndicator show, debugFlash enabled, ...]\n\n";
for (const auto& sf : g_functions) {
const std::string fn = sf.first;
std::string fdetails = "TODO";
@@ -50,7 +60,26 @@
}
}
-int FrameRateIndicator([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+// Returns 1 for positive keywords and 0 for negative keywords.
+// If the string does not match any it will return -1.
+ParseToggleResult parseToggle(const char* str) {
+ const std::unordered_set<std::string> positive{"1", "true", "y", "yes",
+ "on", "enabled", "show"};
+ const std::unordered_set<std::string> negative{"0", "false", "n", "no",
+ "off", "disabled", "hide"};
+
+ const std::string word(str);
+ if (positive.count(word)) {
+ return ParseToggleResult::kTrue;
+ }
+ if (negative.count(word)) {
+ return ParseToggleResult::kFalse;
+ }
+
+ return ParseToggleResult::kError;
+}
+
+int frameRateIndicator(int argc, char** argv) {
bool hide = false, show = false;
if (argc == 3) {
show = strcmp(argv[2], "show") == 0;
@@ -60,13 +89,13 @@
if (show || hide) {
ComposerServiceAIDL::getComposerService()->enableRefreshRateOverlay(show);
} else {
- std::cerr << "Incorrect usage of FrameRateIndicator. Missing [hide | show].\n";
+ std::cerr << "Incorrect usage of frameRateIndicator. Missing [hide | show].\n";
return -1;
}
return 0;
}
-int DebugFlash([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+int debugFlash(int argc, char** argv) {
int delay = 0;
if (argc == 3) {
delay = atoi(argv[2]) == 0;
@@ -86,14 +115,40 @@
return 0;
}
+int forceClientComposition(int argc, char** argv) {
+ bool enabled = true;
+ // A valid command looks like this:
+ // adb shell sfdo forceClientComposition enabled
+ if (argc >= 3) {
+ const ParseToggleResult toggle = parseToggle(argv[2]);
+ if (toggle == ParseToggleResult::kError) {
+ std::cerr << "Incorrect usage of forceClientComposition. "
+ "Missing [enabled | disabled].\n";
+ return -1;
+ }
+ if (argc > 3) {
+ std::cerr << "Too many arguments after [enabled | disabled]. "
+ "Ignoring extra arguments.\n";
+ }
+ enabled = (toggle == ParseToggleResult::kTrue);
+ } else {
+ std::cerr << "Incorrect usage of forceClientComposition. Missing [enabled | disabled].\n";
+ return -1;
+ }
+
+ ComposerServiceAIDL::getComposerService()->forceClientComposition(enabled);
+ return 0;
+}
+
int main(int argc, char** argv) {
std::cout << "Execute SurfaceFlinger internal commands.\n";
std::cout << "sfdo requires to be run with root permissions..\n";
- g_functions["FrameRateIndicator"] = FrameRateIndicator;
- g_functions["DebugFlash"] = DebugFlash;
+ g_functions["frameRateIndicator"] = frameRateIndicator;
+ g_functions["debugFlash"] = debugFlash;
g_functions["scheduleComposite"] = scheduleComposite;
g_functions["scheduleCommit"] = scheduleCommit;
+ g_functions["forceClientComposition"] = forceClientComposition;
if (argc > 1 && g_functions.find(argv[1]) != g_functions.end()) {
std::cout << "Running: " << argv[1] << "\n";
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 1f477f8..0b57e93 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -111,7 +111,7 @@
* It's passed the updated thermal status as parameter, as well as the
* pointer provided by the client that registered a callback.
*/
-typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
+typedef void (*AThermal_StatusCallback)(void* data, AThermalStatus status);
/**
* Acquire an instance of the thermal manager. This must be freed using
@@ -222,6 +222,70 @@
float AThermal_getThermalHeadroom(AThermalManager *manager,
int forecastSeconds) __INTRODUCED_IN(31);
+/**
+ * This struct defines an instance of headroom threshold value and its status.
+ * <p>
+ * The value should be monotonically non-decreasing as the thermal status increases.
+ * For {@link ATHERMAL_STATUS_SEVERE}, its headroom threshold is guaranteed to
+ * be 1.0f. For status below severe status, the value should be lower or equal
+ * to 1.0f, and for status above severe, the value should be larger or equal to 1.0f.
+ * <p>
+ * Also see {@link AThermal_getThermalHeadroom} for explanation on headroom, and
+ * {@link AThermal_getThermalHeadroomThresholds} for how to use this.
+ */
+struct AThermalHeadroomThreshold {
+ float headroom;
+ AThermalStatus thermalStatus;
+};
+
+/**
+ * Gets the thermal headroom thresholds for all available thermal status.
+ *
+ * A thermal status will only exist in output if the device manufacturer has the
+ * corresponding threshold defined for at least one of its slow-moving skin temperature
+ * sensors. If it's set, one should also expect to get it from
+ * {@link #AThermal_getCurrentThermalStatus} or {@link AThermal_StatusCallback}.
+ * <p>
+ * The headroom threshold is used to interpret the possible thermal throttling status based on
+ * the headroom prediction. For example, if the headroom threshold for
+ * {@link ATHERMAL_STATUS_LIGHT} is 0.7, and a headroom prediction in 10s returns 0.75
+ * (or {@code AThermal_getThermalHeadroom(10)=0.75}), one can expect that in 10 seconds the system
+ * could be in lightly throttled state if the workload remains the same. The app can consider
+ * taking actions according to the nearest throttling status the difference between the headroom and
+ * the threshold.
+ * <p>
+ * For new devices it's guaranteed to have a single sensor, but for older devices with multiple
+ * sensors reporting different threshold values, the minimum threshold is taken to be conservative
+ * on predictions. Thus, when reading real-time headroom, it's not guaranteed that a real-time value
+ * of 0.75 (or {@code AThermal_getThermalHeadroom(0)}=0.75) exceeding the threshold of 0.7 above
+ * will always come with lightly throttled state
+ * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_LIGHT}) but it can be lower
+ * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_NONE}).
+ * While it's always guaranteed that the device won't be throttled heavier than the unmet
+ * threshold's state, so a real-time headroom of 0.75 will never come with
+ * {@link #ATHERMAL_STATUS_MODERATE} but always lower, and 0.65 will never come with
+ * {@link ATHERMAL_STATUS_LIGHT} but {@link #ATHERMAL_STATUS_NONE}.
+ * <p>
+ * The returned list of thresholds is cached on first successful query and owned by the thermal
+ * manager, which will not change between calls to this function. The caller should only need to
+ * free the manager with {@link AThermal_releaseManager}.
+ *
+ * @param manager The manager instance to use.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param outThresholds non-null output pointer to null AThermalHeadroomThreshold pointer, which
+ * will be set to the cached array of thresholds if thermal thresholds are supported
+ * by the system or device, otherwise nullptr or unmodified.
+ * @param size non-null output pointer whose value will be set to the size of the threshold array
+ * or 0 if it's not supported.
+ * @return 0 on success
+ * EINVAL if outThresholds or size_t is nullptr, or *outThresholds is not nullptr.
+ * EPIPE if communication with the system service has failed.
+ * ENOSYS if the feature is disabled by the current system.
+ */
+int AThermal_getThermalHeadroomThresholds(AThermalManager* manager,
+ const AThermalHeadroomThreshold ** outThresholds,
+ size_t* size) __INTRODUCED_IN(35);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/private/thermal_private.h b/include/private/thermal_private.h
new file mode 100644
index 0000000..951d953
--- /dev/null
+++ b/include/private/thermal_private.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_PRIVATE_NATIVE_THERMAL_H
+#define ANDROID_PRIVATE_NATIVE_THERMAL_H
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+/**
+ * For testing only.
+ */
+void AThermal_setIThermalServiceForTesting(void* iThermalService);
+
+__END_DECLS
+
+#endif // ANDROID_PRIVATE_NATIVE_THERMAL_H
\ No newline at end of file
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 620c23c..f90c618 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -251,13 +251,15 @@
srcs: [
// Trusty-specific files
"OS_android.cpp",
- "trusty/logging.cpp",
"trusty/OS.cpp",
"trusty/RpcServerTrusty.cpp",
"trusty/RpcTransportTipcTrusty.cpp",
"trusty/TrustyStatus.cpp",
"trusty/socket.cpp",
],
+ shared_libs: [
+ "liblog",
+ ],
}
cc_defaults {
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 8243238..301dbf6 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -19,7 +19,6 @@
#include <atomic>
#include <set>
-#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
@@ -308,7 +307,7 @@
Extras* e = getOrCreateExtras();
RpcMutexUniqueLock lock(e->mLock);
if (mRecordingOn) {
- LOG(INFO) << "Could not start Binder recording. Another is already in progress.";
+ ALOGI("Could not start Binder recording. Another is already in progress.");
return INVALID_OPERATION;
} else {
status_t readStatus = data.readUniqueFileDescriptor(&(e->mRecordingFd));
@@ -316,7 +315,7 @@
return readStatus;
}
mRecordingOn = true;
- LOG(INFO) << "Started Binder recording.";
+ ALOGI("Started Binder recording.");
return NO_ERROR;
}
}
@@ -340,10 +339,10 @@
if (mRecordingOn) {
e->mRecordingFd.reset();
mRecordingOn = false;
- LOG(INFO) << "Stopped Binder recording.";
+ ALOGI("Stopped Binder recording.");
return NO_ERROR;
} else {
- LOG(INFO) << "Could not stop Binder recording. One is not in progress.";
+ ALOGI("Could not stop Binder recording. One is not in progress.");
return INVALID_OPERATION;
}
}
@@ -377,11 +376,11 @@
err = stopRecordingTransactions();
break;
case EXTENSION_TRANSACTION:
- CHECK(reply != nullptr);
+ LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr");
err = reply->writeStrongBinder(getExtension());
break;
case DEBUG_PID_TRANSACTION:
- CHECK(reply != nullptr);
+ LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr");
err = reply->writeInt32(getDebugPid());
break;
case SET_RPC_CLIENT_TRANSACTION: {
@@ -414,10 +413,10 @@
reply ? *reply : emptyReply, err);
if (transaction) {
if (status_t err = transaction->dumpToFile(e->mRecordingFd); err != NO_ERROR) {
- LOG(INFO) << "Failed to dump RecordedTransaction to file with error " << err;
+ ALOGI("Failed to dump RecordedTransaction to file with error %d", err);
}
} else {
- LOG(INFO) << "Failed to create RecordedTransaction object.";
+ ALOGI("Failed to create RecordedTransaction object.");
}
}
}
@@ -705,7 +704,7 @@
return status;
}
rpcServer->setMaxThreads(binderThreadPoolMaxCount);
- LOG(INFO) << "RpcBinder: Started Binder debug on " << getInterfaceDescriptor();
+ ALOGI("RpcBinder: Started Binder debug on %s", String8(getInterfaceDescriptor()).c_str());
rpcServer->start();
e->mRpcServerLinks.emplace(link);
LOG_RPC_DETAIL("%s(fd=%d) successful", __PRETTY_FUNCTION__, socketFdForPrint);
@@ -723,20 +722,20 @@
{
if (!wasParceled()) {
if (getExtension()) {
- ALOGW("Binder %p destroyed with extension attached before being parceled.", this);
+ ALOGW("Binder %p destroyed with extension attached before being parceled.", this);
}
if (isRequestingSid()) {
- ALOGW("Binder %p destroyed when requesting SID before being parceled.", this);
+ ALOGW("Binder %p destroyed when requesting SID before being parceled.", this);
}
if (isInheritRt()) {
- ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this);
+ ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this);
}
#ifdef __linux__
if (getMinSchedulerPolicy() != SCHED_NORMAL) {
- ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
+ ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
}
if (getMinSchedulerPriority() != 0) {
- ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
+ ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
}
#endif // __linux__
}
@@ -752,7 +751,7 @@
{
switch (code) {
case INTERFACE_TRANSACTION:
- CHECK(reply != nullptr);
+ LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr");
reply->writeString16(getInterfaceDescriptor());
return NO_ERROR;
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index a1fbbf3..895690f 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -53,7 +53,7 @@
#ifdef BINDER_RPC_SINGLE_THREADED
return mTriggered;
#else
- return mWrite == -1;
+ return !mWrite.ok();
#endif
}
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index cedd3af..f7b5a55 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -15,10 +15,10 @@
*/
#include <android-base/file.h>
-#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <binder/Functional.h>
#include <binder/RecordedTransaction.h>
+#include <inttypes.h>
#include <sys/mman.h>
#include <algorithm>
@@ -127,18 +127,17 @@
t.mData.mInterfaceName = std::string(String8(interfaceName).c_str());
if (interfaceName.size() != t.mData.mInterfaceName.size()) {
- LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
- "utf-8.";
+ ALOGE("Interface Name is not valid. Contains characters that aren't single byte utf-8.");
return std::nullopt;
}
if (t.mSent.setData(dataParcel.data(), dataParcel.dataBufferSize()) != android::NO_ERROR) {
- LOG(ERROR) << "Failed to set sent parcel data.";
+ ALOGE("Failed to set sent parcel data.");
return std::nullopt;
}
if (t.mReply.setData(replyParcel.data(), replyParcel.dataBufferSize()) != android::NO_ERROR) {
- LOG(ERROR) << "Failed to set reply parcel data.";
+ ALOGE("Failed to set reply parcel data.");
return std::nullopt;
}
@@ -168,38 +167,37 @@
const long pageSize = sysconf(_SC_PAGE_SIZE);
struct stat fileStat;
if (fstat(fd.get(), &fileStat) != 0) {
- LOG(ERROR) << "Unable to get file information";
+ ALOGE("Unable to get file information");
return std::nullopt;
}
off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
if (fdCurrentPosition == -1) {
- LOG(ERROR) << "Invalid offset in file descriptor.";
+ ALOGE("Invalid offset in file descriptor.");
return std::nullopt;
}
do {
if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) {
- LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
+ ALOGE("Not enough file remains to contain expected chunk descriptor");
return std::nullopt;
}
if (!android::base::ReadFully(fd, &chunk, sizeof(ChunkDescriptor))) {
- LOG(ERROR) << "Failed to read ChunkDescriptor from fd " << fd.get() << ". "
- << strerror(errno);
+ ALOGE("Failed to read ChunkDescriptor from fd %d. %s", fd.get(), strerror(errno));
return std::nullopt;
}
transaction_checksum_t checksum = *reinterpret_cast<transaction_checksum_t*>(&chunk);
fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
if (fdCurrentPosition == -1) {
- LOG(ERROR) << "Invalid offset in file descriptor.";
+ ALOGE("Invalid offset in file descriptor.");
return std::nullopt;
}
off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize;
off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart;
if (chunk.dataSize > kMaxChunkDataSize) {
- LOG(ERROR) << "Chunk data exceeds maximum size.";
+ ALOGE("Chunk data exceeds maximum size.");
return std::nullopt;
}
@@ -207,12 +205,12 @@
chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t);
if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) {
- LOG(ERROR) << "Chunk payload exceeds remaining file size.";
+ ALOGE("Chunk payload exceeds remaining file size.");
return std::nullopt;
}
if (PADDING8(chunkPayloadSize) != 0) {
- LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize;
+ ALOGE("Invalid chunk size, not aligned %zu", chunkPayloadSize);
return std::nullopt;
}
@@ -228,8 +226,7 @@
sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap
// page-alignment
if (payloadMap == MAP_FAILED) {
- LOG(ERROR) << "Memory mapping failed for fd " << fd.get() << ": " << errno << " "
- << strerror(errno);
+ ALOGE("Memory mapping failed for fd %d: %d %s", fd.get(), errno, strerror(errno));
return std::nullopt;
}
for (size_t checksumIndex = 0;
@@ -237,21 +234,21 @@
checksum ^= payloadMap[checksumIndex];
}
if (checksum != 0) {
- LOG(ERROR) << "Checksum failed.";
+ ALOGE("Checksum failed.");
return std::nullopt;
}
fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
if (fdCurrentPosition == -1) {
- LOG(ERROR) << "Invalid offset in file descriptor.";
+ ALOGE("Invalid offset in file descriptor.");
return std::nullopt;
}
switch (chunk.chunkType) {
case HEADER_CHUNK: {
if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) {
- LOG(ERROR) << "Header Chunk indicated size " << chunk.dataSize << "; Expected "
- << sizeof(TransactionHeader) << ".";
+ ALOGE("Header Chunk indicated size %" PRIu32 "; Expected %zu.", chunk.dataSize,
+ sizeof(TransactionHeader));
return std::nullopt;
}
t.mData.mHeader = *reinterpret_cast<TransactionHeader*>(payloadMap);
@@ -265,7 +262,7 @@
case DATA_PARCEL_CHUNK: {
if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap),
chunk.dataSize) != android::NO_ERROR) {
- LOG(ERROR) << "Failed to set sent parcel data.";
+ ALOGE("Failed to set sent parcel data.");
return std::nullopt;
}
break;
@@ -273,7 +270,7 @@
case REPLY_PARCEL_CHUNK: {
if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap),
chunk.dataSize) != android::NO_ERROR) {
- LOG(ERROR) << "Failed to set reply parcel data.";
+ ALOGE("Failed to set reply parcel data.");
return std::nullopt;
}
break;
@@ -281,7 +278,7 @@
case END_CHUNK:
break;
default:
- LOG(INFO) << "Unrecognized chunk.";
+ ALOGI("Unrecognized chunk.");
break;
}
} while (chunk.chunkType != END_CHUNK);
@@ -292,7 +289,7 @@
android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType,
size_t byteCount, const uint8_t* data) const {
if (byteCount > kMaxChunkDataSize) {
- LOG(ERROR) << "Chunk data exceeds maximum size";
+ ALOGE("Chunk data exceeds maximum size");
return BAD_VALUE;
}
ChunkDescriptor descriptor = {.chunkType = chunkType,
@@ -321,7 +318,7 @@
// Write buffer to file
if (!android::base::WriteFully(fd, buffer.data(), buffer.size())) {
- LOG(ERROR) << "Failed to write chunk fd " << fd.get();
+ ALOGE("Failed to write chunk fd %d", fd.get());
return UNKNOWN_ERROR;
}
return NO_ERROR;
@@ -331,26 +328,26 @@
if (NO_ERROR !=
writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader),
reinterpret_cast<const uint8_t*>(&(mData.mHeader)))) {
- LOG(ERROR) << "Failed to write transactionHeader to fd " << fd.get();
+ ALOGE("Failed to write transactionHeader to fd %d", fd.get());
return UNKNOWN_ERROR;
}
if (NO_ERROR !=
writeChunk(fd, INTERFACE_NAME_CHUNK, mData.mInterfaceName.size() * sizeof(uint8_t),
reinterpret_cast<const uint8_t*>(mData.mInterfaceName.c_str()))) {
- LOG(INFO) << "Failed to write Interface Name Chunk to fd " << fd.get();
+ ALOGI("Failed to write Interface Name Chunk to fd %d", fd.get());
return UNKNOWN_ERROR;
}
if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataBufferSize(), mSent.data())) {
- LOG(ERROR) << "Failed to write sent Parcel to fd " << fd.get();
+ ALOGE("Failed to write sent Parcel to fd %d", fd.get());
return UNKNOWN_ERROR;
}
if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataBufferSize(), mReply.data())) {
- LOG(ERROR) << "Failed to write reply Parcel to fd " << fd.get();
+ ALOGE("Failed to write reply Parcel to fd %d", fd.get());
return UNKNOWN_ERROR;
}
if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) {
- LOG(ERROR) << "Failed to write end chunk to fd " << fd.get();
+ ALOGE("Failed to write end chunk to fd %d", fd.get());
return UNKNOWN_ERROR;
}
return NO_ERROR;
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 1ba20b3..fefaa81 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -168,7 +168,7 @@
void RpcServer::setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier) {
RpcMutexLockGuard _l(mLock);
- LOG_ALWAYS_FATAL_IF(mServer.fd != -1, "Already started");
+ LOG_ALWAYS_FATAL_IF(mServer.fd.ok(), "Already started");
mServerSocketModifier = std::move(modifier);
}
@@ -200,7 +200,7 @@
status_t RpcServer::acceptSocketConnection(const RpcServer& server, RpcTransportFd* out) {
RpcTransportFd clientSocket(unique_fd(TEMP_FAILURE_RETRY(
accept4(server.mServer.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK))));
- if (clientSocket.fd < 0) {
+ if (!clientSocket.fd.ok()) {
int savedErrno = errno;
ALOGE("Could not accept4 socket: %s", strerror(savedErrno));
return -savedErrno;
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index c895b21..cd8f417 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -208,7 +208,7 @@
unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
- if (serverFd == -1) {
+ if (!serverFd.ok()) {
int savedErrno = errno;
ALOGE("Could not connect to /dev/null: %s", strerror(savedErrno));
return -savedErrno;
@@ -594,7 +594,7 @@
unique_fd serverFd(TEMP_FAILURE_RETRY(
socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
- if (serverFd == -1) {
+ if (!serverFd.ok()) {
int savedErrno = errno;
ALOGE("Could not create socket at %s: %s", addr.toString().c_str(),
strerror(savedErrno));
diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp
index 3b53b05..2f2fe7d 100644
--- a/libs/binder/RpcTrusty.cpp
+++ b/libs/binder/RpcTrusty.cpp
@@ -16,7 +16,6 @@
#define LOG_TAG "RpcTrusty"
-#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <binder/RpcSession.h>
#include <binder/RpcTransportTipcAndroid.h>
@@ -35,13 +34,13 @@
auto request = [=] {
int tipcFd = tipc_connect(device, port);
if (tipcFd < 0) {
- LOG(ERROR) << "Failed to connect to Trusty service. Error code: " << tipcFd;
+ ALOGE("Failed to connect to Trusty service. Error code: %d", tipcFd);
return unique_fd();
}
return unique_fd(tipcFd);
};
if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
- LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up Trusty client. Error: %s", statusToString(status).c_str());
return nullptr;
}
return session;
diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp
index 47fd17d..d9a96af 100644
--- a/libs/binder/Utils.cpp
+++ b/libs/binder/Utils.cpp
@@ -16,7 +16,6 @@
#include "Utils.h"
-#include <android-base/logging.h>
#include <string.h>
namespace android {
@@ -26,7 +25,7 @@
}
std::string HexString(const void* bytes, size_t len) {
- CHECK(bytes != nullptr || len == 0) << bytes << " " << len;
+ LOG_ALWAYS_FATAL_IF(len > 0 && bytes == nullptr, "%p %zu", bytes, len);
// b/132916539: Doing this the 'C way', std::setfill triggers ubsan implicit conversion
const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index c8431aa..eec09eb 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#pragma once
+
#include <stddef.h>
#include <sys/uio.h>
#include <cstdint>
@@ -22,8 +24,16 @@
#include <log/log.h>
#include <utils/Errors.h>
-#define PLOGE_VA_ARGS(...) , ##__VA_ARGS__
-#define PLOGE(fmt, ...) ALOGE(fmt ": %s" PLOGE_VA_ARGS(__VA_ARGS__), strerror(errno))
+#define PLOGE(fmt, ...) \
+ do { \
+ auto savedErrno = errno; \
+ ALOGE(fmt ": %s" __VA_OPT__(, ) __VA_ARGS__, strerror(savedErrno)); \
+ } while (0)
+#define PLOGF(fmt, ...) \
+ do { \
+ auto savedErrno = errno; \
+ LOG_ALWAYS_FATAL(fmt ": %s" __VA_OPT__(, ) __VA_ARGS__, strerror(savedErrno)); \
+ } while (0)
/* TEMP_FAILURE_RETRY is not available on macOS and Trusty. */
#ifndef TEMP_FAILURE_RETRY
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index f51cd9b..118409e 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -16,7 +16,6 @@
#include <binder_rpc_unstable.hpp>
-#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <android/binder_libbinder.h>
#include <binder/RpcServer.h>
@@ -85,8 +84,8 @@
}
if (status_t status = server->setupVsockServer(bindCid, port); status != OK) {
- LOG(ERROR) << "Failed to set up vsock server with port " << port
- << " error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up vsock server with port %u error: %s", port,
+ statusToString(status).c_str());
return nullptr;
}
if (cid != VMADDR_CID_ANY) {
@@ -95,7 +94,7 @@
const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
if (cid != vaddr->svm_cid) {
- LOG(ERROR) << "Rejected vsock connection from CID " << vaddr->svm_cid;
+ ALOGE("Rejected vsock connection from CID %u", vaddr->svm_cid);
return false;
}
return true;
@@ -109,12 +108,12 @@
auto server = RpcServer::make();
auto fd = unique_fd(socketFd);
if (!fd.ok()) {
- LOG(ERROR) << "Invalid socket fd " << socketFd;
+ ALOGE("Invalid socket fd %d", socketFd);
return nullptr;
}
if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
- LOG(ERROR) << "Failed to set up RPC server with fd " << socketFd
- << " error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up RPC server with fd %d error: %s", socketFd,
+ statusToString(status).c_str());
return nullptr;
}
server->setRootObject(AIBinder_toPlatformBinder(service));
@@ -125,13 +124,13 @@
auto server = RpcServer::make();
auto fd = unique_fd(bootstrapFd);
if (!fd.ok()) {
- LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
+ ALOGE("Invalid bootstrap fd %d", bootstrapFd);
return nullptr;
}
if (status_t status = server->setupUnixDomainSocketBootstrapServer(std::move(fd));
status != OK) {
- LOG(ERROR) << "Failed to set up Unix Domain RPC server with bootstrap fd " << bootstrapFd
- << " error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up Unix Domain RPC server with bootstrap fd %d error: %s", bootstrapFd,
+ statusToString(status).c_str());
return nullptr;
}
server->setRootObject(AIBinder_toPlatformBinder(service));
@@ -141,8 +140,8 @@
ARpcServer* ARpcServer_newInet(AIBinder* service, const char* address, unsigned int port) {
auto server = RpcServer::make();
if (status_t status = server->setupInetServer(address, port, nullptr); status != OK) {
- LOG(ERROR) << "Failed to set up inet RPC server with address " << address << " and port "
- << port << " error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up inet RPC server with address %s and port %u error: %s", address,
+ port, statusToString(status).c_str());
return nullptr;
}
server->setRootObject(AIBinder_toPlatformBinder(service));
@@ -191,8 +190,8 @@
AIBinder* ARpcSession_setupVsockClient(ARpcSession* handle, unsigned int cid, unsigned int port) {
auto session = handleToStrongPointer<RpcSession>(handle);
if (status_t status = session->setupVsockClient(cid, port); status != OK) {
- LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port
- << " error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up vsock client with CID %u and port %u error: %s", cid, port,
+ statusToString(status).c_str());
return nullptr;
}
return AIBinder_fromPlatformBinder(session->getRootObject());
@@ -203,8 +202,8 @@
pathname = ANDROID_SOCKET_DIR "/" + pathname;
auto session = handleToStrongPointer<RpcSession>(handle);
if (status_t status = session->setupUnixDomainClient(pathname.c_str()); status != OK) {
- LOG(ERROR) << "Failed to set up Unix Domain RPC client with path: " << pathname
- << " error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up Unix Domain RPC client with path: %s error: %s", pathname.c_str(),
+ statusToString(status).c_str());
return nullptr;
}
return AIBinder_fromPlatformBinder(session->getRootObject());
@@ -214,13 +213,13 @@
auto session = handleToStrongPointer<RpcSession>(handle);
auto fd = unique_fd(dup(bootstrapFd));
if (!fd.ok()) {
- LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
+ ALOGE("Invalid bootstrap fd %d", bootstrapFd);
return nullptr;
}
if (status_t status = session->setupUnixDomainSocketBootstrapClient(std::move(fd));
status != OK) {
- LOG(ERROR) << "Failed to set up Unix Domain RPC client with bootstrap fd: " << bootstrapFd
- << " error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up Unix Domain RPC client with bootstrap fd: %d error: %s",
+ bootstrapFd, statusToString(status).c_str());
return nullptr;
}
return AIBinder_fromPlatformBinder(session->getRootObject());
@@ -229,8 +228,8 @@
AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsigned int port) {
auto session = handleToStrongPointer<RpcSession>(handle);
if (status_t status = session->setupInetClient(address, port); status != OK) {
- LOG(ERROR) << "Failed to set up inet RPC client with address " << address << " and port "
- << port << " error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up inet RPC client with address %s and port %u error: %s", address,
+ port, statusToString(status).c_str());
return nullptr;
}
return AIBinder_fromPlatformBinder(session->getRootObject());
@@ -241,7 +240,7 @@
auto session = handleToStrongPointer<RpcSession>(handle);
auto request = [=] { return unique_fd{requestFd(param)}; };
if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
- LOG(ERROR) << "Failed to set up vsock client. error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up vsock client. error: %s", statusToString(status).c_str());
return nullptr;
}
return AIBinder_fromPlatformBinder(session->getRootObject());
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 47da296..bf7a0ba 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <android-base/logging.h>
#include <android/binder_ibinder.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_stability.h>
@@ -48,8 +47,8 @@
void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */};
static void attach(const sp<IBinder>& binder) {
- // can only attach once
- CHECK_EQ(nullptr, binder->attachObject(kId, kValue, nullptr /*cookie*/, clean));
+ auto alreadyAttached = binder->attachObject(kId, kValue, nullptr /*cookie*/, clean);
+ LOG_ALWAYS_FATAL_IF(alreadyAttached != nullptr, "can only attach once");
}
static bool has(const sp<IBinder>& binder) {
return binder != nullptr && binder->findObject(kId) == kValue;
@@ -65,9 +64,9 @@
};
void clean(const void* id, void* obj, void* cookie) {
// be weary of leaks!
- // LOG(INFO) << "Deleting an ABpBinder";
+ // ALOGI("Deleting an ABpBinder");
- CHECK(id == kId) << id << " " << obj << " " << cookie;
+ LOG_ALWAYS_FATAL_IF(id != kId, "%p %p %p", id, obj, cookie);
delete static_cast<Value*>(obj);
};
@@ -121,14 +120,13 @@
if (mClazz != nullptr && !asABpBinder()) {
const String16& currentDescriptor = mClazz->getInterfaceDescriptor();
if (newDescriptor == currentDescriptor) {
- LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor
- << "' match during associateClass, but they are different class objects ("
- << clazz << " vs " << mClazz << "). Class descriptor collision?";
+ ALOGE("Class descriptors '%s' match during associateClass, but they are different class"
+ " objects (%p vs %p). Class descriptor collision?",
+ String8(currentDescriptor).c_str(), clazz, mClazz);
} else {
- LOG(ERROR) << __func__
- << ": Class cannot be associated on object which already has a class. "
- "Trying to associate to '"
- << newDescriptor << "' but already set to '" << currentDescriptor << "'.";
+ ALOGE("%s: Class cannot be associated on object which already has a class. "
+ "Trying to associate to '%s' but already set to '%s'.",
+ __func__, String8(newDescriptor).c_str(), String8(currentDescriptor).c_str());
}
// always a failure because we know mClazz != clazz
@@ -141,13 +139,12 @@
// more flake-proof. However, the check is not dependent on the lock.
if (descriptor != newDescriptor && !(asABpBinder() && asABpBinder()->isServiceFuzzing())) {
if (getBinder()->isBinderAlive()) {
- LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
- << "' but descriptor is actually '" << SanitizeString(descriptor) << "'.";
+ ALOGE("%s: Expecting binder to have class '%s' but descriptor is actually '%s'.",
+ __func__, String8(newDescriptor).c_str(), SanitizeString(descriptor).c_str());
} else {
// b/155793159
- LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor
- << "' to dead binder with cached descriptor '" << SanitizeString(descriptor)
- << "'.";
+ ALOGE("%s: Cannot associate class '%s' to dead binder with cached descriptor '%s'.",
+ __func__, String8(newDescriptor).c_str(), SanitizeString(descriptor).c_str());
}
return false;
}
@@ -164,7 +161,7 @@
ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData)
: AIBinder(clazz), BBinder(), mUserData(userData) {
- CHECK(clazz != nullptr);
+ LOG_ALWAYS_FATAL_IF(clazz == nullptr, "clazz == nullptr");
}
ABBinder::~ABBinder() {
getClass()->onDestroy(mUserData);
@@ -184,7 +181,7 @@
// technically UINT32_MAX would be okay here, but INT32_MAX is expected since this may be
// null in Java
if (args.size() > INT32_MAX) {
- LOG(ERROR) << "ABBinder::dump received too many arguments: " << args.size();
+ ALOGE("ABBinder::dump received too many arguments: %zu", args.size());
return STATUS_BAD_VALUE;
}
@@ -263,7 +260,7 @@
ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder)
: AIBinder(nullptr /*clazz*/), mRemote(binder) {
- CHECK(binder != nullptr);
+ LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr");
}
ABpBinder::~ABpBinder() {}
@@ -373,27 +370,27 @@
}
void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) {
- CHECK(clazz != nullptr) << "setOnDump requires non-null clazz";
+ LOG_ALWAYS_FATAL_IF(clazz == nullptr, "setOnDump requires non-null clazz");
// this is required to be called before instances are instantiated
clazz->onDump = onDump;
}
void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) {
- CHECK(clazz != nullptr) << "disableInterfaceTokenHeader requires non-null clazz";
+ LOG_ALWAYS_FATAL_IF(clazz == nullptr, "disableInterfaceTokenHeader requires non-null clazz");
clazz->writeHeader = false;
}
void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz,
AIBinder_handleShellCommand handleShellCommand) {
- CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz";
+ LOG_ALWAYS_FATAL_IF(clazz == nullptr, "setHandleShellCommand requires non-null clazz");
clazz->handleShellCommand = handleShellCommand;
}
const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) {
- CHECK(clazz != nullptr) << "getDescriptor requires non-null clazz";
+ LOG_ALWAYS_FATAL_IF(clazz == nullptr, "getDescriptor requires non-null clazz");
return clazz->getInterfaceDescriptorUtf8();
}
@@ -405,8 +402,8 @@
}
void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
- CHECK(who == mWho) << who.unsafe_get() << "(" << who.get_refs() << ") vs " << mWho.unsafe_get()
- << " (" << mWho.get_refs() << ")";
+ LOG_ALWAYS_FATAL_IF(who != mWho, "%p (%p) vs %p (%p)", who.unsafe_get(), who.get_refs(),
+ mWho.unsafe_get(), mWho.get_refs());
mOnDied(mCookie);
@@ -417,7 +414,7 @@
if (recipient != nullptr && strongWho != nullptr) {
status_t result = recipient->unlinkToDeath(strongWho, mCookie);
if (result != ::android::DEAD_OBJECT) {
- LOG(WARNING) << "Unlinking to dead binder resulted in: " << result;
+ ALOGW("Unlinking to dead binder resulted in: %d", result);
}
}
@@ -426,7 +423,7 @@
AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied)
: mOnDied(onDied), mOnUnlinked(nullptr) {
- CHECK(onDied != nullptr);
+ LOG_ALWAYS_FATAL_IF(onDied == nullptr, "onDied == nullptr");
}
void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() {
@@ -438,7 +435,7 @@
}
binder_status_t AIBinder_DeathRecipient::linkToDeath(const sp<IBinder>& binder, void* cookie) {
- CHECK(binder != nullptr);
+ LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr");
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
@@ -459,7 +456,7 @@
}
binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& binder, void* cookie) {
- CHECK(binder != nullptr);
+ LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr");
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
@@ -471,9 +468,8 @@
status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/);
if (status != ::android::OK) {
- LOG(ERROR) << __func__
- << ": removed reference to death recipient but unlink failed: "
- << statusToString(status);
+ ALOGE("%s: removed reference to death recipient but unlink failed: %s", __func__,
+ statusToString(status).c_str());
}
return PruneStatusT(status);
}
@@ -490,7 +486,7 @@
AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) {
if (clazz == nullptr) {
- LOG(ERROR) << __func__ << ": Must provide class to construct local binder.";
+ ALOGE("%s: Must provide class to construct local binder.", __func__);
return nullptr;
}
@@ -554,8 +550,7 @@
binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
void* cookie) {
if (binder == nullptr || recipient == nullptr) {
- LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient ("
- << recipient << ")";
+ ALOGE("%s: Must provide binder (%p) and recipient (%p)", __func__, binder, recipient);
return STATUS_UNEXPECTED_NULL;
}
@@ -566,8 +561,7 @@
binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
void* cookie) {
if (binder == nullptr || recipient == nullptr) {
- LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient ("
- << recipient << ")";
+ ALOGE("%s: Must provide binder (%p) and recipient (%p)", __func__, binder, recipient);
return STATUS_UNEXPECTED_NULL;
}
@@ -596,7 +590,7 @@
}
void AIBinder_decStrong(AIBinder* binder) {
if (binder == nullptr) {
- LOG(ERROR) << __func__ << ": on null binder";
+ ALOGE("%s: on null binder", __func__);
return;
}
@@ -604,7 +598,7 @@
}
int32_t AIBinder_debugGetRefCount(AIBinder* binder) {
if (binder == nullptr) {
- LOG(ERROR) << __func__ << ": on null binder";
+ ALOGE("%s: on null binder", __func__);
return -1;
}
@@ -642,15 +636,14 @@
binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) {
if (binder == nullptr || in == nullptr) {
- LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder
- << ") and in (" << in << ").";
+ ALOGE("%s: requires non-null parameters binder (%p) and in (%p).", __func__, binder, in);
return STATUS_UNEXPECTED_NULL;
}
const AIBinder_Class* clazz = binder->getClass();
if (clazz == nullptr) {
- LOG(ERROR) << __func__
- << ": Class must be defined for a remote binder transaction. See "
- "AIBinder_associateClass.";
+ ALOGE("%s: Class must be defined for a remote binder transaction. See "
+ "AIBinder_associateClass.",
+ __func__);
return STATUS_INVALID_OPERATION;
}
@@ -683,7 +676,7 @@
binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, AParcel** in,
AParcel** out, binder_flags_t flags) {
if (in == nullptr) {
- LOG(ERROR) << __func__ << ": requires non-null in parameter";
+ ALOGE("%s: requires non-null in parameter", __func__);
return STATUS_UNEXPECTED_NULL;
}
@@ -693,27 +686,26 @@
AutoParcelDestroyer forIn(in, DestroyParcel);
if (!isUserCommand(code)) {
- LOG(ERROR) << __func__
- << ": Only user-defined transactions can be made from the NDK, but requested: "
- << code;
+ ALOGE("%s: Only user-defined transactions can be made from the NDK, but requested: %d",
+ __func__, code);
return STATUS_UNKNOWN_TRANSACTION;
}
constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY | FLAG_CLEAR_BUF;
if ((flags & ~kAllFlags) != 0) {
- LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags;
+ ALOGE("%s: Unrecognized flags sent: %d", __func__, flags);
return STATUS_BAD_VALUE;
}
if (binder == nullptr || *in == nullptr || out == nullptr) {
- LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder << "), in ("
- << in << "), and out (" << out << ").";
+ ALOGE("%s: requires non-null parameters binder (%p), in (%p), and out (%p).", __func__,
+ binder, in, out);
return STATUS_UNEXPECTED_NULL;
}
if ((*in)->getBinder() != binder) {
- LOG(ERROR) << __func__ << ": parcel is associated with binder object " << binder
- << " but called with " << (*in)->getBinder();
+ ALOGE("%s: parcel is associated with binder object %p but called with %p", __func__, binder,
+ (*in)->getBinder());
return STATUS_BAD_VALUE;
}
@@ -733,7 +725,7 @@
AIBinder_DeathRecipient* AIBinder_DeathRecipient_new(
AIBinder_DeathRecipient_onBinderDied onBinderDied) {
if (onBinderDied == nullptr) {
- LOG(ERROR) << __func__ << ": requires non-null onBinderDied parameter.";
+ ALOGE("%s: requires non-null onBinderDied parameter.", __func__);
return nullptr;
}
auto ret = new AIBinder_DeathRecipient(onBinderDied);
@@ -799,9 +791,8 @@
void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) {
ABBinder* localBinder = binder->asABBinder();
- if (localBinder == nullptr) {
- LOG(FATAL) << "AIBinder_setRequestingSid must be called on a local binder";
- }
+ LOG_ALWAYS_FATAL_IF(localBinder == nullptr,
+ "AIBinder_setRequestingSid must be called on a local binder");
localBinder->setRequestingSid(requestingSid);
}
@@ -816,9 +807,8 @@
void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) {
ABBinder* localBinder = binder->asABBinder();
- if (localBinder == nullptr) {
- LOG(FATAL) << "AIBinder_setInheritRt must be called on a local binder";
- }
+ LOG_ALWAYS_FATAL_IF(localBinder == nullptr,
+ "AIBinder_setInheritRt must be called on a local binder");
localBinder->setInheritRt(inheritRt);
}
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 037aa2e..c15bcf9 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -14,20 +14,19 @@
* limitations under the License.
*/
+#include <android-base/unique_fd.h>
#include <android/binder_parcel.h>
#include <android/binder_parcel_platform.h>
-#include "parcel_internal.h"
-
-#include "ibinder_internal.h"
-#include "status_internal.h"
+#include <binder/Parcel.h>
+#include <binder/ParcelFileDescriptor.h>
+#include <inttypes.h>
+#include <utils/Unicode.h>
#include <limits>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <binder/Parcel.h>
-#include <binder/ParcelFileDescriptor.h>
-#include <utils/Unicode.h>
+#include "ibinder_internal.h"
+#include "parcel_internal.h"
+#include "status_internal.h"
using ::android::IBinder;
using ::android::Parcel;
@@ -52,11 +51,11 @@
if (length < -1) return STATUS_BAD_VALUE;
if (!isNullArray && length < 0) {
- LOG(ERROR) << __func__ << ": non-null array but length is " << length;
+ ALOGE("non-null array but length is %" PRIi32, length);
return STATUS_BAD_VALUE;
}
if (isNullArray && length > 0) {
- LOG(ERROR) << __func__ << ": null buffer cannot be for size " << length << " array.";
+ ALOGE("null buffer cannot be for size %" PRIi32 " array.", length);
return STATUS_BAD_VALUE;
}
@@ -325,7 +324,7 @@
binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t length) {
if (string == nullptr) {
if (length != -1) {
- LOG(WARNING) << __func__ << ": null string must be used with length == -1.";
+ ALOGW("null string must be used with length == -1.");
return STATUS_BAD_VALUE;
}
@@ -334,7 +333,7 @@
}
if (length < 0) {
- LOG(WARNING) << __func__ << ": Negative string length: " << length;
+ ALOGW("Negative string length: %" PRIi32, length);
return STATUS_BAD_VALUE;
}
@@ -342,7 +341,7 @@
const ssize_t len16 = utf8_to_utf16_length(str8, length);
if (len16 < 0 || len16 >= std::numeric_limits<int32_t>::max()) {
- LOG(WARNING) << __func__ << ": Invalid string length: " << len16;
+ ALOGW("Invalid string length: %zd", len16);
return STATUS_BAD_VALUE;
}
@@ -383,7 +382,7 @@
}
if (len8 <= 0 || len8 > std::numeric_limits<int32_t>::max()) {
- LOG(WARNING) << __func__ << ": Invalid string length: " << len8;
+ ALOGW("Invalid string length: %zd", len8);
return STATUS_BAD_VALUE;
}
@@ -391,7 +390,7 @@
bool success = allocator(stringData, len8, &str8);
if (!success || str8 == nullptr) {
- LOG(WARNING) << __func__ << ": AParcel_stringAllocator failed to allocate.";
+ ALOGW("AParcel_stringAllocator failed to allocate.");
return STATUS_NO_MEMORY;
}
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index 0fea57b..0072ac3 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -15,12 +15,10 @@
*/
#include <android/binder_process.h>
+#include <binder/IPCThreadState.h>
#include <mutex>
-#include <android-base/logging.h>
-#include <binder/IPCThreadState.h>
-
using ::android::IPCThreadState;
using ::android::ProcessState;
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 2977786..3bfdc59 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -15,14 +15,12 @@
*/
#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <binder/LazyServiceRegistrar.h>
#include "ibinder_internal.h"
#include "status_internal.h"
-#include <android-base/logging.h>
-#include <binder/IServiceManager.h>
-#include <binder/LazyServiceRegistrar.h>
-
using ::android::defaultServiceManager;
using ::android::IBinder;
using ::android::IServiceManager;
@@ -115,7 +113,8 @@
std::lock_guard<std::mutex> l(m);
if (onRegister == nullptr) return;
- CHECK_EQ(String8(smInstance), instance);
+ LOG_ALWAYS_FATAL_IF(String8(smInstance) != instance, "onServiceRegistration: %s != %s",
+ String8(smInstance).c_str(), instance);
sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder);
AIBinder_incStrong(ret.get());
@@ -135,8 +134,8 @@
AServiceManager_registerForServiceNotifications(const char* instance,
AServiceManager_onRegister onRegister,
void* cookie) {
- CHECK_NE(instance, nullptr);
- CHECK_NE(onRegister, nullptr) << instance;
+ LOG_ALWAYS_FATAL_IF(instance == nullptr, "instance == nullptr");
+ LOG_ALWAYS_FATAL_IF(onRegister == nullptr, "onRegister == nullptr for %s", instance);
// cookie can be nullptr
auto cb = sp<AServiceManager_NotificationRegistration>::make();
@@ -146,8 +145,8 @@
sp<IServiceManager> sm = defaultServiceManager();
if (status_t res = sm->registerForNotifications(String16(instance), cb); res != STATUS_OK) {
- LOG(ERROR) << "Failed to register for service notifications for " << instance << ": "
- << statusToString(res);
+ ALOGE("Failed to register for service notifications for %s: %s", instance,
+ statusToString(res).c_str());
return nullptr;
}
@@ -157,7 +156,7 @@
void AServiceManager_NotificationRegistration_delete(
AServiceManager_NotificationRegistration* notification) {
- CHECK_NE(notification, nullptr);
+ LOG_ALWAYS_FATAL_IF(notification == nullptr, "notification == nullptr");
notification->clear();
notification->decStrong(nullptr);
}
@@ -172,9 +171,9 @@
}
void AServiceManager_forEachDeclaredInstance(const char* interface, void* context,
void (*callback)(const char*, void*)) {
- CHECK(interface != nullptr);
+ LOG_ALWAYS_FATAL_IF(interface == nullptr, "interface == nullptr");
// context may be nullptr
- CHECK(callback != nullptr);
+ LOG_ALWAYS_FATAL_IF(callback == nullptr, "callback == nullptr");
sp<IServiceManager> sm = defaultServiceManager();
for (const String16& instance : sm->getDeclaredInstances(String16(interface))) {
@@ -191,9 +190,9 @@
}
void AServiceManager_getUpdatableApexName(const char* instance, void* context,
void (*callback)(const char*, void*)) {
- CHECK_NE(instance, nullptr);
+ LOG_ALWAYS_FATAL_IF(instance == nullptr, "instance == nullptr");
// context may be nullptr
- CHECK_NE(callback, nullptr);
+ LOG_ALWAYS_FATAL_IF(callback == nullptr, "callback == nullptr");
sp<IServiceManager> sm = defaultServiceManager();
std::optional<String16> updatableViaApex = sm->updatableViaApex(String16(instance));
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 8ed91a5..3aac3c0 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -17,8 +17,6 @@
#include <android/binder_status.h>
#include "status_internal.h"
-#include <android-base/logging.h>
-
using ::android::status_t;
using ::android::statusToString;
using ::android::binder::Status;
@@ -127,8 +125,8 @@
return STATUS_UNKNOWN_ERROR;
default:
- LOG(WARNING) << __func__ << ": Unknown status_t (" << statusToString(status)
- << ") pruned into STATUS_UNKNOWN_ERROR";
+ ALOGW("%s: Unknown status_t (%s) pruned into STATUS_UNKNOWN_ERROR", __func__,
+ statusToString(status).c_str());
return STATUS_UNKNOWN_ERROR;
}
}
@@ -159,8 +157,8 @@
return EX_TRANSACTION_FAILED;
default:
- LOG(WARNING) << __func__ << ": Unknown binder exception (" << exception
- << ") pruned into EX_TRANSACTION_FAILED";
+ ALOGW("%s: Unknown binder exception (%d) pruned into EX_TRANSACTION_FAILED", __func__,
+ exception);
return EX_TRANSACTION_FAILED;
}
}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 78f8877..6d122c5 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -27,7 +27,7 @@
use std::convert::TryFrom;
use std::ffi::{c_void, CStr, CString};
use std::fmt;
-use std::fs::File;
+use std::io::Write;
use std::marker::PhantomData;
use std::ops::Deref;
use std::os::raw::c_char;
@@ -62,7 +62,7 @@
///
/// This handler is a no-op by default and should be implemented for each
/// Binder service struct that wishes to respond to dump transactions.
- fn dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> {
+ fn dump(&self, _writer: &mut dyn Write, _args: &[&CStr]) -> Result<()> {
Ok(())
}
}
@@ -165,7 +165,7 @@
/// Handle a request to invoke the dump transaction on this
/// object.
- fn on_dump(&self, file: &File, args: &[&CStr]) -> Result<()>;
+ fn on_dump(&self, file: &mut dyn Write, args: &[&CStr]) -> Result<()>;
/// Retrieve the class of this remote object.
///
@@ -934,8 +934,8 @@
}
}
- fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
- self.0.dump(file, args)
+ fn on_dump(&self, writer: &mut dyn std::io::Write, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
+ self.0.dump(writer, args)
}
fn get_class() -> $crate::binder_impl::InterfaceClass {
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index b248f5e..b250012 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -25,6 +25,7 @@
use std::convert::TryFrom;
use std::ffi::{c_void, CStr, CString};
use std::fs::File;
+use std::io::Write;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::os::raw::c_char;
@@ -341,7 +342,7 @@
}
// Safety: Our caller promised that fd is a file descriptor. We don't
// own this file descriptor, so we need to be careful not to drop it.
- let file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) };
+ let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) };
if args.is_null() && num_args != 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
@@ -366,7 +367,7 @@
// Safety: Our caller promised that the binder has a `T` pointer in its
// user data.
let binder: &T = unsafe { &*(object as *const T) };
- let res = binder.on_dump(&file, &args);
+ let res = binder.on_dump(&mut *file, &args);
match res {
Ok(()) => 0,
@@ -569,7 +570,7 @@
Ok(())
}
- fn on_dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> {
+ fn on_dump(&self, _writer: &mut dyn Write, _args: &[&CStr]) -> Result<()> {
Ok(())
}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index c049b80..c87fa89 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -26,7 +26,7 @@
use std::convert::{TryFrom, TryInto};
use std::ffi::CStr;
-use std::fs::File;
+use std::io::Write;
use std::sync::Mutex;
/// Name of service runner.
@@ -118,7 +118,7 @@
}
impl Interface for TestService {
- fn dump(&self, _file: &File, args: &[&CStr]) -> Result<(), StatusCode> {
+ fn dump(&self, _writer: &mut dyn Write, args: &[&CStr]) -> Result<(), StatusCode> {
let mut dump_args = self.dump_args.lock().unwrap();
dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned()));
Ok(())
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index bbb310a..624edba 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -87,8 +87,8 @@
android::base::borrowed_fd /* readEnd */)>& f) {
android::base::unique_fd childWriteEnd;
android::base::unique_fd childReadEnd;
- CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) << strerror(errno);
- CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) << strerror(errno);
+ if (!android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) PLOGF("child write pipe failed");
+ if (!android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) PLOGF("child read pipe failed");
if (0 == (mPid = fork())) {
// racey: assume parent doesn't crash before this is set
prctl(PR_SET_PDEATHSIG, SIGHUP);
@@ -146,8 +146,10 @@
auto socket_addr = UnixSocketAddress(addr.c_str());
base::unique_fd fd(
TEMP_FAILURE_RETRY(socket(socket_addr.addr()->sa_family, SOCK_STREAM, AF_UNIX)));
- CHECK(fd.ok());
- CHECK_EQ(0, TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize())));
+ if (!fd.ok()) PLOGF("initUnixSocket failed to create socket");
+ if (0 != TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize()))) {
+ PLOGF("initUnixSocket failed to bind");
+ }
return fd;
}
@@ -205,14 +207,12 @@
static base::unique_fd connectTo(const RpcSocketAddress& addr) {
base::unique_fd serverFd(
TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
- int savedErrno = errno;
- CHECK(serverFd.ok()) << "Could not create socket " << addr.toString() << ": "
- << strerror(savedErrno);
+ if (!serverFd.ok()) {
+ PLOGF("Could not create socket %s", addr.toString().c_str());
+ }
if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
- int savedErrno = errno;
- LOG(FATAL) << "Could not connect to socket " << addr.toString() << ": "
- << strerror(savedErrno);
+ PLOGF("Could not connect to socket %s", addr.toString().c_str());
}
return serverFd;
}
@@ -221,8 +221,7 @@
static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) {
base::unique_fd sockClient, sockServer;
if (!base::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) {
- int savedErrno = errno;
- LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
+ PLOGF("Failed socketpair()");
}
int zero = 0;
@@ -231,8 +230,7 @@
fds.emplace_back(std::move(sockServer));
if (binder::os::sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) {
- int savedErrno = errno;
- LOG(FATAL) << "Failed sendMessageOnSocket: " << strerror(savedErrno);
+ PLOGF("Failed sendMessageOnSocket");
}
return std::move(sockClient);
}
@@ -246,10 +244,12 @@
// threads.
std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
const BinderRpcOptions& options) {
- CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
+ LOG_ALWAYS_FATAL_IF(options.numSessions < 1, "Must have at least one session to a server");
if (options.numIncomingConnectionsBySession.size() != 0) {
- CHECK_EQ(options.numIncomingConnectionsBySession.size(), options.numSessions);
+ LOG_ALWAYS_FATAL_IF(options.numIncomingConnectionsBySession.size() != options.numSessions,
+ "%s: %zu != %zu", __func__,
+ options.numIncomingConnectionsBySession.size(), options.numSessions);
}
SocketType socketType = GetParam().type;
@@ -274,8 +274,7 @@
// Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec.
// This is because we cannot pass ParcelFileDescriptor over a pipe.
if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &socketFd)) {
- int savedErrno = errno;
- LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
+ PLOGF("Failed socketpair()");
}
}
@@ -288,8 +287,10 @@
auto writeFd = std::to_string(writeEnd.get());
auto readFd = std::to_string(readEnd.get());
- execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(),
- NULL);
+ auto status = execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(),
+ readFd.c_str(), NULL);
+ PLOGF("execl('%s', _, %s, %s) should not return at all, but it returned %d",
+ servicePath.c_str(), writeFd.c_str(), readFd.c_str(), status);
}));
BinderRpcTestServerConfig serverConfig;
@@ -334,16 +335,16 @@
}
writeToFd(ret->host.writeEnd(), clientInfo);
- CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
+ LOG_ALWAYS_FATAL_IF(serverInfo.port > std::numeric_limits<unsigned int>::max());
if (socketType == SocketType::INET) {
- CHECK_NE(0, serverInfo.port);
+ LOG_ALWAYS_FATAL_IF(0 == serverInfo.port);
}
if (rpcSecurity == RpcSecurity::TLS) {
const auto& serverCert = serverInfo.cert.data;
- CHECK_EQ(OK,
- certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
- serverCert));
+ LOG_ALWAYS_FATAL_IF(
+ OK !=
+ certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert));
}
}
@@ -356,7 +357,7 @@
? options.numIncomingConnectionsBySession.at(i)
: 0;
- CHECK(session->setProtocolVersion(clientVersion));
+ LOG_ALWAYS_FATAL_IF(!session->setProtocolVersion(clientVersion));
session->setMaxIncomingThreads(numIncoming);
session->setMaxOutgoingConnections(options.numOutgoingConnections);
session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
@@ -408,7 +409,7 @@
ret->sessions.clear();
break;
}
- CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
+ LOG_ALWAYS_FATAL_IF(status != OK, "Could not connect: %s", statusToString(status).c_str());
ret->sessions.push_back({session, session->getRootObject()});
}
return ret;
@@ -589,12 +590,12 @@
android::os::ParcelFileDescriptor fdA;
EXPECT_OK(proc.rootIface->blockingRecvFd(&fdA));
std::string result;
- CHECK(android::base::ReadFdToString(fdA.get(), &result));
+ ASSERT_TRUE(android::base::ReadFdToString(fdA.get(), &result));
EXPECT_EQ(result, "a");
android::os::ParcelFileDescriptor fdB;
EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB));
- CHECK(android::base::ReadFdToString(fdB.get(), &result));
+ ASSERT_TRUE(android::base::ReadFdToString(fdB.get(), &result));
EXPECT_EQ(result, "b");
saturateThreadPool(kNumServerThreads, proc.rootIface);
@@ -951,8 +952,8 @@
ASSERT_TRUE(status.isOk()) << status;
std::string result;
- CHECK(android::base::ReadFdToString(out.get(), &result));
- EXPECT_EQ(result, "hello");
+ ASSERT_TRUE(android::base::ReadFdToString(out.get(), &result));
+ ASSERT_EQ(result, "hello");
}
TEST_P(BinderRpc, SendFiles) {
@@ -981,7 +982,7 @@
ASSERT_TRUE(status.isOk()) << status;
std::string result;
- CHECK(android::base::ReadFdToString(out.get(), &result));
+ EXPECT_TRUE(android::base::ReadFdToString(out.get(), &result));
EXPECT_EQ(result, "123abcd");
}
@@ -1006,7 +1007,7 @@
ASSERT_TRUE(status.isOk()) << status;
std::string result;
- CHECK(android::base::ReadFdToString(out.get(), &result));
+ EXPECT_TRUE(android::base::ReadFdToString(out.get(), &result));
EXPECT_EQ(result, std::string(253, 'a'));
}
@@ -1158,7 +1159,7 @@
return false;
}
- LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno));
+ LOG_ALWAYS_FATAL_IF(!serverFd.ok(), "Could not create socket: %s", strerror(errno));
sockaddr_vm serverAddr{
.svm_family = AF_VSOCK,
@@ -1180,7 +1181,7 @@
// and they return ETIMEDOUT after that.
android::base::unique_fd connectFd(
TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
- LOG_ALWAYS_FATAL_IF(connectFd == -1, "Could not create socket for port %u: %s", vsockPort,
+ LOG_ALWAYS_FATAL_IF(!connectFd.ok(), "Could not create socket for port %u: %s", vsockPort,
strerror(errno));
bool success = false;
@@ -1359,7 +1360,11 @@
};
TEST(BinderRpc, Java) {
-#if !defined(__ANDROID__)
+ bool expectDebuggable = false;
+#if defined(__ANDROID__)
+ expectDebuggable = android::base::GetBoolProperty("ro.debuggable", false) &&
+ android::base::GetProperty("ro.build.type", "") != "user";
+#else
GTEST_SKIP() << "This test is only run on Android. Though it can technically run on host on"
"createRpcDelegateServiceManager() with a device attached, such test belongs "
"to binderHostDeviceTest. Hence, just disable this test on host.";
@@ -1387,8 +1392,7 @@
auto keepAlive = sp<BBinder>::make();
auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive);
- if (!android::base::GetBoolProperty("ro.debuggable", false) ||
- android::base::GetProperty("ro.build.type", "") == "user") {
+ if (!expectDebuggable) {
ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus)
<< "setRpcClientDebug should return INVALID_OPERATION on non-debuggable or user "
"builds, but get "
@@ -1593,12 +1597,10 @@
iovec iov{&buf, sizeof(buf)};
if (binder::os::receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) {
- int savedErrno = errno;
- LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno);
+ PLOGF("Failed receiveMessage");
}
- if (fds.size() != 1) {
- LOG(FATAL) << "Expected one FD from receiveMessage(), got " << fds.size();
- }
+ LOG_ALWAYS_FATAL_IF(fds.size() != 1, "Expected one FD from receiveMessage(), got %zu",
+ fds.size());
return std::move(std::get<base::unique_fd>(fds[0]));
}
@@ -2083,7 +2085,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+ __android_log_set_logger(__android_log_stderr_logger);
return RUN_ALL_TESTS();
}
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index c3070dd..b2b63e4 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -36,10 +36,12 @@
#include <string>
#include <vector>
+#ifdef __ANDROID__
+#include <android-base/properties.h>
+#endif
+
#ifndef __TRUSTY__
#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android/binder_auto_utils.h>
#include <android/binder_libbinder.h>
#include <binder/ProcessState.h>
@@ -156,21 +158,21 @@
#ifndef __TRUSTY__
static inline void writeString(android::base::borrowed_fd fd, std::string_view str) {
uint64_t length = str.length();
- CHECK(android::base::WriteFully(fd, &length, sizeof(length)));
- CHECK(android::base::WriteFully(fd, str.data(), str.length()));
+ LOG_ALWAYS_FATAL_IF(!android::base::WriteFully(fd, &length, sizeof(length)));
+ LOG_ALWAYS_FATAL_IF(!android::base::WriteFully(fd, str.data(), str.length()));
}
static inline std::string readString(android::base::borrowed_fd fd) {
uint64_t length;
- CHECK(android::base::ReadFully(fd, &length, sizeof(length)));
+ LOG_ALWAYS_FATAL_IF(!android::base::ReadFully(fd, &length, sizeof(length)));
std::string ret(length, '\0');
- CHECK(android::base::ReadFully(fd, ret.data(), length));
+ LOG_ALWAYS_FATAL_IF(!android::base::ReadFully(fd, ret.data(), length));
return ret;
}
static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) {
Parcel parcel;
- CHECK_EQ(OK, parcelable.writeToParcel(&parcel));
+ LOG_ALWAYS_FATAL_IF(OK != parcelable.writeToParcel(&parcel));
writeString(fd, std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize()));
}
@@ -178,9 +180,10 @@
static inline T readFromFd(android::base::borrowed_fd fd) {
std::string data = readString(fd);
Parcel parcel;
- CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
+ LOG_ALWAYS_FATAL_IF(OK !=
+ parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
T object;
- CHECK_EQ(OK, object.readFromParcel(&parcel));
+ LOG_ALWAYS_FATAL_IF(OK != object.readFromParcel(&parcel));
return object;
}
@@ -207,7 +210,7 @@
// Create an FD that returns `contents` when read.
static inline base::unique_fd mockFileDescriptor(std::string contents) {
android::base::unique_fd readFd, writeFd;
- CHECK(android::base::Pipe(&readFd, &writeFd)) << strerror(errno);
+ LOG_ALWAYS_FATAL_IF(!android::base::Pipe(&readFd, &writeFd), "%s", strerror(errno));
RpcMaybeThread([writeFd = std::move(writeFd), contents = std::move(contents)]() {
signal(SIGPIPE, SIG_IGN); // ignore possible SIGPIPE from the write
if (!WriteStringToFd(contents, writeFd)) {
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 7435f30..5025bd6 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -65,7 +65,7 @@
std::string acc;
for (const auto& file : files) {
std::string result;
- CHECK(android::base::ReadFdToString(file.get(), &result));
+ LOG_ALWAYS_FATAL_IF(!android::base::ReadFdToString(file.get(), &result));
acc.append(result);
}
out->reset(mockFileDescriptor(acc));
@@ -98,7 +98,7 @@
};
int main(int argc, char* argv[]) {
- android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+ __android_log_set_logger(__android_log_stderr_logger);
LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc);
base::unique_fd writeEnd(atoi(argv[1]));
@@ -118,7 +118,7 @@
auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
sp<RpcServer> server = RpcServer::make(newTlsFactory(rpcSecurity, certVerifier));
- CHECK(server->setProtocolVersion(serverConfig.serverVersion));
+ LOG_ALWAYS_FATAL_IF(!server->setProtocolVersion(serverConfig.serverVersion));
server->setMaxThreads(serverConfig.numThreads);
server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes);
@@ -129,22 +129,25 @@
case SocketType::PRECONNECTED:
[[fallthrough]];
case SocketType::UNIX:
- CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str()))
- << serverConfig.addr;
+ LOG_ALWAYS_FATAL_IF(OK != server->setupUnixDomainServer(serverConfig.addr.c_str()),
+ "%s", serverConfig.addr.c_str());
break;
case SocketType::UNIX_BOOTSTRAP:
- CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(socketFd)));
+ LOG_ALWAYS_FATAL_IF(OK !=
+ server->setupUnixDomainSocketBootstrapServer(std::move(socketFd)));
break;
case SocketType::UNIX_RAW:
- CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd)));
+ LOG_ALWAYS_FATAL_IF(OK != server->setupRawSocketServer(std::move(socketFd)));
break;
case SocketType::VSOCK:
- CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort))
- << "Need `sudo modprobe vsock_loopback`?";
+ LOG_ALWAYS_FATAL_IF(OK !=
+ server->setupVsockServer(VMADDR_CID_LOCAL,
+ serverConfig.vsockPort),
+ "Need `sudo modprobe vsock_loopback`?");
break;
case SocketType::INET: {
- CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
- CHECK_NE(0, outPort);
+ LOG_ALWAYS_FATAL_IF(OK != server->setupInetServer(kLocalInetAddress, 0, &outPort));
+ LOG_ALWAYS_FATAL_IF(0 == outPort);
break;
}
default:
@@ -159,21 +162,21 @@
if (rpcSecurity == RpcSecurity::TLS) {
for (const auto& clientCert : clientInfo.certs) {
- CHECK_EQ(OK,
- certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
- clientCert.data));
+ LOG_ALWAYS_FATAL_IF(OK !=
+ certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+ clientCert.data));
}
}
server->setPerSessionRootObject([&](wp<RpcSession> session, const void* addrPtr, size_t len) {
{
sp<RpcSession> spSession = session.promote();
- CHECK_NE(nullptr, spSession.get());
+ LOG_ALWAYS_FATAL_IF(nullptr == spSession.get());
}
// UNIX sockets with abstract addresses return
// sizeof(sa_family_t)==2 in addrlen
- CHECK_GE(len, sizeof(sa_family_t));
+ LOG_ALWAYS_FATAL_IF(len < sizeof(sa_family_t));
const sockaddr* addr = reinterpret_cast<const sockaddr*>(addrPtr);
sp<MyBinderRpcTestAndroid> service = sp<MyBinderRpcTestAndroid>::make();
switch (addr->sa_family) {
@@ -181,15 +184,15 @@
// nothing to save
break;
case AF_VSOCK:
- CHECK_EQ(len, sizeof(sockaddr_vm));
+ LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_vm));
service->port = reinterpret_cast<const sockaddr_vm*>(addr)->svm_port;
break;
case AF_INET:
- CHECK_EQ(len, sizeof(sockaddr_in));
+ LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_in));
service->port = ntohs(reinterpret_cast<const sockaddr_in*>(addr)->sin_port);
break;
case AF_INET6:
- CHECK_EQ(len, sizeof(sockaddr_in));
+ LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_in));
service->port = ntohs(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port);
break;
default:
diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk
index 1f05ef7..d2b37aa 100644
--- a/libs/binder/trusty/kernel/rules.mk
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -24,7 +24,6 @@
FMTLIB_DIR := external/fmtlib
MODULE_SRCS := \
- $(LOCAL_DIR)/../logging.cpp \
$(LOCAL_DIR)/../TrustyStatus.cpp \
$(LIBBINDER_DIR)/Binder.cpp \
$(LIBBINDER_DIR)/BpBinder.cpp \
diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp
deleted file mode 100644
index 88a1075..0000000
--- a/libs/binder/trusty/logging.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TLOG_TAG "libbinder"
-
-#include "android-base/logging.h"
-
-#include <trusty_log.h>
-#include <iostream>
-#include <string>
-
-#include <android-base/strings.h>
-
-namespace android {
-namespace base {
-
-static const char* GetFileBasename(const char* file) {
- const char* last_slash = strrchr(file, '/');
- if (last_slash != nullptr) {
- return last_slash + 1;
- }
- return file;
-}
-
-// This splits the message up line by line, by calling log_function with a pointer to the start of
-// each line and the size up to the newline character. It sends size = -1 for the final line.
-template <typename F, typename... Args>
-static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
- const char* newline;
- while ((newline = strchr(msg, '\n')) != nullptr) {
- log_function(msg, newline - msg, args...);
- msg = newline + 1;
- }
-
- log_function(msg, -1, args...);
-}
-
-void DefaultAborter(const char* abort_message) {
- TLOGC("aborting: %s\n", abort_message);
- abort();
-}
-
-static void TrustyLogLine(const char* msg, int /*length*/, android::base::LogSeverity severity,
- const char* tag) {
- switch (severity) {
- case VERBOSE:
- case DEBUG:
- TLOGD("%s: %s\n", tag, msg);
- break;
- case INFO:
- TLOGI("%s: %s\n", tag, msg);
- break;
- case WARNING:
- TLOGW("%s: %s\n", tag, msg);
- break;
- case ERROR:
- TLOGE("%s: %s\n", tag, msg);
- break;
- case FATAL_WITHOUT_ABORT:
- case FATAL:
- TLOGC("%s: %s\n", tag, msg);
- break;
- }
-}
-
-void TrustyLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
- const char*, unsigned int, const char* full_message) {
- SplitByLines(full_message, TrustyLogLine, severity, tag);
-}
-
-// This indirection greatly reduces the stack impact of having lots of
-// checks/logging in a function.
-class LogMessageData {
-public:
- LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- int error)
- : file_(GetFileBasename(file)),
- line_number_(line),
- severity_(severity),
- tag_(tag),
- error_(error) {}
-
- const char* GetFile() const { return file_; }
-
- unsigned int GetLineNumber() const { return line_number_; }
-
- LogSeverity GetSeverity() const { return severity_; }
-
- const char* GetTag() const { return tag_; }
-
- int GetError() const { return error_; }
-
- std::ostream& GetBuffer() { return buffer_; }
-
- std::string ToString() const { return buffer_.str(); }
-
-private:
- std::ostringstream buffer_;
- const char* const file_;
- const unsigned int line_number_;
- const LogSeverity severity_;
- const char* const tag_;
- const int error_;
-
- DISALLOW_COPY_AND_ASSIGN(LogMessageData);
-};
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
- const char* tag, int error)
- : LogMessage(file, line, severity, tag, error) {}
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- int error)
- : data_(new LogMessageData(file, line, severity, tag, error)) {}
-
-LogMessage::~LogMessage() {
- // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
- if (!WOULD_LOG(data_->GetSeverity())) {
- return;
- }
-
- // Finish constructing the message.
- if (data_->GetError() != -1) {
- data_->GetBuffer() << ": " << strerror(data_->GetError());
- }
- std::string msg(data_->ToString());
-
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
- msg.c_str());
-
- // Abort if necessary.
- if (data_->GetSeverity() == FATAL) {
- DefaultAborter(msg.c_str());
- }
-}
-
-std::ostream& LogMessage::stream() {
- return data_->GetBuffer();
-}
-
-void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- const char* message) {
- TrustyLogger(DEFAULT, severity, tag ?: "<unknown>", file, line, message);
-}
-
-bool ShouldLog(LogSeverity /*severity*/, const char* /*tag*/) {
- // This is controlled by Trusty's log level.
- return true;
-}
-
-} // namespace base
-} // namespace android
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index 2e56cbd..9cad556 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -24,7 +24,6 @@
FMTLIB_DIR := external/fmtlib
MODULE_SRCS := \
- $(LOCAL_DIR)/logging.cpp \
$(LOCAL_DIR)/OS.cpp \
$(LOCAL_DIR)/RpcServerTrusty.cpp \
$(LOCAL_DIR)/RpcTransportTipcTrusty.cpp \
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 8d0331e..f317a2e 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -41,6 +41,8 @@
#include <android-base/thread_annotations.h>
#include <chrono>
+#include <com_android_graphics_libgui_flags.h>
+
using namespace com::android::graphics::libgui;
using namespace std::chrono_literals;
@@ -102,12 +104,11 @@
}
}
-void BLASTBufferItemConsumer::updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
- const sp<Fence>& glDoneFence,
- const sp<Fence>& presentFence,
- const sp<Fence>& prevReleaseFence,
- CompositorTiming compositorTiming,
- nsecs_t latchTime, nsecs_t dequeueReadyTime) {
+void BLASTBufferItemConsumer::updateFrameTimestamps(
+ uint64_t frameNumber, uint64_t previousFrameNumber, nsecs_t refreshStartTime,
+ const sp<Fence>& glDoneFence, const sp<Fence>& presentFence,
+ const sp<Fence>& prevReleaseFence, CompositorTiming compositorTiming, nsecs_t latchTime,
+ nsecs_t dequeueReadyTime) {
Mutex::Autolock lock(mMutex);
// if the producer is not connected, don't bother updating,
@@ -118,7 +119,15 @@
std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(prevReleaseFence);
mFrameEventHistory.addLatch(frameNumber, latchTime);
- mFrameEventHistory.addRelease(frameNumber, dequeueReadyTime, std::move(releaseFenceTime));
+ if (flags::frametimestamps_previousrelease()) {
+ if (previousFrameNumber > 0) {
+ mFrameEventHistory.addRelease(previousFrameNumber, dequeueReadyTime,
+ std::move(releaseFenceTime));
+ }
+ } else {
+ mFrameEventHistory.addRelease(frameNumber, dequeueReadyTime, std::move(releaseFenceTime));
+ }
+
mFrameEventHistory.addPreComposition(frameNumber, refreshStartTime);
mFrameEventHistory.addPostComposition(frameNumber, glDoneFenceTime, presentFenceTime,
compositorTiming);
@@ -364,6 +373,7 @@
if (stat.latchTime > 0) {
mBufferItemConsumer
->updateFrameTimestamps(stat.frameEventStats.frameNumber,
+ stat.frameEventStats.previousFrameNumber,
stat.frameEventStats.refreshStartTime,
stat.frameEventStats.gpuCompositionDoneFence,
stat.presentFence, stat.previousReleaseFence,
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 29d64af..f5d19aa 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -25,6 +25,10 @@
#include <gui/LayerState.h>
#include <private/gui/ParcelUtils.h>
+#include <com_android_graphics_libgui_flags.h>
+
+using namespace com::android::graphics::libgui;
+
namespace android {
namespace { // Anonymous
@@ -49,6 +53,11 @@
status_t err = output->writeUint64(frameNumber);
if (err != NO_ERROR) return err;
+ if (flags::frametimestamps_previousrelease()) {
+ err = output->writeUint64(previousFrameNumber);
+ if (err != NO_ERROR) return err;
+ }
+
if (gpuCompositionDoneFence) {
err = output->writeBool(true);
if (err != NO_ERROR) return err;
@@ -79,6 +88,11 @@
status_t err = input->readUint64(&frameNumber);
if (err != NO_ERROR) return err;
+ if (flags::frametimestamps_previousrelease()) {
+ err = input->readUint64(&previousFrameNumber);
+ if (err != NO_ERROR) return err;
+ }
+
bool hasFence = false;
err = input->readBool(&hasFence);
if (err != NO_ERROR) return err;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index a351811..922b0dd 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -3122,12 +3122,12 @@
return statusTFromBinderStatus(status);
}
-status_t ScreenshotClient::captureDisplay(DisplayId displayId,
+status_t ScreenshotClient::captureDisplay(DisplayId displayId, const gui::CaptureArgs& captureArgs,
const sp<IScreenCaptureListener>& captureListener) {
sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService());
if (s == nullptr) return NO_INIT;
- binder::Status status = s->captureDisplayById(displayId.value, captureListener);
+ binder::Status status = s->captureDisplayById(displayId.value, captureArgs, captureListener);
return statusTFromBinderStatus(status);
}
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 2eb6bd6..6a4460b 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -66,8 +66,9 @@
bool WindowInfo::operator==(const WindowInfo& info) const {
return info.token == token && info.id == id && info.name == name &&
info.dispatchingTimeout == dispatchingTimeout && info.frame == frame &&
- info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
- info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
+ info.contentSize == contentSize && info.surfaceInset == surfaceInset &&
+ info.globalScaleFactor == globalScaleFactor && info.transform == transform &&
+ info.touchableRegion.hasSameRects(touchableRegion) &&
info.touchOcclusionMode == touchOcclusionMode && info.ownerPid == ownerPid &&
info.ownerUid == ownerUid && info.packageName == packageName &&
info.inputConfig == inputConfig && info.displayId == displayId &&
@@ -101,6 +102,8 @@
parcel->writeInt32(
static_cast<std::underlying_type_t<WindowInfo::Type>>(layoutParamsType)) ?:
parcel->write(frame) ?:
+ parcel->writeInt32(contentSize.width) ?:
+ parcel->writeInt32(contentSize.height) ?:
parcel->writeInt32(surfaceInset) ?:
parcel->writeFloat(globalScaleFactor) ?:
parcel->writeFloat(alpha) ?:
@@ -150,6 +153,8 @@
status = parcel->readInt32(&lpFlags) ?:
parcel->readInt32(&lpType) ?:
parcel->read(frame) ?:
+ parcel->readInt32(&contentSize.width) ?:
+ parcel->readInt32(&contentSize.height) ?:
parcel->readInt32(&surfaceInset) ?:
parcel->readFloat(&globalScaleFactor) ?:
parcel->readFloat(&alpha) ?:
diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl
new file mode 100644
index 0000000..920d949
--- /dev/null
+++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.gui;
+
+parcelable CaptureArgs cpp_header "gui/DisplayCaptureArgs.h";
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index a7cf5dd..d24f8ee 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -16,6 +16,7 @@
package android.gui;
+import android.gui.CaptureArgs;
import android.gui.Color;
import android.gui.CompositionPreference;
import android.gui.ContentSamplingAttributes;
@@ -238,7 +239,8 @@
* Capture the specified screen. This requires the READ_FRAME_BUFFER
* permission.
*/
- oneway void captureDisplayById(long displayId, IScreenCaptureListener listener);
+ oneway void captureDisplayById(long displayId, in CaptureArgs args,
+ IScreenCaptureListener listener);
/**
* Capture a subtree of the layer hierarchy, potentially ignoring the root node.
@@ -514,6 +516,13 @@
void scheduleCommit();
/**
+ * Force all window composition to the GPU (i.e. disable Hardware Overlays).
+ * This can help check if there is a bug in HW Composer.
+ * Requires root or android.permission.HARDWARE_TEST
+ */
+ void forceClientComposition(boolean enabled);
+
+ /**
* Gets priority of the RenderEngine in SurfaceFlinger.
*/
int getGpuContextPriority();
diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
index 17f4c63..2e270b7 100644
--- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
@@ -141,8 +141,8 @@
CompositorTiming compTiming;
sp<Fence> previousFence = new Fence(memfd_create("pfd", MFD_ALLOW_SEALING));
sp<Fence> gpuFence = new Fence(memfd_create("gfd", MFD_ALLOW_SEALING));
- FrameEventHistoryStats frameStats(frameNumber, gpuFence, compTiming,
- mFdp.ConsumeIntegral<int64_t>(),
+ FrameEventHistoryStats frameStats(frameNumber, mFdp.ConsumeIntegral<uint64_t>(), gpuFence,
+ compTiming, mFdp.ConsumeIntegral<int64_t>(),
mFdp.ConsumeIntegral<int64_t>());
std::vector<SurfaceControlStats> stats;
sp<Fence> presentFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING));
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 3142103..ffe7e41 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -100,8 +100,8 @@
MOCK_METHOD(binder::Status, setGameContentType, (const sp<IBinder>&, bool), (override));
MOCK_METHOD(binder::Status, captureDisplay,
(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&), (override));
- MOCK_METHOD(binder::Status, captureDisplayById, (int64_t, const sp<IScreenCaptureListener>&),
- (override));
+ MOCK_METHOD(binder::Status, captureDisplayById,
+ (int64_t, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&), (override));
MOCK_METHOD(binder::Status, captureLayers,
(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&), (override));
MOCK_METHOD(binder::Status, clearAnimationFrameStats, (), (override));
@@ -154,6 +154,7 @@
MOCK_METHOD(binder::Status, setDebugFlash, (int), (override));
MOCK_METHOD(binder::Status, scheduleComposite, (), (override));
MOCK_METHOD(binder::Status, scheduleCommit, (), (override));
+ MOCK_METHOD(binder::Status, forceClientComposition, (bool), (override));
MOCK_METHOD(binder::Status, updateSmallAreaDetection,
(const std::vector<int32_t>&, const std::vector<float>&), (override));
MOCK_METHOD(binder::Status, setSmallAreaDetectionThreshold, (int32_t, float), (override));
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 892215e..0e1a505 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -50,8 +50,8 @@
void onDisconnect() override EXCLUDES(mMutex);
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) override EXCLUDES(mMutex);
- void updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
- const sp<Fence>& gpuCompositionDoneFence,
+ void updateFrameTimestamps(uint64_t frameNumber, uint64_t previousFrameNumber,
+ nsecs_t refreshStartTime, const sp<Fence>& gpuCompositionDoneFence,
const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
CompositorTiming compositorTiming, nsecs_t latchTime,
nsecs_t dequeueReadyTime) EXCLUDES(mMutex);
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 364a57b..bc97cd0 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -95,15 +95,18 @@
status_t readFromParcel(const Parcel* input) override;
FrameEventHistoryStats() = default;
- FrameEventHistoryStats(uint64_t fn, const sp<Fence>& gpuCompFence, CompositorTiming compTiming,
+ FrameEventHistoryStats(uint64_t frameNumber, uint64_t previousFrameNumber,
+ const sp<Fence>& gpuCompFence, CompositorTiming compTiming,
nsecs_t refreshTime, nsecs_t dequeueReadyTime)
- : frameNumber(fn),
+ : frameNumber(frameNumber),
+ previousFrameNumber(previousFrameNumber),
gpuCompositionDoneFence(gpuCompFence),
compositorTiming(compTiming),
refreshStartTime(refreshTime),
dequeueReadyTime(dequeueReadyTime) {}
uint64_t frameNumber;
+ uint64_t previousFrameNumber;
sp<Fence> gpuCompositionDoneFence;
CompositorTiming compositorTiming;
nsecs_t refreshStartTime;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 54c3aa7..5bf6c47 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -841,8 +841,14 @@
class ScreenshotClient {
public:
static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
- static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+ static status_t captureDisplay(DisplayId, const gui::CaptureArgs&,
+ const sp<IScreenCaptureListener>&);
static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
+
+ [[deprecated]] static status_t captureDisplay(DisplayId id,
+ const sp<IScreenCaptureListener>& listener) {
+ return captureDisplay(id, gui::CaptureArgs(), listener);
+ }
};
// ---------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index bd2eb74..dcc38d7 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -26,6 +26,7 @@
#include <gui/constants.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/Size.h>
#include <ui/Transform.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -196,6 +197,9 @@
/* These values are filled in by SurfaceFlinger. */
Rect frame = Rect::INVALID_RECT;
+ // The real size of the content, excluding any crop. If no buffer is rendered, this is 0,0
+ ui::Size contentSize = ui::Size(0, 0);
+
/*
* SurfaceFlinger consumes this value to shrink the computed frame. This is
* different from shrinking the touchable region in that it DOES shift the coordinate
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index a16be78..b081030 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -8,3 +8,10 @@
is_fixed_read_only: true
}
+flag {
+ name: "frametimestamps_previousrelease"
+ namespace: "core_graphics"
+ description: "Controls a fence fixup for timestamp apis"
+ bug: "310927247"
+ is_fixed_read_only: true
+}
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 9893c71..ea7078d 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -42,9 +42,12 @@
#include <gtest/gtest.h>
+#include <com_android_graphics_libgui_flags.h>
+
using namespace std::chrono_literals;
namespace android {
+using namespace com::android::graphics::libgui;
using Transaction = SurfaceComposerClient::Transaction;
using android::hardware::graphics::common::V1_2::BufferUsage;
@@ -1581,6 +1584,9 @@
nsecs_t postedTimeB = 0;
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true);
history.applyDelta(qbOutput.frameTimestamps);
+
+ adapter.waitForCallback(2);
+
events = history.getFrame(1);
ASSERT_NE(nullptr, events);
@@ -1590,7 +1596,9 @@
ASSERT_GE(events->postedTime, postedTimeA);
ASSERT_GE(events->latchTime, postedTimeA);
- ASSERT_GE(events->dequeueReadyTime, events->latchTime);
+ if (flags::frametimestamps_previousrelease()) {
+ ASSERT_EQ(events->dequeueReadyTime, FrameEvents::TIMESTAMP_PENDING);
+ }
ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
ASSERT_NE(nullptr, events->displayPresentFence);
ASSERT_NE(nullptr, events->releaseFence);
@@ -1602,6 +1610,50 @@
ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeB);
+ // Now do the same as above with a third buffer, so that timings related to
+ // buffer releases make it back to the first frame.
+ nsecs_t requestedPresentTimeC = 0;
+ nsecs_t postedTimeC = 0;
+ setUpAndQueueBuffer(igbProducer, &requestedPresentTimeC, &postedTimeC, &qbOutput, true);
+ history.applyDelta(qbOutput.frameTimestamps);
+
+ adapter.waitForCallback(3);
+
+ // Check the first frame...
+ events = history.getFrame(1);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeA);
+ ASSERT_GE(events->latchTime, postedTimeA);
+ // Now dequeueReadyTime is valid, because the release timings finally
+ // propaged to queueBuffer()
+ ASSERT_GE(events->dequeueReadyTime, events->latchTime);
+ ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+ ASSERT_NE(nullptr, events->displayPresentFence);
+ ASSERT_NE(nullptr, events->releaseFence);
+
+ // ...and the second
+ events = history.getFrame(2);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeB);
+ ASSERT_GE(events->latchTime, postedTimeB);
+ if (flags::frametimestamps_previousrelease()) {
+ ASSERT_EQ(events->dequeueReadyTime, FrameEvents::TIMESTAMP_PENDING);
+ }
+ ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+ ASSERT_NE(nullptr, events->displayPresentFence);
+ ASSERT_NE(nullptr, events->releaseFence);
+
+ // ...and finally the third!
+ events = history.getFrame(3);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(3, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeC, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeC);
+
// wait for any callbacks that have not been received
adapter.waitForCallbacks();
}
@@ -1660,6 +1712,8 @@
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeC, &postedTimeC, &qbOutput, true);
history.applyDelta(qbOutput.frameTimestamps);
+ adapter.waitForCallback(3);
+
// frame number, requestedPresentTime, and postTime should not have changed
ASSERT_EQ(1, events->frameNumber);
ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
@@ -1679,6 +1733,42 @@
ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeB);
ASSERT_GE(events->latchTime, postedTimeB);
+
+ if (flags::frametimestamps_previousrelease()) {
+ ASSERT_EQ(events->dequeueReadyTime, FrameEvents::TIMESTAMP_PENDING);
+ }
+ ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+ ASSERT_NE(nullptr, events->displayPresentFence);
+ ASSERT_NE(nullptr, events->releaseFence);
+
+ // Queue another buffer to check for timestamps that came late
+ nsecs_t requestedPresentTimeD = 0;
+ nsecs_t postedTimeD = 0;
+ setUpAndQueueBuffer(igbProducer, &requestedPresentTimeD, &postedTimeD, &qbOutput, true);
+ history.applyDelta(qbOutput.frameTimestamps);
+
+ adapter.waitForCallback(4);
+
+ // frame number, requestedPresentTime, and postTime should not have changed
+ events = history.getFrame(1);
+ ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeA);
+
+ // a valid latchtime and pre and post composition info should not be set for the dropped frame
+ ASSERT_FALSE(events->hasLatchInfo());
+ ASSERT_FALSE(events->hasDequeueReadyInfo());
+ ASSERT_FALSE(events->hasGpuCompositionDoneInfo());
+ ASSERT_FALSE(events->hasDisplayPresentInfo());
+ ASSERT_FALSE(events->hasReleaseInfo());
+
+ // we should also have gotten values for the presented frame
+ events = history.getFrame(2);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeB);
+ ASSERT_GE(events->latchTime, postedTimeB);
ASSERT_GE(events->dequeueReadyTime, events->latchTime);
ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
ASSERT_NE(nullptr, events->displayPresentFence);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 86ced2c..60221aa 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -208,21 +208,6 @@
ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
}
- static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
- ScreenCaptureResults& captureResults) {
- const auto sf = ComposerServiceAIDL::getComposerService();
- SurfaceComposerClient::Transaction().apply(true);
-
- const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- binder::Status status = sf->captureDisplay(captureArgs, captureListener);
- status_t err = gui::aidl_utils::statusTFromBinderStatus(status);
- if (err != NO_ERROR) {
- return err;
- }
- captureResults = captureListener->waitForResults();
- return fenceStatus(captureResults.fenceResult);
- }
-
sp<Surface> mSurface;
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mSurfaceControl;
@@ -260,56 +245,6 @@
EXPECT_EQ(1, result);
}
-// This test probably doesn't belong here.
-TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersDontSucceed) {
- sp<ANativeWindow> anw(mSurface);
-
- // Verify the screenshot works with no protected buffers.
- const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
- ASSERT_FALSE(ids.empty());
- // display 0 is picked for now, can extend to support all displays if needed
- const sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
- ASSERT_FALSE(display == nullptr);
-
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = display;
- captureArgs.width = 64;
- captureArgs.height = 64;
-
- ScreenCaptureResults captureResults;
- ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
-
- ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
- NATIVE_WINDOW_API_CPU));
- // Set the PROTECTED usage bit and verify that the screenshot fails. Note
- // that we need to dequeue a buffer in order for it to actually get
- // allocated in SurfaceFlinger.
- ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(),
- GRALLOC_USAGE_PROTECTED));
- ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3));
- ANativeWindowBuffer* buf = nullptr;
-
- status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf);
- if (err) {
- // we could fail if GRALLOC_USAGE_PROTECTED is not supported.
- // that's okay as long as this is the reason for the failure.
- // try again without the GRALLOC_USAGE_PROTECTED bit.
- ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0));
- ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
- &buf));
- return;
- }
- ASSERT_EQ(NO_ERROR, anw->cancelBuffer(anw.get(), buf, -1));
-
- for (int i = 0; i < 4; i++) {
- // Loop to make sure SurfaceFlinger has retired a protected buffer.
- ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
- &buf));
- ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
- }
- ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
-}
-
TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
sp<ANativeWindow> anw(mSurface);
int result = -123;
@@ -851,7 +786,8 @@
return binder::Status::ok();
}
- binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override {
+ binder::Status captureDisplayById(int64_t, const gui::CaptureArgs&,
+ const sp<IScreenCaptureListener>&) override {
return binder::Status::ok();
}
@@ -999,6 +935,10 @@
binder::Status scheduleCommit() override { return binder::Status::ok(); }
+ binder::Status forceClientComposition(bool /*enabled*/) override {
+ return binder::Status::ok();
+ }
+
binder::Status updateSmallAreaDetection(const std::vector<int32_t>& /*appIds*/,
const std::vector<float>& /*thresholds*/) {
return binder::Status::ok();
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index f2feaef..5eb5d3b 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -28,6 +28,7 @@
using gui::InputApplicationInfo;
using gui::TouchOcclusionMode;
using gui::WindowInfo;
+using ui::Size;
namespace test {
@@ -53,6 +54,7 @@
i.layoutParamsType = WindowInfo::Type::INPUT_METHOD;
i.dispatchingTimeout = 12s;
i.frame = Rect(93, 34, 16, 19);
+ i.contentSize = Size(10, 40);
i.surfaceInset = 17;
i.globalScaleFactor = 0.3;
i.alpha = 0.7;
@@ -83,6 +85,7 @@
ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout);
ASSERT_EQ(i.frame, i2.frame);
+ ASSERT_EQ(i.contentSize, i2.contentSize);
ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
ASSERT_EQ(i.alpha, i2.alpha);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index c37db16..dd8dc8d 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -50,7 +50,7 @@
// overrides for flags, without having to link against a separate version of libinput or of this
// library. Bundling this library directly into libinput prevents us from having to add this
// library as a shared lib dependency everywhere where libinput is used.
- test: true,
+ mode: "test",
shared: {
enabled: false,
},
diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index 9a459b1..db7031a 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -193,6 +193,7 @@
{AKEYCODE_NUMPAD_ENTER, KEY_KPENTER},
{AKEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL},
{AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA},
+ {AKEYCODE_LANGUAGE_SWITCH, KEY_LANGUAGE},
};
VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
VirtualKeyboard::~VirtualKeyboard() {}
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 3672387..a807d82 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -62,3 +62,10 @@
description: "Disable touch rejection when the stylus hovers the screen"
bug: "301216095"
}
+
+flag {
+ name: "enable_input_filter_rust_impl"
+ namespace: "input"
+ description: "Enable input filter rust implementation"
+ bug: "294546335"
+}
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index 5f05a0f..b60d7c9 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -124,10 +124,7 @@
self.name, device_id, self.touching_pointer_ids_by_device
));
}
- let it = self
- .touching_pointer_ids_by_device
- .entry(device_id)
- .or_insert_with(HashSet::new);
+ let it = self.touching_pointer_ids_by_device.entry(device_id).or_default();
it.insert(pointer_properties[0].id);
}
MotionAction::PointerDown { action_index } => {
@@ -224,19 +221,13 @@
self.name, device_id, self.hovering_pointer_ids_by_device
));
}
- let it = self
- .hovering_pointer_ids_by_device
- .entry(device_id)
- .or_insert_with(HashSet::new);
+ let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default();
it.insert(pointer_properties[0].id);
}
MotionAction::HoverMove => {
// For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
// If there was no prior HOVER_ENTER, just start a new hovering pointer.
- let it = self
- .hovering_pointer_ids_by_device
- .entry(device_id)
- .or_insert_with(HashSet::new);
+ let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default();
it.insert(pointer_properties[0].id);
}
MotionAction::HoverExit => {
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index 538c1d2..4246c40 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -363,9 +363,9 @@
}
void FenceToFenceTimeMap::garbageCollectLocked() {
- for (auto& it : mMap) {
+ for (auto it = mMap.begin(); it != mMap.end();) {
// Erase all expired weak pointers from the vector.
- auto& vect = it.second;
+ auto& vect = it->second;
vect.erase(
std::remove_if(vect.begin(), vect.end(),
[](const std::weak_ptr<FenceTime>& ft) {
@@ -375,7 +375,9 @@
// Also erase the map entry if the vector is now empty.
if (vect.empty()) {
- mMap.erase(it.first);
+ it = mMap.erase(it);
+ } else {
+ it++;
}
}
}
diff --git a/opengl/Android.bp b/opengl/Android.bp
index b15694b..4454f36 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -72,6 +72,10 @@
llndk: {
llndk_headers: true,
},
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
}
subdirs = [
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp
index ed3c616..9905210 100644
--- a/opengl/libs/EGL/MultifileBlobCache.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache.cpp
@@ -18,6 +18,7 @@
#include "MultifileBlobCache.h"
+#include <android-base/properties.h>
#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -62,12 +63,15 @@
namespace android {
MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
- const std::string& baseDir)
+ size_t maxTotalEntries, const std::string& baseDir)
: mInitialized(false),
+ mCacheVersion(0),
mMaxKeySize(maxKeySize),
mMaxValueSize(maxValueSize),
mMaxTotalSize(maxTotalSize),
+ mMaxTotalEntries(maxTotalEntries),
mTotalCacheSize(0),
+ mTotalCacheEntries(0),
mHotCacheLimit(0),
mHotCacheSize(0),
mWorkerThreadIdle(true) {
@@ -76,6 +80,26 @@
return;
}
+ // Set the cache version, override if debug value set
+ mCacheVersion = kMultifileBlobCacheVersion;
+ int debugCacheVersion = base::GetIntProperty("debug.egl.blobcache.cache_version", -1);
+ if (debugCacheVersion >= 0) {
+ ALOGV("INIT: Using %u as cacheVersion instead of %u", debugCacheVersion, mCacheVersion);
+ mCacheVersion = debugCacheVersion;
+ }
+
+ // Set the platform build ID, override if debug value set
+ mBuildId = base::GetProperty("ro.build.id", "");
+ std::string debugBuildId = base::GetProperty("debug.egl.blobcache.build_id", "");
+ if (!debugBuildId.empty()) {
+ ALOGV("INIT: Using %s as buildId instead of %s", debugBuildId.c_str(), mBuildId.c_str());
+ if (debugBuildId.length() > PROP_VALUE_MAX) {
+ ALOGV("INIT: debugBuildId is too long (%zu), reduce it to %u", debugBuildId.length(),
+ PROP_VALUE_MAX);
+ }
+ mBuildId = debugBuildId;
+ }
+
// Establish the name of our multifile directory
mMultifileDirName = baseDir + ".multifile";
@@ -93,14 +117,30 @@
mTaskThread = std::thread(&MultifileBlobCache::processTasks, this);
// See if the dir exists, and initialize using its contents
+ bool statusGood = false;
+
+ // Check that our cacheVersion and buildId match
struct stat st;
if (stat(mMultifileDirName.c_str(), &st) == 0) {
+ if (checkStatus(mMultifileDirName.c_str())) {
+ statusGood = true;
+ } else {
+ ALOGV("INIT: Cache status has changed, clearing the cache");
+ if (!clearCache()) {
+ ALOGE("INIT: Unable to clear cache");
+ return;
+ }
+ }
+ }
+
+ if (statusGood) {
// Read all the files and gather details, then preload their contents
DIR* dir;
struct dirent* entry;
if ((dir = opendir(mMultifileDirName.c_str())) != nullptr) {
while ((entry = readdir(dir)) != nullptr) {
- if (entry->d_name == "."s || entry->d_name == ".."s) {
+ if (entry->d_name == "."s || entry->d_name == ".."s ||
+ strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
continue;
}
@@ -123,7 +163,8 @@
if (st.st_size <= 0 || st.st_atime <= 0) {
ALOGE("INIT: Entry %u has invalid stats! Removing.", entryHash);
if (remove(fullPath.c_str()) != 0) {
- ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
+ std::strerror(errno));
}
continue;
}
@@ -140,7 +181,7 @@
MultifileHeader header;
size_t result = read(fd, static_cast<void*>(&header), sizeof(MultifileHeader));
if (result != sizeof(MultifileHeader)) {
- ALOGE("Error reading MultifileHeader from cache entry (%s): %s",
+ ALOGE("INIT: Error reading MultifileHeader from cache entry (%s): %s",
fullPath.c_str(), std::strerror(errno));
close(fd);
return;
@@ -150,7 +191,8 @@
if (header.magic != kMultifileMagic) {
ALOGE("INIT: Entry %u has bad magic (%u)! Removing.", entryHash, header.magic);
if (remove(fullPath.c_str()) != 0) {
- ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
+ std::strerror(errno));
}
close(fd);
continue;
@@ -175,7 +217,7 @@
if (header.crc !=
crc32c(mappedEntry + sizeof(MultifileHeader),
fileSize - sizeof(MultifileHeader))) {
- ALOGE("INIT: Entry %u failed CRC check! Removing.", entryHash);
+ ALOGV("INIT: Entry %u failed CRC check! Removing.", entryHash);
if (remove(fullPath.c_str()) != 0) {
ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
}
@@ -184,11 +226,12 @@
// If the cache entry is damaged or no good, remove it
if (header.keySize <= 0 || header.valueSize <= 0) {
- ALOGE("INIT: Entry %u has a bad header keySize (%lu) or valueSize (%lu), "
+ ALOGV("INIT: Entry %u has a bad header keySize (%lu) or valueSize (%lu), "
"removing.",
entryHash, header.keySize, header.valueSize);
if (remove(fullPath.c_str()) != 0) {
- ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
+ std::strerror(errno));
}
continue;
}
@@ -226,9 +269,17 @@
// If the multifile directory does not exist, create it and start from scratch
if (mkdir(mMultifileDirName.c_str(), 0755) != 0 && (errno != EEXIST)) {
ALOGE("Unable to create directory (%s), errno (%i)", mMultifileDirName.c_str(), errno);
+ return;
+ }
+
+ // Create new status file
+ if (!createStatus(mMultifileDirName.c_str())) {
+ ALOGE("INIT: Failed to create status file!");
+ return;
}
}
+ ALOGV("INIT: Multifile BlobCache initialization succeeded");
mInitialized = true;
}
@@ -270,7 +321,7 @@
size_t fileSize = sizeof(MultifileHeader) + keySize + valueSize;
// If we're going to be over the cache limit, kick off a trim to clear space
- if (getTotalSize() + fileSize > mMaxTotalSize) {
+ if (getTotalSize() + fileSize > mMaxTotalSize || getTotalEntries() + 1 > mMaxTotalEntries) {
ALOGV("SET: Cache is full, calling trimCache to clear space");
trimCache();
}
@@ -469,6 +520,112 @@
}
}
+bool MultifileBlobCache::createStatus(const std::string& baseDir) {
+ // Populate the status struct
+ struct MultifileStatus status;
+ memset(&status, 0, sizeof(status));
+ status.magic = kMultifileMagic;
+ status.cacheVersion = mCacheVersion;
+
+ // Copy the buildId string in, up to our allocated space
+ strncpy(status.buildId, mBuildId.c_str(),
+ mBuildId.length() > PROP_VALUE_MAX ? PROP_VALUE_MAX : mBuildId.length());
+
+ // Finally update the crc, using cacheVersion and everything the follows
+ status.crc =
+ crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion),
+ sizeof(status) - offsetof(MultifileStatus, cacheVersion));
+
+ // Create the status file
+ std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile;
+ int fd = open(cacheStatus.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ ALOGE("STATUS(CREATE): Unable to create status file: %s, error: %s", cacheStatus.c_str(),
+ std::strerror(errno));
+ return false;
+ }
+
+ // Write the buffer contents to disk
+ ssize_t result = write(fd, &status, sizeof(status));
+ close(fd);
+ if (result != sizeof(status)) {
+ ALOGE("STATUS(CREATE): Error writing cache status file: %s, error %s", cacheStatus.c_str(),
+ std::strerror(errno));
+ return false;
+ }
+
+ ALOGV("STATUS(CREATE): Created status file: %s", cacheStatus.c_str());
+ return true;
+}
+
+bool MultifileBlobCache::checkStatus(const std::string& baseDir) {
+ std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile;
+
+ // Does status exist
+ struct stat st;
+ if (stat(cacheStatus.c_str(), &st) != 0) {
+ ALOGV("STATUS(CHECK): Status file (%s) missing", cacheStatus.c_str());
+ return false;
+ }
+
+ // If the status entry is damaged or no good, remove it
+ if (st.st_size <= 0 || st.st_atime <= 0) {
+ ALOGE("STATUS(CHECK): Cache status has invalid stats!");
+ return false;
+ }
+
+ // Open the file so we can read its header
+ int fd = open(cacheStatus.c_str(), O_RDONLY);
+ if (fd == -1) {
+ ALOGE("STATUS(CHECK): Cache error - failed to open cacheStatus: %s, error: %s",
+ cacheStatus.c_str(), std::strerror(errno));
+ return false;
+ }
+
+ // Read in the status header
+ MultifileStatus status;
+ size_t result = read(fd, static_cast<void*>(&status), sizeof(MultifileStatus));
+ close(fd);
+ if (result != sizeof(MultifileStatus)) {
+ ALOGE("STATUS(CHECK): Error reading cache status (%s): %s", cacheStatus.c_str(),
+ std::strerror(errno));
+ return false;
+ }
+
+ // Verify header magic
+ if (status.magic != kMultifileMagic) {
+ ALOGE("STATUS(CHECK): Cache status has bad magic (%u)!", status.magic);
+ return false;
+ }
+
+ // Ensure we have a good CRC
+ if (status.crc !=
+ crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion),
+ sizeof(status) - offsetof(MultifileStatus, cacheVersion))) {
+ ALOGE("STATUS(CHECK): Cache status failed CRC check!");
+ return false;
+ }
+
+ // Check cacheVersion
+ if (status.cacheVersion != mCacheVersion) {
+ ALOGV("STATUS(CHECK): Cache version has changed! old(%u) new(%u)", status.cacheVersion,
+ mCacheVersion);
+ return false;
+ }
+
+ // Check buildId
+ if (strcmp(status.buildId, mBuildId.c_str()) != 0) {
+ ALOGV("STATUS(CHECK): BuildId has changed! old(%s) new(%s)", status.buildId,
+ mBuildId.c_str());
+ return false;
+ }
+
+ // All checks passed!
+ ALOGV("STATUS(CHECK): Status file is good! cacheVersion(%u), buildId(%s) file(%s)",
+ status.cacheVersion, status.buildId, cacheStatus.c_str());
+ return true;
+}
+
void MultifileBlobCache::trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize,
time_t accessTime) {
mEntries.insert(entryHash);
@@ -485,10 +642,12 @@
void MultifileBlobCache::increaseTotalCacheSize(size_t fileSize) {
mTotalCacheSize += fileSize;
+ mTotalCacheEntries++;
}
void MultifileBlobCache::decreaseTotalCacheSize(size_t fileSize) {
mTotalCacheSize -= fileSize;
+ mTotalCacheEntries--;
}
bool MultifileBlobCache::addToHotCache(uint32_t newEntryHash, int newFd, uint8_t* newEntryBuffer,
@@ -557,7 +716,7 @@
return false;
}
-bool MultifileBlobCache::applyLRU(size_t cacheLimit) {
+bool MultifileBlobCache::applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit) {
// Walk through our map of sorted last access times and remove files until under the limit
for (auto cacheEntryIter = mEntryStats.begin(); cacheEntryIter != mEntryStats.end();) {
uint32_t entryHash = cacheEntryIter->first;
@@ -590,9 +749,10 @@
// See if it has been reduced enough
size_t totalCacheSize = getTotalSize();
- if (totalCacheSize <= cacheLimit) {
+ size_t totalCacheEntries = getTotalEntries();
+ if (totalCacheSize <= cacheSizeLimit && totalCacheEntries <= cacheEntryLimit) {
// Success
- ALOGV("LRU: Reduced cache to %zu", totalCacheSize);
+ ALOGV("LRU: Reduced cache to size %zu entries %zu", totalCacheSize, totalCacheEntries);
return true;
}
}
@@ -601,8 +761,43 @@
return false;
}
+// Clear the cache by removing all entries and deleting the directory
+bool MultifileBlobCache::clearCache() {
+ DIR* dir;
+ struct dirent* entry;
+ dir = opendir(mMultifileDirName.c_str());
+ if (dir == nullptr) {
+ ALOGE("CLEAR: Unable to open multifile dir: %s", mMultifileDirName.c_str());
+ return false;
+ }
+
+ // Delete all entries and the status file
+ while ((entry = readdir(dir)) != nullptr) {
+ if (entry->d_name == "."s || entry->d_name == ".."s) {
+ continue;
+ }
+
+ std::string entryName = entry->d_name;
+ std::string fullPath = mMultifileDirName + "/" + entryName;
+ if (remove(fullPath.c_str()) != 0) {
+ ALOGE("CLEAR: Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ return false;
+ }
+ }
+
+ // Delete the directory
+ if (remove(mMultifileDirName.c_str()) != 0) {
+ ALOGE("CLEAR: Error removing %s: %s", mMultifileDirName.c_str(), std::strerror(errno));
+ return false;
+ }
+
+ ALOGV("CLEAR: Cleared the multifile blobcache");
+ return true;
+}
+
// When removing files, what fraction of the overall limit should be reached when removing files
// A divisor of two will decrease the cache to 50%, four to 25% and so on
+// We use the same limit to manage size and entry count
constexpr uint32_t kCacheLimitDivisor = 2;
// Calculate the cache size and remove old entries until under the limit
@@ -611,8 +806,9 @@
ALOGV("TRIM: Waiting for work to complete.");
waitForWorkComplete();
- ALOGV("TRIM: Reducing multifile cache size to %zu", mMaxTotalSize / kCacheLimitDivisor);
- if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor)) {
+ ALOGV("TRIM: Reducing multifile cache size to %zu, entries %zu",
+ mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor);
+ if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor)) {
ALOGE("Error when clearing multifile shader cache");
return;
}
diff --git a/opengl/libs/EGL/MultifileBlobCache.h b/opengl/libs/EGL/MultifileBlobCache.h
index 5e527dc..18566c2 100644
--- a/opengl/libs/EGL/MultifileBlobCache.h
+++ b/opengl/libs/EGL/MultifileBlobCache.h
@@ -21,6 +21,7 @@
#include <EGL/eglext.h>
#include <android-base/thread_annotations.h>
+#include <cutils/properties.h>
#include <future>
#include <map>
#include <queue>
@@ -33,6 +34,9 @@
namespace android {
+constexpr uint32_t kMultifileBlobCacheVersion = 1;
+constexpr char kMultifileBlobCacheStatusFile[] = "cache.status";
+
struct MultifileHeader {
uint32_t magic;
uint32_t crc;
@@ -46,6 +50,13 @@
time_t accessTime;
};
+struct MultifileStatus {
+ uint32_t magic;
+ uint32_t crc;
+ uint32_t cacheVersion;
+ char buildId[PROP_VALUE_MAX];
+};
+
struct MultifileHotCache {
int entryFd;
uint8_t* entryBuffer;
@@ -92,7 +103,7 @@
class MultifileBlobCache {
public:
MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
- const std::string& baseDir);
+ size_t maxTotalEntries, const std::string& baseDir);
~MultifileBlobCache();
void set(const void* key, EGLsizeiANDROID keySize, const void* value,
@@ -103,6 +114,13 @@
void finish();
size_t getTotalSize() const { return mTotalCacheSize; }
+ size_t getTotalEntries() const { return mTotalCacheEntries; }
+
+ const std::string& getCurrentBuildId() const { return mBuildId; }
+ void setCurrentBuildId(const std::string& buildId) { mBuildId = buildId; }
+
+ uint32_t getCurrentCacheVersion() const { return mCacheVersion; }
+ void setCurrentCacheVersion(uint32_t cacheVersion) { mCacheVersion = cacheVersion; }
private:
void trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize,
@@ -111,6 +129,9 @@
bool removeEntry(uint32_t entryHash);
MultifileEntryStats getEntryStats(uint32_t entryHash);
+ bool createStatus(const std::string& baseDir);
+ bool checkStatus(const std::string& baseDir);
+
size_t getFileSize(uint32_t entryHash);
size_t getValueSize(uint32_t entryHash);
@@ -120,12 +141,16 @@
bool addToHotCache(uint32_t entryHash, int fd, uint8_t* entryBufer, size_t entrySize);
bool removeFromHotCache(uint32_t entryHash);
+ bool clearCache();
void trimCache();
- bool applyLRU(size_t cacheLimit);
+ bool applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit);
bool mInitialized;
std::string mMultifileDirName;
+ std::string mBuildId;
+ uint32_t mCacheVersion;
+
std::unordered_set<uint32_t> mEntries;
std::unordered_map<uint32_t, MultifileEntryStats> mEntryStats;
std::unordered_map<uint32_t, MultifileHotCache> mHotCache;
@@ -133,7 +158,9 @@
size_t mMaxKeySize;
size_t mMaxValueSize;
size_t mMaxTotalSize;
+ size_t mMaxTotalEntries;
size_t mTotalCacheSize;
+ size_t mTotalCacheEntries;
size_t mHotCacheLimit;
size_t mHotCacheEntryLimit;
size_t mHotCacheSize;
diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp
index 1639be6..90a0f1e 100644
--- a/opengl/libs/EGL/MultifileBlobCache_test.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp
@@ -16,13 +16,17 @@
#include "MultifileBlobCache.h"
+#include <android-base/properties.h>
#include <android-base/test_utils.h>
#include <fcntl.h>
#include <gtest/gtest.h>
#include <stdio.h>
+#include <fstream>
#include <memory>
+using namespace std::literals;
+
namespace android {
template <typename T>
@@ -31,23 +35,40 @@
constexpr size_t kMaxKeySize = 2 * 1024;
constexpr size_t kMaxValueSize = 6 * 1024;
constexpr size_t kMaxTotalSize = 32 * 1024;
+constexpr size_t kMaxTotalEntries = 64;
class MultifileBlobCacheTest : public ::testing::Test {
protected:
virtual void SetUp() {
+ clearProperties();
mTempFile.reset(new TemporaryFile());
mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize,
- &mTempFile->path[0]));
+ kMaxTotalEntries, &mTempFile->path[0]));
}
- virtual void TearDown() { mMBC.reset(); }
+ virtual void TearDown() {
+ clearProperties();
+ mMBC.reset();
+ }
int getFileDescriptorCount();
+ std::vector<std::string> getCacheEntries();
+
+ void clearProperties();
std::unique_ptr<TemporaryFile> mTempFile;
std::unique_ptr<MultifileBlobCache> mMBC;
};
+void MultifileBlobCacheTest::clearProperties() {
+ // Clear any debug properties used in the tests
+ base::SetProperty("debug.egl.blobcache.cache_version", "");
+ base::WaitForProperty("debug.egl.blobcache.cache_version", "");
+
+ base::SetProperty("debug.egl.blobcache.build_id", "");
+ base::WaitForProperty("debug.egl.blobcache.build_id", "");
+}
+
TEST_F(MultifileBlobCacheTest, CacheSingleValueSucceeds) {
unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mMBC->set("abcd", 4, "efgh", 4);
@@ -211,6 +232,23 @@
}
}
+TEST_F(MultifileBlobCacheTest, CacheMaxEntrySucceeds) {
+ // Fill the cache with max entries
+ int i = 0;
+ for (i = 0; i < kMaxTotalEntries; i++) {
+ mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i));
+ }
+
+ // Ensure it is full
+ ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries);
+
+ // Add another entry
+ mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i));
+
+ // Ensure total entries is cut in half + 1
+ ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries / 2 + 1);
+}
+
TEST_F(MultifileBlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
unsigned char buf[1] = {0xee};
mMBC->set("x", 1, "y", 1);
@@ -234,8 +272,7 @@
TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) {
// Populate the cache with a bunch of entries
- size_t kLargeNumberOfEntries = 1024;
- for (int i = 0; i < kLargeNumberOfEntries; i++) {
+ for (int i = 0; i < kMaxTotalEntries; i++) {
// printf("Caching: %i", i);
// Use the index as the key and value
@@ -247,27 +284,223 @@
}
// Ensure we don't have a bunch of open fds
- ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+ ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
// Close the cache so everything writes out
mMBC->finish();
mMBC.reset();
// Now open it again and ensure we still don't have a bunch of open fds
- mMBC.reset(
- new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &mTempFile->path[0]));
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
// Check after initialization
- ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+ ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
- for (int i = 0; i < kLargeNumberOfEntries; i++) {
+ for (int i = 0; i < kMaxTotalEntries; i++) {
int result = 0;
ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
ASSERT_EQ(i, result);
}
// And again after we've actually used it
- ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+ ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
+}
+
+std::vector<std::string> MultifileBlobCacheTest::getCacheEntries() {
+ std::string cachePath = &mTempFile->path[0];
+ std::string multifileDirName = cachePath + ".multifile";
+ std::vector<std::string> cacheEntries;
+
+ struct stat info;
+ if (stat(multifileDirName.c_str(), &info) == 0) {
+ // We have a multifile dir. Skip the status file and return the only entry.
+ DIR* dir;
+ struct dirent* entry;
+ if ((dir = opendir(multifileDirName.c_str())) != nullptr) {
+ while ((entry = readdir(dir)) != nullptr) {
+ if (entry->d_name == "."s || entry->d_name == ".."s) {
+ continue;
+ }
+ if (strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
+ continue;
+ }
+ cacheEntries.push_back(multifileDirName + "/" + entry->d_name);
+ }
+ } else {
+ printf("Unable to open %s, error: %s\n", multifileDirName.c_str(),
+ std::strerror(errno));
+ }
+ } else {
+ printf("Unable to stat %s, error: %s\n", multifileDirName.c_str(), std::strerror(errno));
+ }
+
+ return cacheEntries;
+}
+
+TEST_F(MultifileBlobCacheTest, CacheContainsStatus) {
+ struct stat info;
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // After INIT, cache should have a status
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure status lives after closing the cache
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+
+ // Open the cache again
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we still have a status
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+}
+
+// Verify missing cache status file causes cache the be cleared
+TEST_F(MultifileBlobCacheTest, MissingCacheStatusClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Delete the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+ remove(statusFile.str().c_str());
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify modified cache status file BEGIN causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, ModifiedCacheStatusBeginClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Modify the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // Stomp on the beginning of the cache file
+ const char* stomp = "BADF00D";
+ std::fstream fs(statusFile.str());
+ fs.seekp(0, std::ios_base::beg);
+ fs.write(stomp, strlen(stomp));
+ fs.flush();
+ fs.close();
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify modified cache status file END causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, ModifiedCacheStatusEndClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Modify the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // Stomp on the END of the cache status file, modifying its contents
+ const char* stomp = "BADF00D";
+ std::fstream fs(statusFile.str());
+ fs.seekp(-strlen(stomp), std::ios_base::end);
+ fs.write(stomp, strlen(stomp));
+ fs.flush();
+ fs.close();
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify mismatched cacheVersion causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, MismatchedCacheVersionClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Set a debug cacheVersion
+ std::string newCacheVersion = std::to_string(kMultifileBlobCacheVersion + 1);
+ ASSERT_TRUE(base::SetProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str()));
+ ASSERT_TRUE(
+ base::WaitForProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str()));
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify mismatched buildId causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, MismatchedBuildIdClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Set a debug buildId
+ base::SetProperty("debug.egl.blobcache.build_id", "foo");
+ base::WaitForProperty("debug.egl.blobcache.build_id", "foo");
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
}
} // namespace android
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index 1b68344..98ff1d1 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -41,6 +41,7 @@
constexpr uint32_t kMaxMultifileKeySize = 1 * 1024 * 1024;
constexpr uint32_t kMaxMultifileValueSize = 8 * 1024 * 1024;
constexpr uint32_t kMaxMultifileTotalSize = 32 * 1024 * 1024;
+constexpr uint32_t kMaxMultifileTotalEntries = 4 * 1024;
namespace android {
@@ -277,7 +278,7 @@
if (mMultifileBlobCache == nullptr) {
mMultifileBlobCache.reset(new MultifileBlobCache(kMaxMultifileKeySize,
kMaxMultifileValueSize, mCacheByteLimit,
- mFilename));
+ kMaxMultifileTotalEntries, mFilename));
}
return mMultifileBlobCache.get();
}
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 55a2682..1ada33e 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -64,11 +64,6 @@
return false;
}
-bool needsAndroidPEglMitigation() {
- static const int32_t vndk_version = base::GetIntProperty("ro.vndk.version", -1);
- return vndk_version <= 28;
-}
-
int egl_get_init_count(EGLDisplay dpy) {
egl_display_t* eglDisplay = egl_display_t::get(dpy);
return eglDisplay ? eglDisplay->getRefsCount() : 0;
@@ -365,14 +360,6 @@
if (len) {
// NOTE: we could avoid the copy if we had strnstr.
const std::string ext(start, len);
- // Mitigation for Android P vendor partitions: Adreno 530 driver shipped on
- // some Android P vendor partitions this extension under the draft KHR name,
- // but during Khronos review it was decided to demote it to EXT.
- if (needsAndroidPEglMitigation() && ext == "EGL_EXT_image_gl_colorspace" &&
- findExtension(disp.queryString.extensions, "EGL_KHR_image_gl_colorspace")) {
- mExtensionString.append("EGL_EXT_image_gl_colorspace ");
- }
-
if (findExtension(disp.queryString.extensions, ext.c_str(), len)) {
mExtensionString.append(ext + " ");
}
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 87c2176..867a117 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -39,7 +39,6 @@
struct egl_connection_t;
bool findExtension(const char* exts, const char* name, size_t nameLen = 0);
-bool needsAndroidPEglMitigation();
class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
static std::map<EGLDisplay, std::unique_ptr<egl_display_t>> displayMap;
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 440eb17..a6af713 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -1644,26 +1644,6 @@
const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_IMAGE_KHR;
- std::vector<AttrType> strippedAttribs;
- if (needsAndroidPEglMitigation()) {
- // Mitigation for Android P vendor partitions: eglImageCreateKHR should accept
- // EGL_GL_COLORSPACE_LINEAR_KHR, EGL_GL_COLORSPACE_SRGB_KHR and
- // EGL_GL_COLORSPACE_DEFAULT_EXT if EGL_EXT_image_gl_colorspace is supported,
- // but some drivers don't like the DEFAULT value and generate an error.
- for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
- if (attr[0] == EGL_GL_COLORSPACE_KHR &&
- dp->haveExtension("EGL_EXT_image_gl_colorspace")) {
- if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR &&
- attr[1] != EGL_GL_COLORSPACE_SRGB_KHR) {
- continue;
- }
- }
- strippedAttribs.push_back(attr[0]);
- strippedAttribs.push_back(attr[1]);
- }
- strippedAttribs.push_back(EGL_NONE);
- }
-
ContextRef _c(dp, ctx);
egl_context_t* const c = _c.get();
@@ -1671,8 +1651,7 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && eglCreateImageFunc) {
result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer,
- needsAndroidPEglMitigation() ? strippedAttribs.data()
- : attrib_list);
+ attrib_list);
}
return result;
}
diff --git a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp
index 633cc9c..2d9ee3a 100644
--- a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp
+++ b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp
@@ -28,6 +28,7 @@
constexpr size_t kMaxKeySize = 2 * 1024;
constexpr size_t kMaxValueSize = 6 * 1024;
constexpr size_t kMaxTotalSize = 32 * 1024;
+constexpr size_t kMaxTotalEntries = 64;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// To fuzz this, we're going to create a key/value pair from data
@@ -79,8 +80,8 @@
std::unique_ptr<MultifileBlobCache> mbc;
tempFile.reset(new TemporaryFile());
- mbc.reset(
- new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0]));
+ mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &tempFile->path[0]));
// With remaining data, select different paths below
int loopCount = 1;
uint8_t bumpCount = 0;
@@ -131,8 +132,8 @@
// Place the maxKey/maxValue twice
// The first will fit, the second will trigger hot cache trimming
tempFile.reset(new TemporaryFile());
- mbc.reset(
- new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0]));
+ mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &tempFile->path[0]));
uint8_t* buffer = new uint8_t[kMaxValueSize];
mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize);
mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize);
@@ -145,7 +146,7 @@
// overflow
tempFile.reset(new TemporaryFile());
mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, 2 * (kMaxKeySize + kMaxValueSize),
- &tempFile->path[0]));
+ kMaxTotalEntries, &tempFile->path[0]));
mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize);
mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize);
mbc->get(maxKey1.data(), kMaxKeySize, buffer, kMaxValueSize);
diff --git a/opengl/tests/EGLTest/egl_cache_test.cpp b/opengl/tests/EGLTest/egl_cache_test.cpp
index f81c68f..ce58182 100644
--- a/opengl/tests/EGLTest/egl_cache_test.cpp
+++ b/opengl/tests/EGLTest/egl_cache_test.cpp
@@ -114,25 +114,26 @@
struct stat info;
if (stat(multifileDirName.c_str(), &info) == 0) {
// Ensure we only have one file to manage
- int realFileCount = 0;
+ int entryFileCount = 0;
- // We have a multifile dir. Return the only real file in it.
+ // We have a multifile dir. Return the only entry file in it.
DIR* dir;
struct dirent* entry;
if ((dir = opendir(multifileDirName.c_str())) != nullptr) {
while ((entry = readdir(dir)) != nullptr) {
- if (entry->d_name == "."s || entry->d_name == ".."s) {
+ if (entry->d_name == "."s || entry->d_name == ".."s ||
+ strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
continue;
}
cachefileName = multifileDirName + "/" + entry->d_name;
- realFileCount++;
+ entryFileCount++;
}
} else {
printf("Unable to open %s, error: %s\n",
multifileDirName.c_str(), std::strerror(errno));
}
- if (realFileCount != 1) {
+ if (entryFileCount != 1) {
// If there was more than one real file in the directory, this
// violates test assumptions
cachefileName = "";
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index b213f9a..45c9b5c 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -76,6 +76,7 @@
srcs: [
"InputCommonConverter.cpp",
"InputDeviceMetricsCollector.cpp",
+ "InputFilter.cpp",
"InputProcessor.cpp",
"PointerChoreographer.cpp",
"PreferStylusOverTouchBlocker.cpp",
@@ -243,6 +244,9 @@
"Bug-115739809",
"StructLayout_test",
+ // jni
+ "libservices.core",
+
// rust targets
"libinput_rust_test",
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
new file mode 100644
index 0000000..1b8fad3
--- /dev/null
+++ b/services/inputflinger/InputFilter.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputFilter"
+
+#include "InputFilter.h"
+
+namespace android {
+
+using aidl::com::android::server::inputflinger::IInputFilter;
+using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+
+AidlKeyEvent notifyKeyArgsToKeyEvent(const NotifyKeyArgs& args) {
+ AidlKeyEvent event;
+ event.id = args.id;
+ event.eventTime = args.eventTime;
+ event.deviceId = args.deviceId;
+ event.source = args.source;
+ event.displayId = args.displayId;
+ event.policyFlags = args.policyFlags;
+ event.action = args.action;
+ event.flags = args.flags;
+ event.keyCode = args.keyCode;
+ event.scanCode = args.scanCode;
+ event.metaState = args.metaState;
+ event.downTime = args.downTime;
+ event.readTime = args.readTime;
+ return event;
+}
+
+NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
+ return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId, event.source,
+ event.displayId, event.policyFlags, event.action, event.flags,
+ event.keyCode, event.scanCode, event.metaState, event.downTime);
+}
+
+namespace {
+
+class RustCallbacks : public IInputFilter::BnInputFilterCallbacks {
+public:
+ RustCallbacks(InputListenerInterface& nextListener) : mNextListener(nextListener) {}
+ ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override {
+ mNextListener.notifyKey(keyEventToNotifyKeyArgs(event));
+ return ndk::ScopedAStatus::ok();
+ }
+
+private:
+ InputListenerInterface& mNextListener;
+};
+
+} // namespace
+
+InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust)
+ : mNextListener(listener), mCallbacks(ndk::SharedRefBase::make<RustCallbacks>(listener)) {
+ LOG_ALWAYS_FATAL_IF(!rust.createInputFilter(mCallbacks, &mInputFilterRust).isOk());
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust);
+}
+
+void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+ if (isFilterEnabled()) {
+ std::vector<int32_t> deviceIds;
+ for (auto info : args.inputDeviceInfos) {
+ deviceIds.push_back(info.getId());
+ }
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(deviceIds).isOk());
+ }
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyKey(const NotifyKeyArgs& args) {
+ if (!isFilterEnabled()) {
+ mNextListener.notifyKey(args);
+ return;
+ }
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
+}
+
+void InputFilter::notifyMotion(const NotifyMotionArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifySwitch(const NotifySwitchArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifySensor(const NotifySensorArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyVibratorState(const NotifyVibratorStateArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
+ mNextListener.notify(args);
+}
+
+bool InputFilter::isFilterEnabled() {
+ bool result;
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->isEnabled(&result).isOk());
+ return result;
+}
+
+void InputFilter::dump(std::string& dump) {
+ dump += "InputFilter:\n";
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h
new file mode 100644
index 0000000..699f3a0
--- /dev/null
+++ b/services/inputflinger/InputFilter.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
+#include "InputListener.h"
+#include "NotifyArgs.h"
+
+namespace android {
+
+/**
+ * The C++ component of InputFilter designed as a wrapper around the rust implementation.
+ */
+class InputFilterInterface : public InputListenerInterface {
+public:
+ /**
+ * This method may be called on any thread (usually by the input manager on a binder thread).
+ */
+ virtual void dump(std::string& dump) = 0;
+};
+
+class InputFilter : public InputFilterInterface {
+public:
+ using IInputFlingerRust = aidl::com::android::server::inputflinger::IInputFlingerRust;
+ using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter;
+ using IInputFilterCallbacks =
+ aidl::com::android::server::inputflinger::IInputFilter::IInputFilterCallbacks;
+
+ explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust&);
+ ~InputFilter() override = default;
+ void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+ void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
+ void notifyKey(const NotifyKeyArgs& args) override;
+ void notifyMotion(const NotifyMotionArgs& args) override;
+ void notifySwitch(const NotifySwitchArgs& args) override;
+ void notifySensor(const NotifySensorArgs& args) override;
+ void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
+ void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+ void dump(std::string& dump) override;
+
+private:
+ InputListenerInterface& mNextListener;
+ std::shared_ptr<IInputFilterCallbacks> mCallbacks;
+ std::shared_ptr<IInputFilter> mInputFilterRust;
+
+ bool isFilterEnabled();
+};
+
+} // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 92c65e1..8cf61f9 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -42,6 +42,7 @@
sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
+const bool ENABLE_INPUT_FILTER_RUST = input_flags::enable_input_filter_rust_impl();
int32_t exceptionCodeFromStatusT(status_t status) {
switch (status) {
@@ -118,6 +119,7 @@
* The event flow is via the "InputListener" interface, as follows:
* InputReader
* -> UnwantedInteractionBlocker
+ * -> InputFilter
* -> PointerChoreographer
* -> InputProcessor
* -> InputDeviceMetricsCollector
@@ -132,6 +134,12 @@
mTracingStages.emplace_back(
std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher));
+ if (ENABLE_INPUT_FILTER_RUST) {
+ mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust);
+ mTracingStages.emplace_back(
+ std::make_unique<TracedInputListener>("InputFilter", *mInputFilter));
+ }
+
if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
mCollector = std::make_unique<InputDeviceMetricsCollector>(*mTracingStages.back());
mTracingStages.emplace_back(
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 20b9fd5..aea7bd5 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -21,6 +21,7 @@
*/
#include "InputDeviceMetricsCollector.h"
+#include "InputFilter.h"
#include "InputProcessor.h"
#include "InputReaderBase.h"
#include "PointerChoreographer.h"
@@ -40,6 +41,7 @@
using android::os::BnInputFlinger;
+using aidl::com::android::server::inputflinger::IInputFilter;
using aidl::com::android::server::inputflinger::IInputFlingerRust;
namespace android {
@@ -137,6 +139,8 @@
std::unique_ptr<UnwantedInteractionBlockerInterface> mBlocker;
+ std::unique_ptr<InputFilterInterface> mInputFilter;
+
std::unique_ptr<PointerChoreographerInterface> mChoreographer;
std::unique_ptr<InputProcessorInterface> mProcessor;
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index e529bdd..f1faf69 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -31,6 +31,11 @@
args.pointerProperties[0].toolType == ToolType::MOUSE;
}
+bool isFromTouchpad(const NotifyMotionArgs& args) {
+ return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
+ args.pointerProperties[0].toolType == ToolType::FINGER;
+}
+
bool isHoverAction(int32_t action) {
return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
@@ -83,6 +88,8 @@
if (isFromMouse(args)) {
return processMouseEventLocked(args);
+ } else if (isFromTouchpad(args)) {
+ return processTouchpadEventLocked(args);
} else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
processStylusHoverEventLocked(args);
} else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
@@ -97,17 +104,7 @@
<< args.dump();
}
- const int32_t displayId = getTargetMouseDisplayLocked(args.displayId);
-
- // Get the mouse pointer controller for the display, or create one if it doesn't exist.
- auto [it, emplaced] =
- mMousePointersByDisplay.try_emplace(displayId,
- getMouseControllerConstructor(displayId));
- if (emplaced) {
- notifyPointerDisplayIdChangedLocked();
- }
-
- PointerControllerInterface& pc = *it->second;
+ auto [displayId, pc] = getDisplayIdAndMouseControllerLocked(args.displayId);
const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
@@ -124,6 +121,40 @@
return newArgs;
}
+NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
+ auto [displayId, pc] = getDisplayIdAndMouseControllerLocked(args.displayId);
+
+ NotifyMotionArgs newArgs(args);
+ newArgs.displayId = displayId;
+ if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) {
+ // This is a movement of the mouse pointer.
+ const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ pc.move(deltaX, deltaY);
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ const auto [x, y] = pc.getPosition();
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ newArgs.xCursorPosition = x;
+ newArgs.yCursorPosition = y;
+ } else {
+ // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ const auto [x, y] = pc.getPosition();
+ for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
+ newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
+ args.pointerCoords[i].getX() + x);
+ newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
+ args.pointerCoords[i].getY() + y);
+ }
+ newArgs.xCursorPosition = x;
+ newArgs.yCursorPosition = y;
+ }
+ return newArgs;
+}
+
/**
* When screen is touched, fade the mouse pointer on that display. We only call fade for
* ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
@@ -270,6 +301,21 @@
return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
}
+std::pair<int32_t, PointerControllerInterface&>
+PointerChoreographer::getDisplayIdAndMouseControllerLocked(int32_t associatedDisplayId) {
+ const int32_t displayId = getTargetMouseDisplayLocked(associatedDisplayId);
+
+ // Get the mouse pointer controller for the display, or create one if it doesn't exist.
+ auto [it, emplaced] =
+ mMousePointersByDisplay.try_emplace(displayId,
+ getMouseControllerConstructor(displayId));
+ if (emplaced) {
+ notifyPointerDisplayIdChangedLocked();
+ }
+
+ return {displayId, *it->second};
+}
+
InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
[deviceId](const auto& info) { return info.getId() == deviceId; });
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 26d2fef..90a0d3f 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -95,10 +95,13 @@
void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock);
const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock);
int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock);
+ std::pair<int32_t, PointerControllerInterface&> getDisplayIdAndMouseControllerLocked(
+ int32_t associatedDisplayId) REQUIRES(mLock);
InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+ NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processDeviceReset(const NotifyDeviceResetArgs& args);
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 0f62324..1e2b9b3a 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -67,6 +67,16 @@
const bool DEBUG_MODEL =
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Model", ANDROID_LOG_INFO);
+/**
+ * When multi-device input is enabled, we shouldn't use PreferStylusOverTouchBlocker at all.
+ * However, multi-device input has the following default behaviour: hovering stylus rejects touch.
+ * Therefore, if we want to disable that behaviour (and go back to a place where stylus down
+ * blocks touch, but hovering stylus doesn't interact with touch), we should just disable the entire
+ * multi-device input feature.
+ */
+const bool ENABLE_MULTI_DEVICE_INPUT = input_flags::enable_multi_device_input() &&
+ !input_flags::disable_reject_touch_on_stylus_hover();
+
// Category (=namespace) name for the input settings that are applied at boot time
static const char* INPUT_NATIVE_BOOT = "input_native_boot";
/**
@@ -347,7 +357,7 @@
ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args.dump().c_str());
{ // acquire lock
std::scoped_lock lock(mLock);
- if (input_flags::enable_multi_device_input()) {
+ if (ENABLE_MULTI_DEVICE_INPUT) {
notifyMotionLocked(args);
} else {
const std::vector<NotifyMotionArgs> processedArgs =
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
new file mode 100644
index 0000000..44f959e
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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 com.android.server.inputflinger;
+
+import com.android.server.inputflinger.KeyEvent;
+
+/**
+ * A local AIDL interface used as a foreign function interface (ffi) to
+ * filter input events.
+ *
+ * NOTE: Since we use this as a local interface, all processing happens on the
+ * calling thread.
+ */
+interface IInputFilter {
+
+ /** Callbacks for the rust InputFilter to call into C++. */
+ interface IInputFilterCallbacks {
+ /** Sends back a filtered key event */
+ void sendKeyEvent(in KeyEvent event);
+ }
+
+ /** Returns if InputFilter is enabled */
+ boolean isEnabled();
+
+ /** Notifies if a key event occurred */
+ void notifyKey(in KeyEvent event);
+
+ /** Notifies if any InputDevice list changed and provides the list of connected peripherals */
+ void notifyInputDevicesChanged(in int[] deviceIds);
+}
+
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
index 8e826fd..de852c0 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
@@ -16,6 +16,9 @@
package com.android.server.inputflinger;
+import com.android.server.inputflinger.IInputFilter;
+import com.android.server.inputflinger.IInputFilter.IInputFilterCallbacks;
+
/**
* A local AIDL interface used as a foreign function interface (ffi) to
* communicate with the Rust component of inputflinger.
@@ -31,4 +34,7 @@
interface IInputFlingerRustBootstrapCallback {
void onProvideInputFlingerRust(in IInputFlingerRust inputFlingerRust);
}
+
+ /** Create the rust implementation of InputFilter. */
+ IInputFilter createInputFilter(IInputFilterCallbacks callbacks);
}
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
new file mode 100644
index 0000000..e213221
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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 com.android.server.inputflinger;
+
+/**
+ * Analogous to Android's native KeyEvent / NotifyKeyArgs.
+ * Stores the basic information about Key events.
+ */
+parcelable KeyEvent {
+ int id;
+ int deviceId;
+ long downTime;
+ long readTime;
+ long eventTime;
+ int source;
+ int displayId;
+ int policyFlags;
+ int action;
+ int flags;
+ int keyCode;
+ int scanCode;
+ int metaState;
+}
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 2d1a22b..cc0d49c 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -139,15 +139,15 @@
source(source),
displayId(displayId),
action(action),
- flags(flags),
keyCode(keyCode),
scanCode(scanCode),
metaState(metaState),
- repeatCount(repeatCount),
downTime(downTime),
syntheticRepeat(false),
interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN),
- interceptKeyWakeupTime(0) {
+ interceptKeyWakeupTime(0),
+ flags(flags),
+ repeatCount(repeatCount) {
EventEntry::injectionState = std::move(injectionState);
}
@@ -276,7 +276,7 @@
volatile int32_t DispatchEntry::sNextSeqAtomic;
-DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry,
+DispatchEntry::DispatchEntry(std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> targetFlags,
const ui::Transform& transform, const ui::Transform& rawTransform,
float globalScaleFactor)
@@ -287,21 +287,15 @@
rawTransform(rawTransform),
globalScaleFactor(globalScaleFactor),
deliveryTime(0),
- resolvedAction(0),
resolvedFlags(0) {
switch (this->eventEntry->type) {
case EventEntry::Type::KEY: {
- const KeyEntry& keyEntry = static_cast<KeyEntry&>(*this->eventEntry);
- resolvedEventId = keyEntry.id;
- resolvedAction = keyEntry.action;
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*this->eventEntry);
resolvedFlags = keyEntry.flags;
-
break;
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<MotionEntry&>(*this->eventEntry);
- resolvedEventId = motionEntry.id;
- resolvedAction = motionEntry.action;
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*this->eventEntry);
resolvedFlags = motionEntry.flags;
break;
}
@@ -321,24 +315,9 @@
}
std::ostream& operator<<(std::ostream& out, const DispatchEntry& entry) {
- out << "DispatchEntry{resolvedAction=";
- switch (entry.eventEntry->type) {
- case EventEntry::Type::KEY: {
- out << KeyEvent::actionToString(entry.resolvedAction);
- break;
- }
- case EventEntry::Type::MOTION: {
- out << MotionEvent::actionToString(entry.resolvedAction);
- break;
- }
- default: {
- out << "<invalid, not a key or a motion>";
- break;
- }
- }
std::string transform;
entry.transform.dump(transform, "transform");
- out << ", resolvedFlags=" << entry.resolvedFlags
+ out << "DispatchEntry{resolvedFlags=" << entry.resolvedFlags
<< ", targetFlags=" << entry.targetFlags.string() << ", transform=" << transform
<< "} original: " << entry.eventEntry->getDescription();
return out;
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index d44a211..e2e13c3 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -50,7 +50,7 @@
uint32_t policyFlags;
std::shared_ptr<InjectionState> injectionState;
- bool dispatchInProgress; // initially false, set to true while dispatching
+ mutable bool dispatchInProgress; // initially false, set to true while dispatching
/**
* Injected keys are events from an external (probably untrusted) application
@@ -72,6 +72,8 @@
virtual std::string getDescription() const = 0;
EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
+ EventEntry(const EventEntry&) = delete;
+ EventEntry& operator=(const EventEntry&) = delete;
virtual ~EventEntry() = default;
};
@@ -119,11 +121,9 @@
uint32_t source;
int32_t displayId;
int32_t action;
- int32_t flags;
int32_t keyCode;
int32_t scanCode;
int32_t metaState;
- int32_t repeatCount;
nsecs_t downTime;
bool syntheticRepeat; // set to true for synthetic key repeats
@@ -134,8 +134,11 @@
CONTINUE,
TRY_AGAIN_LATER,
};
- InterceptKeyResult interceptKeyResult; // set based on the interception result
- nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
+ // These are special fields that may need to be modified while the event is being dispatched.
+ mutable InterceptKeyResult interceptKeyResult; // set based on the interception result
+ mutable nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
+ mutable int32_t flags;
+ mutable int32_t repeatCount;
KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
@@ -206,7 +209,7 @@
struct DispatchEntry {
const uint32_t seq; // unique sequence number, never 0
- std::shared_ptr<EventEntry> eventEntry; // the event to dispatch
+ std::shared_ptr<const EventEntry> eventEntry; // the event to dispatch
const ftl::Flags<InputTarget::Flags> targetFlags;
ui::Transform transform;
ui::Transform rawTransform;
@@ -217,12 +220,9 @@
// An ANR will be triggered if a response for this entry is not received by timeoutTime
nsecs_t timeoutTime;
- // Set to the resolved ID, action and flags when the event is enqueued.
- int32_t resolvedEventId;
- int32_t resolvedAction;
int32_t resolvedFlags;
- DispatchEntry(std::shared_ptr<EventEntry> eventEntry,
+ DispatchEntry(std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform,
const ui::Transform& rawTransform, float globalScaleFactor);
DispatchEntry(const DispatchEntry&) = delete;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 1958c35..7f4de7a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -295,9 +295,8 @@
}
dump.append(INDENT4);
dump += entry.eventEntry->getDescription();
- dump += StringPrintf(", seq=%" PRIu32 ", targetFlags=%s, resolvedAction=%d, age=%" PRId64
- "ms",
- entry.seq, entry.targetFlags.string().c_str(), entry.resolvedAction,
+ dump += StringPrintf(", seq=%" PRIu32 ", targetFlags=%s, age=%" PRId64 "ms", entry.seq,
+ entry.targetFlags.string().c_str(),
ns2ms(currentTime - entry.eventEntry->eventTime));
if (entry.deliveryTime != 0) {
// This entry was delivered, so add information on how long we've been waiting
@@ -354,7 +353,7 @@
}
std::unique_ptr<DispatchEntry> createDispatchEntry(
- const InputTarget& inputTarget, std::shared_ptr<EventEntry> eventEntry,
+ const InputTarget& inputTarget, std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> inputTargetFlags) {
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
@@ -455,10 +454,6 @@
bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, const Connection& connection) {
const EventEntry& eventEntry = *dispatchEntry.eventEntry;
const int32_t& inputEventId = eventEntry.id;
- if (inputEventId != dispatchEntry.resolvedEventId) {
- // Event was transmuted
- return false;
- }
if (inputEventId == android::os::IInputConstants::INVALID_INPUT_EVENT_ID) {
return false;
}
@@ -1031,8 +1026,8 @@
}
case EventEntry::Type::FOCUS: {
- std::shared_ptr<FocusEntry> typedEntry =
- std::static_pointer_cast<FocusEntry>(mPendingEvent);
+ std::shared_ptr<const FocusEntry> typedEntry =
+ std::static_pointer_cast<const FocusEntry>(mPendingEvent);
dispatchFocusLocked(currentTime, typedEntry);
done = true;
dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
@@ -1040,7 +1035,7 @@
}
case EventEntry::Type::TOUCH_MODE_CHANGED: {
- const auto typedEntry = std::static_pointer_cast<TouchModeEntry>(mPendingEvent);
+ const auto typedEntry = std::static_pointer_cast<const TouchModeEntry>(mPendingEvent);
dispatchTouchModeChangeLocked(currentTime, typedEntry);
done = true;
dropReason = DropReason::NOT_DROPPED; // touch mode events are never dropped
@@ -1049,22 +1044,23 @@
case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
const auto typedEntry =
- std::static_pointer_cast<PointerCaptureChangedEntry>(mPendingEvent);
+ std::static_pointer_cast<const PointerCaptureChangedEntry>(mPendingEvent);
dispatchPointerCaptureChangedLocked(currentTime, typedEntry, dropReason);
done = true;
break;
}
case EventEntry::Type::DRAG: {
- std::shared_ptr<DragEntry> typedEntry =
- std::static_pointer_cast<DragEntry>(mPendingEvent);
+ std::shared_ptr<const DragEntry> typedEntry =
+ std::static_pointer_cast<const DragEntry>(mPendingEvent);
dispatchDragLocked(currentTime, typedEntry);
done = true;
break;
}
case EventEntry::Type::KEY: {
- std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
+ std::shared_ptr<const KeyEntry> keyEntry =
+ std::static_pointer_cast<const KeyEntry>(mPendingEvent);
if (!REMOVE_APP_SWITCH_DROPS) {
if (isAppSwitchDue) {
if (isAppSwitchKeyEvent(*keyEntry)) {
@@ -1086,8 +1082,8 @@
}
case EventEntry::Type::MOTION: {
- std::shared_ptr<MotionEntry> motionEntry =
- std::static_pointer_cast<MotionEntry>(mPendingEvent);
+ std::shared_ptr<const MotionEntry> motionEntry =
+ std::static_pointer_cast<const MotionEntry>(mPendingEvent);
if (!REMOVE_APP_SWITCH_DROPS) {
if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
dropReason = DropReason::APP_SWITCH;
@@ -1104,8 +1100,8 @@
}
case EventEntry::Type::SENSOR: {
- std::shared_ptr<SensorEntry> sensorEntry =
- std::static_pointer_cast<SensorEntry>(mPendingEvent);
+ std::shared_ptr<const SensorEntry> sensorEntry =
+ std::static_pointer_cast<const SensorEntry>(mPendingEvent);
if (!REMOVE_APP_SWITCH_DROPS) {
if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
dropReason = DropReason::APP_SWITCH;
@@ -1200,7 +1196,7 @@
bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
bool needWake = mInboundQueue.empty();
mInboundQueue.push_back(std::move(newEntry));
- EventEntry& entry = *(mInboundQueue.back());
+ const EventEntry& entry = *(mInboundQueue.back());
traceInboundQueueLengthLocked();
switch (entry.type) {
@@ -1233,7 +1229,7 @@
// time for it may have been handled in the policy and could be dropped.
if (keyEntry.action == AKEY_EVENT_ACTION_UP && mPendingEvent &&
mPendingEvent->type == EventEntry::Type::KEY) {
- KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent);
+ const KeyEntry& pendingKey = static_cast<const KeyEntry&>(*mPendingEvent);
if (pendingKey.keyCode == keyEntry.keyCode &&
pendingKey.interceptKeyResult ==
KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {
@@ -1248,7 +1244,7 @@
case EventEntry::Type::MOTION: {
LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
"Unexpected untrusted event.");
- if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {
+ if (shouldPruneInboundQueueLocked(static_cast<const MotionEntry&>(entry))) {
mNextUnblockedEvent = mInboundQueue.back();
needWake = true;
}
@@ -1272,7 +1268,7 @@
return needWake;
}
-void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) {
+void InputDispatcher::addRecentEventLocked(std::shared_ptr<const EventEntry> entry) {
// Do not store sensor event in recent queue to avoid flooding the queue.
if (entry->type != EventEntry::Type::SENSOR) {
mRecentQueue.push_back(entry);
@@ -1473,7 +1469,7 @@
void InputDispatcher::drainInboundQueueLocked() {
while (!mInboundQueue.empty()) {
- std::shared_ptr<EventEntry> entry = mInboundQueue.front();
+ std::shared_ptr<const EventEntry> entry = mInboundQueue.front();
mInboundQueue.pop_front();
releaseInboundEventLocked(entry);
}
@@ -1487,7 +1483,7 @@
}
}
-void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
+void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<const EventEntry> entry) {
const std::shared_ptr<InjectionState>& injectionState = entry->injectionState;
if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
if (DEBUG_DISPATCH_CYCLE) {
@@ -1508,7 +1504,7 @@
}
std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
- std::shared_ptr<KeyEntry> entry = mKeyRepeatState.lastKeyEntry;
+ std::shared_ptr<const KeyEntry> entry = mKeyRepeatState.lastKeyEntry;
uint32_t policyFlags = entry->policyFlags &
(POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
@@ -1582,17 +1578,17 @@
// This event should go to the front of the queue, but behind all other focus events
// Find the last focus event, and insert right after it
- std::deque<std::shared_ptr<EventEntry>>::reverse_iterator it =
- std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
- [](const std::shared_ptr<EventEntry>& event) {
- return event->type == EventEntry::Type::FOCUS;
- });
+ auto it = std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
+ [](const std::shared_ptr<const EventEntry>& event) {
+ return event->type == EventEntry::Type::FOCUS;
+ });
// Maintain the order of focus events. Insert the entry after all other focus events.
mInboundQueue.insert(it.base(), std::move(focusEntry));
}
-void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry) {
+void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime,
+ std::shared_ptr<const FocusEntry> entry) {
std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
if (channel == nullptr) {
return; // Window has gone away
@@ -1609,7 +1605,7 @@
}
void InputDispatcher::dispatchPointerCaptureChangedLocked(
- nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
+ nsecs_t currentTime, const std::shared_ptr<const PointerCaptureChangedEntry>& entry,
DropReason& dropReason) {
dropReason = DropReason::NOT_DROPPED;
@@ -1680,8 +1676,8 @@
dropReason = DropReason::NOT_DROPPED;
}
-void InputDispatcher::dispatchTouchModeChangeLocked(nsecs_t currentTime,
- const std::shared_ptr<TouchModeEntry>& entry) {
+void InputDispatcher::dispatchTouchModeChangeLocked(
+ nsecs_t currentTime, const std::shared_ptr<const TouchModeEntry>& entry) {
const std::vector<sp<WindowInfoHandle>>& windowHandles =
getWindowHandlesLocked(entry->displayId);
if (windowHandles.empty()) {
@@ -1716,7 +1712,7 @@
return inputTargets;
}
-bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
+bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Preprocessing.
if (!entry->dispatchInProgress) {
@@ -1850,7 +1846,7 @@
}
void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime,
- const std::shared_ptr<SensorEntry>& entry,
+ const std::shared_ptr<const SensorEntry>& entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
@@ -1879,7 +1875,7 @@
std::scoped_lock _l(mLock);
for (auto it = mInboundQueue.begin(); it != mInboundQueue.end(); it++) {
- std::shared_ptr<EventEntry> entry = *it;
+ std::shared_ptr<const EventEntry> entry = *it;
if (entry->type == EventEntry::Type::SENSOR) {
it = mInboundQueue.erase(it);
releaseInboundEventLocked(entry);
@@ -1889,7 +1885,8 @@
return true;
}
-bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
+bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime,
+ std::shared_ptr<const MotionEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
ATRACE_CALL();
// Preprocessing.
@@ -1974,7 +1971,8 @@
enqueueInboundEventLocked(std::move(dragEntry));
}
-void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) {
+void InputDispatcher::dispatchDragLocked(nsecs_t currentTime,
+ std::shared_ptr<const DragEntry> entry) {
std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
if (channel == nullptr) {
return; // Window has gone away
@@ -2020,7 +2018,7 @@
}
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
- std::shared_ptr<EventEntry> eventEntry,
+ std::shared_ptr<const EventEntry> eventEntry,
const std::vector<InputTarget>& inputTargets) {
ATRACE_CALL();
if (DEBUG_DISPATCH_CYCLE) {
@@ -3214,7 +3212,7 @@
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry> eventEntry,
+ std::shared_ptr<const EventEntry> eventEntry,
const InputTarget& inputTarget) {
ATRACE_NAME_IF(ATRACE_ENABLED(),
StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=0x%" PRIx32 ")",
@@ -3281,7 +3279,7 @@
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry> eventEntry,
+ std::shared_ptr<const EventEntry> eventEntry,
const InputTarget& inputTarget) {
ATRACE_NAME_IF(ATRACE_ENABLED(),
StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=0x%" PRIx32 ")",
@@ -3312,7 +3310,7 @@
}
void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry> eventEntry,
+ std::shared_ptr<const EventEntry> eventEntry,
const InputTarget& inputTarget,
ftl::Flags<InputTarget::Flags> dispatchMode) {
ftl::Flags<InputTarget::Flags> inputTargetFlags = inputTarget.flags;
@@ -3330,13 +3328,12 @@
// Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
// different EventEntry than what was passed in.
- EventEntry& newEntry = *(dispatchEntry->eventEntry);
+ eventEntry = dispatchEntry->eventEntry;
// Apply target flags and update the connection's input state.
- switch (newEntry.type) {
+ switch (eventEntry->type) {
case EventEntry::Type::KEY: {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(newEntry);
- if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
- dispatchEntry->resolvedFlags)) {
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*eventEntry);
+ if (!connection->inputState.trackKey(keyEntry, keyEntry.flags)) {
LOG(WARNING) << "channel " << connection->getInputChannelName()
<< "~ dropping inconsistent event: " << *dispatchEntry;
return; // skip the inconsistent event
@@ -3345,58 +3342,90 @@
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(newEntry);
- // Assign a default value to dispatchEntry that will never be generated by InputReader,
- // and assign a InputDispatcher value if it doesn't change in the if-else chain below.
- constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =
- static_cast<int32_t>(IdGenerator::Source::OTHER);
- dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID;
- if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
- } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
- } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_ENTER)) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
- } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
- } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
- } else {
- dispatchEntry->resolvedEventId = motionEntry.id;
- }
- if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
- !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
- motionEntry.displayId)) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover "
- "enter event",
- connection->getInputChannelName().c_str());
- }
- // We keep the 'resolvedEventId' here equal to the original 'motionEntry.id' because
- // this is a one-to-one event conversion.
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
- }
+ std::shared_ptr<const MotionEntry> resolvedMotion =
+ std::static_pointer_cast<const MotionEntry>(eventEntry);
+ {
+ // Determine the resolved motion entry.
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+ int32_t resolvedAction = motionEntry.action;
+ int32_t resolvedFlags = motionEntry.flags;
- if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_CANCEL) {
- dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_CANCELED;
- }
- if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_OBSCURED)) {
- dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
- }
- if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED)) {
- dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
+ resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
+ } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) {
+ resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
+ } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_ENTER)) {
+ resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+ } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
+ resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
+ } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
+ resolvedAction = AMOTION_EVENT_ACTION_DOWN;
+ }
+ if (resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
+ !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
+ motionEntry.displayId)) {
+ if (DEBUG_DISPATCH_CYCLE) {
+ LOG(DEBUG) << "channel '" << connection->getInputChannelName().c_str()
+ << "' ~ enqueueDispatchEntryLocked: filling in missing hover "
+ "enter event";
+ }
+ resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+ }
+
+ if (resolvedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ resolvedFlags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_OBSCURED)) {
+ resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ }
+ if (dispatchEntry->targetFlags.test(
+ InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED)) {
+ resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ }
+
+ dispatchEntry->resolvedFlags = resolvedFlags;
+ if (resolvedAction != motionEntry.action) {
+ // Generate a new MotionEntry with a new eventId using the resolved action and
+ // flags.
+ resolvedMotion =
+ std::make_shared<MotionEntry>(mIdGenerator.nextId(),
+ motionEntry.injectionState,
+ motionEntry.eventTime,
+ motionEntry.deviceId, motionEntry.source,
+ motionEntry.displayId,
+ motionEntry.policyFlags, resolvedAction,
+ motionEntry.actionButton, resolvedFlags,
+ motionEntry.metaState,
+ motionEntry.buttonState,
+ motionEntry.classification,
+ motionEntry.edgeFlags,
+ motionEntry.xPrecision,
+ motionEntry.yPrecision,
+ motionEntry.xCursorPosition,
+ motionEntry.yCursorPosition,
+ motionEntry.downTime,
+ motionEntry.pointerProperties,
+ motionEntry.pointerCoords);
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32
+ ") to MotionEvent(id=0x%" PRIx32 ").",
+ motionEntry.id, resolvedMotion->id);
+ ATRACE_NAME(message.c_str());
+ }
+
+ // Set the resolved motion entry in the DispatchEntry.
+ dispatchEntry->eventEntry = resolvedMotion;
+ eventEntry = resolvedMotion;
+ }
}
// Check if we need to cancel any of the ongoing gestures. We don't support multiple
// devices being active at the same time in the same window, so if a new device is
// active, cancel the gesture from the old device.
-
std::unique_ptr<EventEntry> cancelEvent =
- connection->inputState
- .cancelConflictingInputStream(motionEntry,
- dispatchEntry->resolvedAction);
+ connection->inputState.cancelConflictingInputStream(*resolvedMotion);
if (cancelEvent != nullptr) {
- LOG(INFO) << "Canceling pointers for device " << motionEntry.deviceId << " in "
+ LOG(INFO) << "Canceling pointers for device " << resolvedMotion->deviceId << " in "
<< connection->getInputChannelName() << " with event "
<< cancelEvent->getDescription();
std::unique_ptr<DispatchEntry> cancelDispatchEntry =
@@ -3408,31 +3437,20 @@
connection->outboundQueue.emplace_back(std::move(cancelDispatchEntry));
}
- if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,
+ if (!connection->inputState.trackMotion(*resolvedMotion,
dispatchEntry->resolvedFlags)) {
LOG(WARNING) << "channel " << connection->getInputChannelName()
<< "~ dropping inconsistent event: " << *dispatchEntry;
return; // skip the inconsistent event
}
- dispatchEntry->resolvedEventId =
- dispatchEntry->resolvedEventId == DEFAULT_RESOLVED_EVENT_ID
- ? mIdGenerator.nextId()
- : motionEntry.id;
- if (ATRACE_ENABLED() && dispatchEntry->resolvedEventId != motionEntry.id) {
- std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32
- ") to MotionEvent(id=0x%" PRIx32 ").",
- motionEntry.id, dispatchEntry->resolvedEventId);
- ATRACE_NAME(message.c_str());
- }
-
- if ((motionEntry.flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) &&
- (motionEntry.policyFlags & POLICY_FLAG_TRUSTED)) {
+ if ((resolvedMotion->flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) &&
+ (resolvedMotion->policyFlags & POLICY_FLAG_TRUSTED)) {
// Skip reporting pointer down outside focus to the policy.
break;
}
- dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
+ dispatchPointerDownOutsideFocus(resolvedMotion->source, resolvedMotion->action,
inputTarget.inputChannel->getConnectionToken());
break;
@@ -3450,14 +3468,14 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
- ftl::enum_string(newEntry.type).c_str());
+ ftl::enum_string(eventEntry->type).c_str());
break;
}
}
// Remember that we are waiting for this dispatch to complete.
if (dispatchEntry->hasForegroundTarget()) {
- incrementPendingForegroundDispatches(newEntry);
+ incrementPendingForegroundDispatches(*eventEntry);
}
// Enqueue the dispatch entry.
@@ -3606,18 +3624,17 @@
// Publish the motion event.
return connection.inputPublisher
- .publishMotionEvent(dispatchEntry.seq, dispatchEntry.resolvedEventId,
- motionEntry.deviceId, motionEntry.source, motionEntry.displayId,
- std::move(hmac), dispatchEntry.resolvedAction,
- motionEntry.actionButton, dispatchEntry.resolvedFlags,
- motionEntry.edgeFlags, motionEntry.metaState,
- motionEntry.buttonState, motionEntry.classification,
- dispatchEntry.transform, motionEntry.xPrecision,
- motionEntry.yPrecision, motionEntry.xCursorPosition,
- motionEntry.yCursorPosition, dispatchEntry.rawTransform,
- motionEntry.downTime, motionEntry.eventTime,
- motionEntry.getPointerCount(), motionEntry.pointerProperties.data(),
- usingCoords);
+ .publishMotionEvent(dispatchEntry.seq, motionEntry.id, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId, std::move(hmac),
+ motionEntry.action, motionEntry.actionButton,
+ dispatchEntry.resolvedFlags, motionEntry.edgeFlags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, dispatchEntry.transform,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ dispatchEntry.rawTransform, motionEntry.downTime,
+ motionEntry.eventTime, motionEntry.getPointerCount(),
+ motionEntry.pointerProperties.data(), usingCoords);
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -3649,14 +3666,13 @@
// Publish the key event.
status = connection->inputPublisher
- .publishKeyEvent(dispatchEntry->seq,
- dispatchEntry->resolvedEventId, keyEntry.deviceId,
- keyEntry.source, keyEntry.displayId,
- std::move(hmac), dispatchEntry->resolvedAction,
- dispatchEntry->resolvedFlags, keyEntry.keyCode,
- keyEntry.scanCode, keyEntry.metaState,
- keyEntry.repeatCount, keyEntry.downTime,
- keyEntry.eventTime);
+ .publishKeyEvent(dispatchEntry->seq, keyEntry.id,
+ keyEntry.deviceId, keyEntry.source,
+ keyEntry.displayId, std::move(hmac),
+ keyEntry.action, dispatchEntry->resolvedFlags,
+ keyEntry.keyCode, keyEntry.scanCode,
+ keyEntry.metaState, keyEntry.repeatCount,
+ keyEntry.downTime, keyEntry.eventTime);
break;
}
@@ -3774,7 +3790,7 @@
const std::array<uint8_t, 32> InputDispatcher::getSignature(
const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
- const int32_t actionMasked = MotionEvent::getActionMasked(dispatchEntry.resolvedAction);
+ const int32_t actionMasked = MotionEvent::getActionMasked(motionEntry.action);
if (actionMasked != AMOTION_EVENT_ACTION_UP && actionMasked != AMOTION_EVENT_ACTION_DOWN) {
// Only sign events up and down events as the purely move events
// are tied to their up/down counterparts so signing would be redundant.
@@ -3792,7 +3808,6 @@
const KeyEntry& keyEntry, const DispatchEntry& dispatchEntry) const {
VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(keyEntry);
verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_KEY_EVENT_FLAGS;
- verifiedEvent.action = dispatchEntry.resolvedAction;
return sign(verifiedEvent);
}
@@ -4853,7 +4868,7 @@
return result;
}
-void InputDispatcher::setInjectionResult(EventEntry& entry,
+void InputDispatcher::setInjectionResult(const EventEntry& entry,
InputEventInjectionResult injectionResult) {
if (!entry.injectionState) {
// Not an injected event.
@@ -4914,13 +4929,13 @@
}
}
-void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
+void InputDispatcher::incrementPendingForegroundDispatches(const EventEntry& entry) {
if (entry.injectionState) {
entry.injectionState->pendingForegroundDispatches += 1;
}
}
-void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) {
+void InputDispatcher::decrementPendingForegroundDispatches(const EventEntry& entry) {
if (entry.injectionState) {
entry.injectionState->pendingForegroundDispatches -= 1;
@@ -5734,7 +5749,7 @@
// Dump recently dispatched or dropped events from oldest to newest.
if (!mRecentQueue.empty()) {
dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
- for (const std::shared_ptr<EventEntry>& entry : mRecentQueue) {
+ for (const std::shared_ptr<const EventEntry>& entry : mRecentQueue) {
dump += INDENT2;
dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
@@ -5757,7 +5772,7 @@
// Dump inbound events from oldest to newest.
if (!mInboundQueue.empty()) {
dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size());
- for (const std::shared_ptr<EventEntry>& entry : mInboundQueue) {
+ for (const std::shared_ptr<const EventEntry>& entry : mInboundQueue) {
dump += INDENT2;
dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
@@ -6144,7 +6159,7 @@
uint32_t seq, bool handled,
nsecs_t consumeTime) {
// Handle post-event policy actions.
- std::unique_ptr<KeyEntry> fallbackKeyEntry;
+ std::unique_ptr<const KeyEntry> fallbackKeyEntry;
{ // Start critical section
auto dispatchEntryIt =
@@ -6300,7 +6315,7 @@
}
void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
- KeyEntry& entry) {
+ const KeyEntry& entry) {
const KeyEvent event = createKeyEvent(entry);
nsecs_t delay = 0;
{ // release lock
@@ -6385,7 +6400,7 @@
sendWindowResponsiveCommandLocked(connectionToken, pid);
}
-std::unique_ptr<KeyEntry> InputDispatcher::afterKeyEventLockedInterruptable(
+std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptable(
const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
const KeyEntry& keyEntry, bool handled) {
if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
@@ -6927,11 +6942,12 @@
return nullptr;
}
-void InputDispatcher::setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
+void InputDispatcher::setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
+ std::chrono::nanoseconds delay) {
std::scoped_lock _l(mLock);
- mConfig.keyRepeatTimeout = timeout;
- mConfig.keyRepeatDelay = delay;
+ mConfig.keyRepeatTimeout = timeout.count();
+ mConfig.keyRepeatDelay = delay.count();
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e9d52cd..8aa4a87 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -145,7 +145,8 @@
// Public to allow tests to verify that a Monitor can get ANR.
void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout);
- void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) override;
+ void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
+ std::chrono::nanoseconds delay) override;
private:
enum class DropReason {
@@ -170,9 +171,9 @@
sp<Looper> mLooper;
- std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock);
- std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock);
- std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock);
+ std::shared_ptr<const EventEntry> mPendingEvent GUARDED_BY(mLock);
+ std::deque<std::shared_ptr<const EventEntry>> mInboundQueue GUARDED_BY(mLock);
+ std::deque<std::shared_ptr<const EventEntry>> mRecentQueue GUARDED_BY(mLock);
// A command entry captures state and behavior for an action to be performed in the
// dispatch loop after the initial processing has taken place. It is essentially
@@ -222,7 +223,7 @@
REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
- void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
+ void addRecentEventLocked(std::shared_ptr<const EventEntry> entry) REQUIRES(mLock);
// App switch latency optimization.
bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
@@ -234,7 +235,7 @@
// Blocked event latency optimization. Drops old events when the user intends
// to transfer focus to a new application.
- std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
+ std::shared_ptr<const EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
int32_t displayId, float x, float y, bool isStylus = false,
@@ -281,19 +282,19 @@
// Event injection and synchronization.
std::condition_variable mInjectionResultAvailable;
- void setInjectionResult(EventEntry& entry,
+ void setInjectionResult(const EventEntry& entry,
android::os::InputEventInjectionResult injectionResult);
void transformMotionEntryForInjectionLocked(MotionEntry&,
const ui::Transform& injectedTransform) const
REQUIRES(mLock);
std::condition_variable mInjectionSyncFinished;
- void incrementPendingForegroundDispatches(EventEntry& entry);
- void decrementPendingForegroundDispatches(EventEntry& entry);
+ void incrementPendingForegroundDispatches(const EventEntry& entry);
+ void decrementPendingForegroundDispatches(const EventEntry& entry);
// Key repeat tracking.
struct KeyRepeatState {
- std::shared_ptr<KeyEntry> lastKeyEntry; // or null if no repeat
+ std::shared_ptr<const KeyEntry> lastKeyEntry; // or null if no repeat
nsecs_t nextRepeatTime;
} mKeyRepeatState GUARDED_BY(mLock);
@@ -319,7 +320,7 @@
// Inbound event processing.
void drainInboundQueueLocked() REQUIRES(mLock);
void releasePendingEventLocked() REQUIRES(mLock);
- void releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
+ void releaseInboundEventLocked(std::shared_ptr<const EventEntry> entry) REQUIRES(mLock);
// Dispatch state.
bool mDispatchEnabled GUARDED_BY(mLock);
@@ -430,23 +431,24 @@
const ConfigurationChangedEntry& entry) REQUIRES(mLock);
bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry)
REQUIRES(mLock);
- bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
+ bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
- bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
+ bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<const MotionEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
- void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry)
+ void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<const FocusEntry> entry)
REQUIRES(mLock);
void dispatchPointerCaptureChangedLocked(
- nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
+ nsecs_t currentTime, const std::shared_ptr<const PointerCaptureChangedEntry>& entry,
DropReason& dropReason) REQUIRES(mLock);
void dispatchTouchModeChangeLocked(nsecs_t currentTime,
- const std::shared_ptr<TouchModeEntry>& entry)
+ const std::shared_ptr<const TouchModeEntry>& entry)
REQUIRES(mLock);
- void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
+ void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<const EventEntry> entry,
const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<SensorEntry>& entry,
+ void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<const SensorEntry>& entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
- void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
+ void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<const DragEntry> entry)
+ REQUIRES(mLock);
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
@@ -576,14 +578,15 @@
// If needed, the methods post commands to run later once the critical bits are done.
void prepareDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
- REQUIRES(mLock);
+ std::shared_ptr<const EventEntry>,
+ const InputTarget& inputTarget) REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
- REQUIRES(mLock);
+ std::shared_ptr<const EventEntry>,
+ const InputTarget& inputTarget) REQUIRES(mLock);
void enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry>, const InputTarget& inputTarget,
+ std::shared_ptr<const EventEntry>,
+ const InputTarget& inputTarget,
ftl::Flags<InputTarget::Flags> dispatchMode) REQUIRES(mLock);
status_t publishMotionEvent(Connection& connection, DispatchEntry& dispatchEntry) const;
void startDispatchCycleLocked(nsecs_t currentTime,
@@ -645,7 +648,7 @@
const std::shared_ptr<Connection>& connection, uint32_t seq,
bool handled, nsecs_t consumeTime) REQUIRES(mLock);
void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
- KeyEntry& entry) REQUIRES(mLock);
+ const KeyEntry& entry) REQUIRES(mLock);
void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
REQUIRES(mLock);
@@ -660,7 +663,7 @@
REQUIRES(mLock);
std::map<int32_t /*displayId*/, InputVerifier> mVerifiersByDisplay;
// Returns a fallback KeyEntry that should be sent to the connection, if required.
- std::unique_ptr<KeyEntry> afterKeyEventLockedInterruptable(
+ std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable(
const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
const KeyEntry& keyEntry, bool handled) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 16cc266..a4ac4fb 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -24,19 +24,6 @@
namespace android::inputdispatcher {
-namespace {
-bool isHoverAction(int32_t action) {
- switch (MotionEvent::getActionMasked(action)) {
- case AMOTION_EVENT_ACTION_HOVER_ENTER:
- case AMOTION_EVENT_ACTION_HOVER_MOVE:
- case AMOTION_EVENT_ACTION_HOVER_EXIT: {
- return true;
- }
- }
- return false;
-}
-} // namespace
-
InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {}
InputState::~InputState() {}
@@ -51,8 +38,8 @@
return false;
}
-bool InputState::trackKey(const KeyEntry& entry, int32_t action, int32_t flags) {
- switch (action) {
+bool InputState::trackKey(const KeyEntry& entry, int32_t flags) {
+ switch (entry.action) {
case AKEY_EVENT_ACTION_UP: {
if (entry.flags & AKEY_EVENT_FLAG_FALLBACK) {
std::erase_if(mFallbackKeys,
@@ -101,7 +88,7 @@
* true if the incoming event was correctly tracked,
* false if the incoming event should be dropped.
*/
-bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) {
+bool InputState::trackMotion(const MotionEntry& entry, int32_t flags) {
// Don't track non-pointer events
if (!isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) {
// This is a focus-dispatched event; we don't track its state.
@@ -113,18 +100,11 @@
if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) &&
!isStylusEvent(entry.source, entry.pointerProperties)) {
// We already have a stylus stream, and the new event is not from stylus.
- if (!lastMemento.hovering) {
- // If stylus is currently down, reject the new event unconditionally.
- return false;
- }
- }
- if (!lastMemento.hovering && isHoverAction(action)) {
- // Reject hovers if already down
return false;
}
}
- int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
+ int32_t actionMasked = entry.action & AMOTION_EVENT_ACTION_MASK;
switch (actionMasked) {
case AMOTION_EVENT_ACTION_UP:
case AMOTION_EVENT_ACTION_CANCEL: {
@@ -301,8 +281,7 @@
return pointerProperties.size();
}
-bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry,
- int32_t resolvedAction) const {
+bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry) const {
if (!isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER)) {
// This is a focus-dispatched event that should not affect the previous stream.
return false;
@@ -320,7 +299,7 @@
}
const MotionMemento& lastMemento = mMotionMementos.back();
- const int32_t actionMasked = MotionEvent::getActionMasked(resolvedAction);
+ const int32_t actionMasked = MotionEvent::getActionMasked(motionEntry.action);
// For compatibility, only one input device can be active at a time in the same window.
if (lastMemento.deviceId == motionEntry.deviceId) {
@@ -366,19 +345,13 @@
return false;
}
- // We want stylus down to block touch and other source types, but stylus hover should not
- // have such an effect.
- if (isHoverAction(motionEntry.action) && !lastMemento.hovering) {
- // New event is a hover. Keep the current non-hovering gesture instead
- return false;
- }
-
- if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) && !lastMemento.hovering) {
- // We have non-hovering stylus already active.
+ if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties)) {
+ // A stylus is already active.
if (isStylusEvent(motionEntry.source, motionEntry.pointerProperties) &&
actionMasked == AMOTION_EVENT_ACTION_DOWN) {
- // If this new event is a stylus from a different device going down, then cancel the old
- // stylus and allow the new stylus to take over
+ // If this new event is from a different device, then cancel the old
+ // stylus and allow the new stylus to take over, but only if it's going down.
+ // Otherwise, they will start to race each other.
return true;
}
@@ -395,9 +368,9 @@
return false;
}
-std::unique_ptr<EventEntry> InputState::cancelConflictingInputStream(const MotionEntry& motionEntry,
- int32_t resolvedAction) {
- if (!shouldCancelPreviousStream(motionEntry, resolvedAction)) {
+std::unique_ptr<EventEntry> InputState::cancelConflictingInputStream(
+ const MotionEntry& motionEntry) {
+ if (!shouldCancelPreviousStream(motionEntry)) {
return {};
}
@@ -407,7 +380,7 @@
std::unique_ptr<MotionEntry> cancelEntry =
createCancelEntryForMemento(memento, motionEntry.eventTime);
- if (!trackMotion(*cancelEntry, cancelEntry->action, cancelEntry->flags)) {
+ if (!trackMotion(*cancelEntry, cancelEntry->flags)) {
LOG(FATAL) << "Generated inconsistent cancel event!";
}
return cancelEntry;
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 686c432..b0e4209 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -41,16 +41,15 @@
// Records tracking information for a key event that has just been published.
// Returns true if the event should be delivered, false if it is inconsistent
// and should be skipped.
- bool trackKey(const KeyEntry& entry, int32_t action, int32_t flags);
+ bool trackKey(const KeyEntry& entry, int32_t flags);
// Records tracking information for a motion event that has just been published.
// Returns true if the event should be delivered, false if it is inconsistent
// and should be skipped.
- bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags);
+ bool trackMotion(const MotionEntry& entry, int32_t flags);
// Create cancel events for the previous stream if the current motionEntry requires it.
- std::unique_ptr<EventEntry> cancelConflictingInputStream(const MotionEntry& motionEntry,
- int32_t resolvedAction);
+ std::unique_ptr<EventEntry> cancelConflictingInputStream(const MotionEntry& motionEntry);
// Synthesizes cancelation events for the current state and resets the tracked state.
std::vector<std::unique_ptr<EventEntry>> synthesizeCancelationEvents(
@@ -127,7 +126,7 @@
static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options);
static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options);
- bool shouldCancelPreviousStream(const MotionEntry& motionEntry, int32_t resolvedAction) const;
+ bool shouldCancelPreviousStream(const MotionEntry& motionEntry) const;
std::unique_ptr<MotionEntry> createCancelEntryForMemento(const MotionMemento& memento,
nsecs_t eventTime) const;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index d099b44..bc7b644 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -221,7 +221,8 @@
/*
* Updates key repeat configuration timeout and delay.
*/
- virtual void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) = 0;
+ virtual void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
+ std::chrono::nanoseconds delay) = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 7aeb215..58e35a6 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -527,10 +527,8 @@
if (mEnablePointerChoreographer) {
// Always use DISPLAY_ID_NONE for mouse events.
// PointerChoreographer will make it target the correct the displayId later.
- const auto pointerViewport =
- getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
- mDisplayId = pointerViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt;
- resolvedViewport = pointerViewport;
+ resolvedViewport = getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
+ mDisplayId = resolvedViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt;
} else {
mDisplayId = mPointerController->getDisplayId();
if (auto v = config.getDisplayViewportById(*mDisplayId); v) {
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 9c87c62..2dd05f5 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -131,7 +131,7 @@
bumpGeneration();
}
}
- if (shouldSimulateStylusWithTouch() && outPointer.toolType == ToolType::FINGER) {
+ if (mShouldSimulateStylusWithTouch && outPointer.toolType == ToolType::FINGER) {
outPointer.toolType = ToolType::STYLUS;
}
@@ -177,6 +177,18 @@
mMultiTouchMotionAccumulator.finishSync();
}
+std::list<NotifyArgs> MultiTouchInputMapper::reconfigure(nsecs_t when,
+ const InputReaderConfiguration& config,
+ ConfigurationChanges changes) {
+ const bool simulateStylusWithTouch =
+ sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
+ if (simulateStylusWithTouch != mShouldSimulateStylusWithTouch) {
+ mShouldSimulateStylusWithTouch = simulateStylusWithTouch;
+ bumpGeneration();
+ }
+ return TouchInputMapper::reconfigure(when, config, changes);
+}
+
void MultiTouchInputMapper::configureRawPointerAxes() {
TouchInputMapper::configureRawPointerAxes();
@@ -211,14 +223,7 @@
bool MultiTouchInputMapper::hasStylus() const {
return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() ||
- shouldSimulateStylusWithTouch();
-}
-
-bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
- static const bool SIMULATE_STYLUS_WITH_TOUCH =
- sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
- return SIMULATE_STYLUS_WITH_TOUCH &&
- mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
+ mShouldSimulateStylusWithTouch;
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 1d788df..5c173f3 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -32,6 +32,9 @@
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
+ const InputReaderConfiguration& config,
+ ConfigurationChanges changes) override;
protected:
void syncTouch(nsecs_t when, RawState* outState) override;
@@ -41,13 +44,6 @@
private:
explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
- // simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
- // mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
- // It is used to simulate stylus events for debugging and testing on a device that does not
- // support styluses. It can be enabled using
- // "adb shell setprop persist.debug.input.simulate_stylus_with_touch true",
- // and requires a reboot to take effect.
- inline bool shouldSimulateStylusWithTouch() const;
// If the slot is in use, return the bit id. Return std::nullopt otherwise.
std::optional<int32_t> getActiveBitId(const MultiTouchMotionAccumulator::Slot& inSlot);
@@ -58,6 +54,15 @@
int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
bool mStylusMtToolSeen{false};
+
+ // simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
+ // mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
+ // It is used to simulate stylus events for debugging and testing on a device that does not
+ // support styluses. It can be enabled using
+ // "adb shell setprop debug.input.simulate_stylus_with_touch true".
+ // After enabling, the touchscreen will need to be reconfigured. A reconfiguration usually
+ // happens when turning the screen on/off or by rotating the device orientation.
+ bool mShouldSimulateStylusWithTouch{false};
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index c76fec7..34ca0b3 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -246,7 +246,8 @@
mStateConverter(deviceContext, mMotionAccumulator),
mGestureConverter(*getContext(), deviceContext, getDeviceId()),
mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()),
- mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) {
+ mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())),
+ mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {
RawAbsoluteAxisInfo slotAxisInfo;
deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
@@ -331,31 +332,56 @@
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
mDisplayId = ADISPLAY_ID_NONE;
- if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+ std::optional<DisplayViewport> resolvedViewport;
+ std::optional<FloatRect> boundsInLogicalDisplay;
+ if (auto assocViewport = mDeviceContext.getAssociatedViewport(); assocViewport) {
// This InputDevice is associated with a viewport.
// Only generate events for the associated display.
- const bool mismatchedPointerDisplay =
- (viewport->displayId != mPointerController->getDisplayId());
- if (mismatchedPointerDisplay) {
- ALOGW("Touchpad \"%s\" associated viewport display does not match pointer "
- "controller",
- mDeviceContext.getName().c_str());
+ mDisplayId = assocViewport->displayId;
+ resolvedViewport = *assocViewport;
+ if (!mEnablePointerChoreographer) {
+ const bool mismatchedPointerDisplay =
+ (assocViewport->displayId != mPointerController->getDisplayId());
+ if (mismatchedPointerDisplay) {
+ ALOGW("Touchpad \"%s\" associated viewport display does not match pointer "
+ "controller",
+ mDeviceContext.getName().c_str());
+ mDisplayId.reset();
+ }
}
- mDisplayId = mismatchedPointerDisplay ? std::nullopt
- : std::make_optional(viewport->displayId);
} else {
// The InputDevice is not associated with a viewport, but it controls the mouse pointer.
- mDisplayId = mPointerController->getDisplayId();
- }
-
- ui::Rotation orientation = ui::ROTATION_0;
- if (mDisplayId.has_value()) {
- if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) {
- orientation = getInverseRotation(viewport->orientation);
+ if (mEnablePointerChoreographer) {
+ // Always use DISPLAY_ID_NONE for touchpad events.
+ // PointerChoreographer will make it target the correct the displayId later.
+ resolvedViewport =
+ getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
+ mDisplayId = resolvedViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt;
+ } else {
+ mDisplayId = mPointerController->getDisplayId();
+ if (auto v = config.getDisplayViewportById(*mDisplayId); v) {
+ resolvedViewport = *v;
+ }
+ if (auto bounds = mPointerController->getBounds(); bounds) {
+ boundsInLogicalDisplay = *bounds;
+ }
}
}
+
mGestureConverter.setDisplayId(mDisplayId);
- mGestureConverter.setOrientation(orientation);
+ mGestureConverter.setOrientation(resolvedViewport
+ ? getInverseRotation(resolvedViewport->orientation)
+ : ui::ROTATION_0);
+
+ if (!boundsInLogicalDisplay) {
+ boundsInLogicalDisplay = resolvedViewport
+ ? FloatRect{static_cast<float>(resolvedViewport->logicalLeft),
+ static_cast<float>(resolvedViewport->logicalTop),
+ static_cast<float>(resolvedViewport->logicalRight - 1),
+ static_cast<float>(resolvedViewport->logicalBottom - 1)}
+ : FloatRect{0, 0, 0, 0};
+ }
+ mGestureConverter.setBoundsInLogicalDisplay(*boundsInLogicalDisplay);
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) {
mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve")
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index a68ae43..ece0eca 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -107,6 +107,8 @@
// Tracking IDs for touches that have at some point been reported as palms by the touchpad.
std::set<int32_t> mPalmTrackingIds;
+ const bool mEnablePointerChoreographer;
+
// The display that events generated by this mapper should target. This can be set to
// ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
// std::nullopt), all events will be ignored.
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 4d2b66d..9552104 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -60,7 +60,8 @@
const InputDeviceContext& deviceContext, int32_t deviceId)
: mDeviceId(deviceId),
mReaderContext(readerContext),
- mPointerController(readerContext.getPointerController(deviceId)) {
+ mPointerController(readerContext.getPointerController(deviceId)),
+ mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {
deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
}
@@ -110,9 +111,11 @@
// TODO(b/259547750): set this using the raw axis ranges from the touchpad when pointer capture
// is enabled.
- if (std::optional<FloatRect> rect = mPointerController->getBounds(); rect.has_value()) {
- info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, rect->left, rect->right, 0, 0, 0);
- info.addMotionRange(AMOTION_EVENT_AXIS_Y, SOURCE, rect->top, rect->bottom, 0, 0, 0);
+ if (!mBoundsInLogicalDisplay.isEmpty()) {
+ info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, mBoundsInLogicalDisplay.left,
+ mBoundsInLogicalDisplay.right, 0, 0, 0);
+ info.addMotionRange(AMOTION_EVENT_AXIS_Y, SOURCE, mBoundsInLogicalDisplay.top,
+ mBoundsInLogicalDisplay.bottom, 0, 0, 0);
}
info.addMotionRange(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, SOURCE, -1.0f, 1.0f, 0, 0, 0);
@@ -172,7 +175,8 @@
mPointerController->move(deltaX, deltaY);
mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
PointerCoords coords;
coords.clear();
@@ -196,7 +200,8 @@
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
PointerCoords coords;
coords.clear();
@@ -273,7 +278,8 @@
std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
PointerCoords coords;
coords.clear();
@@ -308,7 +314,8 @@
const Gesture& gesture) {
std::list<NotifyArgs> out;
PointerCoords& coords = mFakeFingerCoords[0];
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
@@ -376,7 +383,8 @@
}
NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
NotifyMotionArgs args =
@@ -394,7 +402,8 @@
float dx, float dy) {
std::list<NotifyArgs> out = {};
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
// If the user changes the number of fingers mid-way through a swipe (e.g. they start with
// three and then put a fourth finger down), the gesture library will treat it as two
@@ -457,7 +466,8 @@
if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
return out;
}
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
@@ -481,7 +491,8 @@
[[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime,
const Gesture& gesture) {
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
// Pinch gesture phases are reported a little differently from others, in that the same details
// struct is used for all phases of the gesture, just with different zoom_state values. When
@@ -538,7 +549,8 @@
std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
out.push_back(makeMotionArgs(when, readTime,
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index e6cf617..732a4b2 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -48,6 +48,8 @@
void setDisplayId(std::optional<int32_t> displayId) { mDisplayId = displayId; }
+ void setBoundsInLogicalDisplay(FloatRect bounds) { mBoundsInLogicalDisplay = bounds; }
+
void populateMotionRanges(InputDeviceInfo& info) const;
[[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
@@ -85,8 +87,10 @@
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
std::shared_ptr<PointerControllerInterface> mPointerController;
+ const bool mEnablePointerChoreographer;
std::optional<int32_t> mDisplayId;
+ FloatRect mBoundsInLogicalDisplay{};
ui::Rotation mOrientation = ui::ROTATION_0;
RawAbsoluteAxisInfo mXAxisInfo;
RawAbsoluteAxisInfo mYAxisInfo;
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
index 23c1691..2775bcc 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -31,8 +31,8 @@
out: ["inputflinger_bootstrap.rs.h"],
}
-rust_ffi_static {
- name: "libinputflinger_rs",
+rust_defaults {
+ name: "libinputflinger_rs_defaults",
crate_name: "inputflinger",
srcs: ["lib.rs"],
rustlibs: [
@@ -45,6 +45,24 @@
host_supported: true,
}
+rust_ffi_static {
+ name: "libinputflinger_rs",
+ defaults: ["libinputflinger_rs_defaults"],
+}
+
+rust_test {
+ name: "libinputflinger_rs_test",
+ defaults: ["libinputflinger_rs_defaults"],
+ test_options: {
+ unit_test: true,
+ },
+ test_suites: ["device_tests"],
+ sanitize: {
+ address: true,
+ hwaddress: true,
+ },
+}
+
cc_library_headers {
name: "inputflinger_rs_bootstrap_cxx_headers",
host_supported: true,
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
new file mode 100644
index 0000000..5851877
--- /dev/null
+++ b/services/inputflinger/rust/input_filter.rs
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+//! InputFilter manages all the filtering components that can intercept events, modify the events,
+//! block events, etc depending on the situation. This will be used support Accessibility features
+//! like Slow keys, Bounce keys, etc.
+
+use binder::{Interface, Strong};
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ IInputFilter::{IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks},
+ KeyEvent::KeyEvent,
+};
+
+/// The rust implementation of InputFilter
+pub struct InputFilter {
+ callbacks: Strong<dyn IInputFilterCallbacks>,
+}
+
+impl Interface for InputFilter {}
+
+impl InputFilter {
+ /// Create a new InputFilter instance.
+ pub fn new(callbacks: Strong<dyn IInputFilterCallbacks>) -> InputFilter {
+ Self { callbacks }
+ }
+}
+
+impl IInputFilter for InputFilter {
+ fn isEnabled(&self) -> binder::Result<bool> {
+ // TODO(b/294546335): Return true if any filters are to be applied, false otherwise
+ Result::Ok(false)
+ }
+ fn notifyKey(&self, event: &KeyEvent) -> binder::Result<()> {
+ // TODO(b/294546335): Handle key event and modify key events here
+ // Just send back the event without processing for now.
+ let _ = self.callbacks.sendKeyEvent(event);
+ Result::Ok(())
+ }
+ fn notifyInputDevicesChanged(&self, _device_ids: &[i32]) -> binder::Result<()> {
+ // TODO(b/294546335): Update data based on device changes here
+ Result::Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input_filter::InputFilter;
+ use binder::{Interface, Strong};
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ IInputFilter::IInputFilter, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
+ KeyEvent::KeyEvent,
+ };
+
+ struct FakeCallbacks {}
+
+ impl Interface for FakeCallbacks {}
+
+ impl IInputFilterCallbacks for FakeCallbacks {
+ fn sendKeyEvent(&self, _event: &KeyEvent) -> binder::Result<()> {
+ Result::Ok(())
+ }
+ }
+
+ #[test]
+ fn test_is_enabled() {
+ let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
+ Strong::new(Box::new(FakeCallbacks {}));
+ let filter: Box<dyn IInputFilter> = Box::new(InputFilter::new(fake_callbacks));
+ let result = filter.isEnabled();
+ assert!(result.is_ok());
+ assert!(!result.unwrap());
+ }
+
+ #[test]
+ fn test_notify_key() {
+ let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
+ Strong::new(Box::new(FakeCallbacks {}));
+ let filter: Box<dyn IInputFilter> = Box::new(InputFilter::new(fake_callbacks));
+ let event = create_key_event();
+ assert!(filter.notifyKey(&event).is_ok());
+ }
+
+ #[test]
+ fn test_notify_devices_changed() {
+ let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
+ Strong::new(Box::new(FakeCallbacks {}));
+ let filter: Box<dyn IInputFilter> = Box::new(InputFilter::new(fake_callbacks));
+ let result = filter.notifyInputDevicesChanged(&[0]);
+ assert!(result.is_ok());
+ }
+
+ fn create_key_event() -> KeyEvent {
+ KeyEvent {
+ id: 1,
+ deviceId: 1,
+ downTime: 0,
+ readTime: 0,
+ eventTime: 0,
+ source: 0,
+ displayId: 0,
+ policyFlags: 0,
+ action: 0,
+ flags: 0,
+ keyCode: 0,
+ scanCode: 0,
+ metaState: 0,
+ }
+ }
+}
diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs
index 501e435..a4049d5 100644
--- a/services/inputflinger/rust/lib.rs
+++ b/services/inputflinger/rust/lib.rs
@@ -19,13 +19,19 @@
//! We use cxxbridge to create IInputFlingerRust - the Rust component of inputflinger - and
//! pass it back to C++ as a local AIDL interface.
+mod input_filter;
+
+use crate::input_filter::InputFilter;
use binder::{
- unstable_api::{AIBinder, new_spibinder,},
+ unstable_api::{new_spibinder, AIBinder},
BinderFeatures, Interface, StatusCode, Strong,
};
-use com_android_server_inputflinger::aidl::com::android::server::inputflinger::IInputFlingerRust::{
- BnInputFlingerRust, IInputFlingerRust,
- IInputFlingerRustBootstrapCallback::IInputFlingerRustBootstrapCallback,
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ IInputFilter::{BnInputFilter, IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks},
+ IInputFlingerRust::{
+ BnInputFlingerRust, IInputFlingerRust,
+ IInputFlingerRustBootstrapCallback::IInputFlingerRustBootstrapCallback,
+ },
};
use log::debug;
@@ -71,8 +77,8 @@
// SAFETY: Our caller guaranteed that `callback` is a valid pointer to an `AIBinder` and its
// reference count has been incremented..
let Some(callback) = (unsafe { new_spibinder(callback) }) else {
- panic!("Failed to get SpAIBinder from raw callback pointer");
- };
+ panic!("Failed to get SpAIBinder from raw callback pointer");
+ };
let callback: Result<Strong<dyn IInputFlingerRustBootstrapCallback>, StatusCode> =
callback.into_interface();
@@ -93,7 +99,19 @@
impl Interface for InputFlingerRust {}
-impl IInputFlingerRust for InputFlingerRust {}
+impl IInputFlingerRust for InputFlingerRust {
+ fn createInputFilter(
+ &self,
+ callbacks: &Strong<dyn IInputFilterCallbacks>,
+ ) -> binder::Result<Strong<dyn IInputFilter>> {
+ debug!("Creating InputFilter");
+ let filter = BnInputFilter::new_binder(
+ InputFilter::new(callbacks.clone()),
+ BinderFeatures::default(),
+ );
+ Result::Ok(filter)
+ }
+}
impl Drop for InputFlingerRust {
fn drop(&mut self) {
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index 5475594..8043812 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -42,7 +42,10 @@
}
int32_t FakePointerController::getDisplayId() const {
- return mDisplayId;
+ if (!mDisplayId) {
+ return ADISPLAY_ID_NONE;
+ }
+ return *mDisplayId;
}
void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) {
@@ -51,6 +54,15 @@
viewport.logicalBottom - 1);
}
+void FakePointerController::assertViewportSet(int32_t displayId) {
+ ASSERT_TRUE(mDisplayId);
+ ASSERT_EQ(displayId, mDisplayId);
+}
+
+void FakePointerController::assertViewportNotSet() {
+ ASSERT_EQ(std::nullopt, mDisplayId);
+}
+
void FakePointerController::assertPosition(float x, float y) {
const auto [actualX, actualY] = getPosition();
ASSERT_NEAR(x, actualX, 1);
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index d7e40b3..9be6a6c 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -36,6 +36,8 @@
int32_t getDisplayId() const override;
void setDisplayViewport(const DisplayViewport& viewport) override;
+ void assertViewportSet(int32_t displayId);
+ void assertViewportNotSet();
void assertPosition(float x, float y);
void assertSpotCount(int32_t displayId, int32_t count);
bool isPointerShown();
@@ -54,7 +56,7 @@
bool mHaveBounds{false};
float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0};
float mX{0}, mY{0};
- int32_t mDisplayId{ADISPLAY_ID_NONE};
+ std::optional<int32_t> mDisplayId;
bool mIsPointerShown{false};
std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 41c7392..d2b68dd 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -35,16 +35,18 @@
namespace android {
+namespace input_flags = com::android::input::flags;
+
namespace {
const auto TOUCHPAD_PALM_REJECTION =
- ACONFIG_FLAG(com::android::input::flags, enable_touchpad_typing_palm_rejection);
+ ACONFIG_FLAG(input_flags, enable_touchpad_typing_palm_rejection);
} // namespace
using testing::AllOf;
-class GestureConverterTest : public testing::Test {
+class GestureConverterTestBase : public testing::Test {
protected:
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
static constexpr int32_t EVENTHUB_ID = 1;
@@ -91,6 +93,14 @@
std::shared_ptr<FakePointerController> mFakePointerController;
};
+class GestureConverterTest : public GestureConverterTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
+ GestureConverterTestBase::SetUp();
+ }
+};
+
TEST_F(GestureConverterTest, Move) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
@@ -1295,4 +1305,1182 @@
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
}
+// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
+// logic can be removed.
+class GestureConverterTestWithChoreographer : public GestureConverterTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(true);
+ GestureConverterTestBase::SetUp();
+ }
+};
+
+TEST_F(GestureConverterTestWithChoreographer, Move) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Move_Rotated) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(10, 5), WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ // Press left and right buttons at once
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
+ ASSERT_EQ(3u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ // Then release the left button
+ Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, leftUpGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ // Finally release the right button
+ Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture);
+ ASSERT_EQ(3u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, DragWithButton) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ // Press the button
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE,
+ /* is_tap= */ false);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ // Move
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ // Release the button
+ Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture);
+ ASSERT_EQ(3u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Scroll) {
+ const nsecs_t downTime = 12345;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER), WithDownTime(downTime),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -10),
+ WithGestureScrollDistance(0, 10, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -15),
+ WithGestureScrollDistance(0, 5, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+ GESTURES_FLING_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0 - 15),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) {
+ const nsecs_t downTime = 12345;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER), WithDownTime(downTime),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-10, 0),
+ WithGestureScrollDistance(0, 10, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-15, 0),
+ WithGestureScrollDistance(0, 5, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+ GESTURES_FLING_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(-15, 0),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsClassificationAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+ GESTURES_FLING_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionClassification(MotionClassification::NONE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsScrollDistanceAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+ GESTURES_FLING_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
+ // need to use another gesture type, like pinch.
+ Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture);
+ ASSERT_FALSE(args.empty());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsClassificationAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/0);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/-5,
+ /*dy=*/10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionClassification(MotionClassification::NONE));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5,
+ /*dy=*/5);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+
+ // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
+ // need to use another gesture type, like pinch.
+ Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture);
+ ASSERT_FALSE(args.empty());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(0)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) {
+ // The gestures library will "lock" a swipe into the dimension it starts in. For example, if you
+ // start swiping up and then start moving left or right, it'll return gesture events with only Y
+ // deltas until you lift your fingers and start swiping again. That's why each of these tests
+ // only checks movement in one dimension.
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
+ /* dy= */ 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ ASSERT_EQ(4u, args.size());
+
+ // Three fake fingers should be created. We don't actually care where they are, so long as they
+ // move appropriately.
+ NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger0Start = arg.pointerCoords[0];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger1Start = arg.pointerCoords[1];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger2Start = arg.pointerCoords[2];
+ args.pop_front();
+
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0, -0.01, EPSILON), WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 10);
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 10);
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 10);
+
+ Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 0, /* dy= */ 5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ ASSERT_EQ(1u, args.size());
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0, -0.005, EPSILON), WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 15);
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 15);
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15);
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
+ /* dy= */ 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ ASSERT_EQ(4u, args.size());
+
+ // Three fake fingers should be created. We don't actually care where they are, so long as they
+ // move appropriately.
+ NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
+ WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger0Start = arg.pointerCoords[0];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger1Start = arg.pointerCoords[1];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger2Start = arg.pointerCoords[2];
+ args.pop_front();
+
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 10);
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 10);
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 10);
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
+
+ Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 0, /* dy= */ 5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ ASSERT_EQ(1u, args.size());
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 15);
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 15);
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 15);
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
+ WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 10, /* dy= */ 0);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ ASSERT_EQ(5u, args.size());
+
+ // Four fake fingers should be created. We don't actually care where they are, so long as they
+ // move appropriately.
+ NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger0Start = arg.pointerCoords[0];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger1Start = arg.pointerCoords[1];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger2Start = arg.pointerCoords[2];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger3Start = arg.pointerCoords[3];
+ args.pop_front();
+
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0.01, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
+ EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY());
+
+ Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 5, /* dy= */ 0);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ ASSERT_EQ(1u, args.size());
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0.005, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
+ EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY());
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+ ASSERT_EQ(4u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Pinch_Inwards) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
+ GESTURES_ZOOM_START);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ ASSERT_EQ(2u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithCoords(-100, 0),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCoords(1, 100, 0),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture);
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(0.8f, EPSILON), WithPointerCoords(0, -80, 0),
+ WithPointerCoords(1, 80, 0), WithPointerCount(2u),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
+ GESTURES_ZOOM_END);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture);
+ ASSERT_EQ(2u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Pinch_Outwards) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
+ GESTURES_ZOOM_START);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ ASSERT_EQ(2u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithCoords(-100, 0),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCoords(1, 100, 0),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dz= */ 1.1, GESTURES_ZOOM_UPDATE);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture);
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.1f, EPSILON), WithPointerCoords(0, -110, 0),
+ WithPointerCoords(1, 110, 0), WithPointerCount(2u),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
+ GESTURES_ZOOM_END);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture);
+ ASSERT_EQ(2u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsClassificationAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*dz=*/1.2, GESTURES_ZOOM_UPDATE);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture);
+
+ Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_END);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionClassification(MotionClassification::NONE));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsScaleFactorAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*dz=*/1.2, GESTURES_ZOOM_UPDATE);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture);
+
+ Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_END);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture);
+
+ // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
+ // need to use another gesture type, like scroll.
+ Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/1,
+ /*dy=*/0);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, scrollGesture);
+ ASSERT_FALSE(args.empty());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGesturePinchScaleFactor(0, EPSILON));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ResetWithButtonPressed) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
+ /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_EQ(3u, args.size());
+
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ResetDuringScroll) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, -10),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/10);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_EQ(3u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ResetDuringPinch) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapDownGesture);
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Tap) {
+ // Tap should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+
+ ASSERT_EQ(5u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Click) {
+ // Click should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+
+ ASSERT_EQ(2u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabled,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+ // Tap should be ignored when disabled
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+
+ // no events should be generated
+ ASSERT_EQ(0u, args.size());
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisabled,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+ // Click should still produce button press/release events
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, MoveEnablesTapToClick,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+ // initially disable tap-to-click
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 91eceb0..e341782 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -20,6 +20,7 @@
#include "TestEventMatchers.h"
#include <NotifyArgsBuilders.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/silent_death_test.h>
#include <android-base/stringprintf.h>
@@ -53,6 +54,7 @@
using namespace ftl::flag_operators;
using testing::AllOf;
+using testing::Not;
namespace {
@@ -1319,10 +1321,13 @@
mInputReceiver->consumeCaptureEvent(hasCapture);
}
- void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
+ const MotionEvent& consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
MotionEvent* motionEvent = consumeMotion();
- ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher;
- ASSERT_THAT(*motionEvent, matcher);
+ if (nullptr == motionEvent) {
+ LOG(FATAL) << "Did not get a motion event, but expected " << matcher;
+ }
+ EXPECT_THAT(*motionEvent, matcher);
+ return *motionEvent;
}
void consumeEvent(InputEventType expectedEventType, int32_t expectedAction,
@@ -2593,9 +2598,9 @@
/**
* One window. Stylus hover on the window. Next, touch from another device goes down. Ensure that
- * touch is not dropped, because stylus hover should be ignored.
+ * touch is dropped, because stylus hover takes precedence.
*/
-TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDoesNotBlockTouchDown) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverBlocksTouchDown) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2624,34 +2629,29 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
.build());
- // Stylus hover is canceled because touch is down
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT),
- WithDeviceId(stylusDeviceId), WithCoords(100, 110)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId),
- WithCoords(140, 145)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
- WithCoords(141, 146)));
+ // Touch is ignored because stylus is hovering
- // Subsequent stylus movements are ignored
+ // Subsequent stylus movements are delivered correctly
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
.build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE),
+ WithDeviceId(stylusDeviceId), WithCoords(101, 111)));
- // but subsequent touches continue to be delivered
+ // and subsequent touches continue to be ignored
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147))
.build());
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
- WithCoords(142, 147)));
+ window->assertNoEvents();
}
/**
* One window. Touch down on the window. Then, stylus hover on the window from another device.
- * Ensure that touch is not canceled, because stylus hover should be dropped.
+ * Ensure that touch is canceled, because stylus hover should take precedence.
*/
-TEST_F(InputDispatcherMultiDeviceTest, TouchIsNotCanceledByStylusHover) {
+TEST_F(InputDispatcherMultiDeviceTest, TouchIsCanceledByStylusHover) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2683,15 +2683,21 @@
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
.build());
- // Stylus hover movement is dropped
+ // Stylus hover movement causes touch to be canceled
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId),
+ WithCoords(141, 146)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ WithDeviceId(stylusDeviceId), WithCoords(100, 110)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE),
+ WithDeviceId(stylusDeviceId), WithCoords(101, 111)));
+ // Subsequent touch movements are ignored
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147))
.build());
- // Subsequent touch movements are delivered correctly
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
- WithCoords(142, 147)));
+
+ window->assertNoEvents();
}
/**
@@ -3008,11 +3014,11 @@
* Three windows: a window on the left, a window on the right, and a spy window positioned above
* both.
* Check hover in left window and touch down in the right window.
- * At first, spy should receive hover, but the touch down should cancel hovering inside spy.
+ * At first, spy should receive hover. Spy shouldn't receive touch while stylus is hovering.
* At the same time, left and right should be getting independent streams of hovering and touch,
* respectively.
*/
-TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlockedByTouchWithSpy) {
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlocksTouchWithSpy) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow =
@@ -3052,28 +3058,25 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build());
leftWindow->assertNoEvents();
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ spyWindow->assertNoEvents();
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- // Stylus movements continue. They should be delivered to the left window only.
+ // Stylus movements continue. They should be delivered to the left window and the spy.
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
.build());
leftWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
- // Touch movements continue. They should be delivered to the right window and to the spy
+ // Touch movements continue. They should be delivered to the right window only
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(301).y(101))
.build());
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
@@ -3288,7 +3291,7 @@
* While the touch is down, new hover events from the stylus device should be ignored. After the
* touch is gone, stylus hovering should start working again.
*/
-TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverIgnoresTouchTap) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -3314,10 +3317,7 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
- // The touch device should cause hover to stop!
- window->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ // The touch device should be ignored!
// Continue hovering with stylus.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -3327,7 +3327,9 @@
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(60).y(60))
.build()));
- // Hovers are now ignored
+ // Hovers continue to work
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
// Lift up the finger
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -3337,7 +3339,6 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(touchDeviceId)));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
@@ -3346,8 +3347,8 @@
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(70).y(70))
.build()));
- window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithDeviceId(stylusDeviceId)));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
window->assertNoEvents();
}
@@ -6543,6 +6544,55 @@
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled());
}
+TEST_F(InputDispatcherTest, HoverEnterExitSynthesisUsesNewEventId) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> left = sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
+ ADISPLAY_ID_DEFAULT);
+ left->setFrame(Rect(0, 0, 100, 100));
+ sp<FakeWindowHandle> right = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Right Window", ADISPLAY_ID_DEFAULT);
+ right->setFrame(Rect(100, 0, 200, 100));
+ sp<FakeWindowHandle> spy =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window", ADISPLAY_ID_DEFAULT);
+ spy->setFrame(Rect(0, 0, 200, 100));
+ spy->setTrustedOverlay(true);
+ spy->setSpy(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spy->getInfo(), *left->getInfo(), *right->getInfo()}, {}, 0, 0});
+
+ // Send hover move to the left window, and ensure hover enter is synthesized with a new eventId.
+ NotifyMotionArgs notifyArgs = generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{50, 50}});
+ mDispatcher->notifyMotion(notifyArgs);
+
+ const MotionEvent& leftEnter = left->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), Not(WithEventId(notifyArgs.id)),
+ WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
+
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ Not(WithEventId(notifyArgs.id)),
+ Not(WithEventId(leftEnter.getId())),
+ WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
+
+ // Send move to the right window, and ensure hover exit and enter are synthesized with new ids.
+ notifyArgs = generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT,
+ {PointF{150, 50}});
+ mDispatcher->notifyMotion(notifyArgs);
+
+ const MotionEvent& leftExit = left->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), Not(WithEventId(notifyArgs.id)),
+ WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
+
+ right->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ Not(WithEventId(notifyArgs.id)),
+ Not(WithEventId(leftExit.getId())),
+ WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
+
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithEventId(notifyArgs.id)));
+}
+
class InputDispatcherFallbackKeyTest : public InputDispatcherTest {
protected:
std::shared_ptr<FakeApplicationHandle> mApp;
@@ -6752,8 +6802,8 @@
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
- static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
- static constexpr nsecs_t KEY_REPEAT_DELAY = 40 * 1000000; // 40 ms
+ static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms;
+ static constexpr std::chrono::nanoseconds KEY_REPEAT_DELAY = 40ms;
std::shared_ptr<FakeApplicationHandle> mApp;
sp<FakeWindowHandle> mWindow;
@@ -8135,7 +8185,8 @@
// Injection is async, so it will succeed
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
- ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
+ /*allowKeyRepeat=*/false));
// At this point, key is still pending, and should not be sent to the application yet.
// Make sure the `assertNoEvents` check doesn't take too long. It uses
// CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood.
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index 787444c..36be684 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -28,10 +28,13 @@
mFakePointerController = std::make_shared<FakePointerController>();
mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
mFakePointerController->setPosition(INITIAL_CURSOR_X, INITIAL_CURSOR_Y);
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
EXPECT_CALL(mMockInputReaderContext, getPointerController(DEVICE_ID))
.WillRepeatedly(Return(mFakePointerController));
+ EXPECT_CALL(mMockInputReaderContext, getPolicy()).WillRepeatedly(Return(mFakePolicy.get()));
+
EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub));
InputDeviceIdentifier identifier;
identifier.name = "device";
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index 3f9061f..05b0e97 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -55,6 +55,7 @@
std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value);
MockEventHubInterface mMockEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
std::shared_ptr<FakePointerController> mFakePointerController;
MockInputReaderContext mMockInputReaderContext;
std::unique_ptr<InputDevice> mDevice;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 5044b3e..e8b779a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -4922,6 +4922,8 @@
// --- CursorInputMapperTestWithChoreographer ---
+// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
+// logic can be removed.
class CursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase {
protected:
void SetUp() override {
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 68f5857..1efb797 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -50,6 +50,9 @@
const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200);
const auto SECOND_TOUCH_POINTER = PointerBuilder(/*id=*/1, ToolType::FINGER).x(200).y(300);
const auto STYLUS_POINTER = PointerBuilder(/*id=*/0, ToolType::STYLUS).x(100).y(200);
+const auto TOUCHPAD_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20);
static InputDeviceInfo generateTestDeviceInfo(int32_t deviceId, uint32_t source,
int32_t associatedDisplayId) {
@@ -237,7 +240,7 @@
.displayId(DISPLAY_ID)
.build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
- ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+ pc->assertViewportSet(DISPLAY_ID);
}
TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMouse) {
@@ -252,11 +255,11 @@
.displayId(DISPLAY_ID)
.build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
- ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+ pc->assertViewportNotSet();
// After Choreographer gets viewport, PointerController should also have viewport.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+ pc->assertViewportSet(DISPLAY_ID);
}
TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) {
@@ -274,7 +277,7 @@
.displayId(ADISPLAY_ID_NONE)
.build());
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
- ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+ pc->assertViewportSet(DISPLAY_ID);
}
TEST_F(PointerChoreographerTest,
@@ -291,7 +294,7 @@
.displayId(ADISPLAY_ID_NONE)
.build());
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
- ASSERT_EQ(DISPLAY_ID, firstDisplayPc->getDisplayId());
+ firstDisplayPc->assertViewportSet(DISPLAY_ID);
// Change default mouse display. Existing PointerController should be removed.
mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
@@ -306,7 +309,7 @@
.displayId(ADISPLAY_ID_NONE)
.build());
auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
- ASSERT_EQ(ANOTHER_DISPLAY_ID, secondDisplayPc->getDisplayId());
+ secondDisplayPc->assertViewportSet(ANOTHER_DISPLAY_ID);
}
TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) {
@@ -410,7 +413,7 @@
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
- // Set bounds and initial position of the PointerController.
+ // Set initial position of the PointerController.
pc->setPosition(100, 200);
// Make NotifyMotionArgs and notify Choreographer.
@@ -461,7 +464,7 @@
auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
- // Set bounds and initial position for PointerControllers.
+ // Set initial position for PointerControllers.
unassociatedMousePc->setPosition(100, 200);
associatedMousePc->setPosition(300, 400);
@@ -501,7 +504,7 @@
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
- // Set bounds and initial position of the PointerController.
+ // Set initial position of the PointerController.
pc->setPosition(100, 200);
// Assume that pointer capture is enabled.
@@ -549,20 +552,6 @@
mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
-
- // Set bounds and initial position of the PointerController.
- pc->setPosition(100, 200);
-
- // Make NotifyMotionArgs and notify Choreographer.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(MOUSE_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
- .build());
-
- // Check that the PointerController updated the position and the pointer is shown.
- pc->assertPosition(110, 220);
ASSERT_TRUE(pc->isPointerShown());
// Enable pointer capture and check if the PointerController hid the pointer.
@@ -641,7 +630,6 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
mChoreographer.setShowTouchesEnabled(true);
- assertPointerControllerNotCreated();
mChoreographer.notifyMotion(
MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(FIRST_TOUCH_POINTER)
@@ -885,8 +873,8 @@
.build());
auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
- // Check that displayId is set.
- ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+ // Check that viewport is set for the PointerController.
+ pc->assertViewportSet(DISPLAY_ID);
}
TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterSetsViewportForStylusPointerController) {
@@ -902,14 +890,14 @@
.build());
auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
- // Check that displayId is unset.
- ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+ // Check that viewport is unset.
+ pc->assertViewportNotSet();
// Set viewport.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- // Check that displayId is set.
- ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+ // Check that the viewport is set for the PointerController.
+ pc->assertViewportSet(DISPLAY_ID);
}
TEST_F(PointerChoreographerTest,
@@ -926,14 +914,14 @@
.build());
auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
- // Check that displayId is unset.
- ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+ // Check that viewport is unset.
+ pc->assertViewportNotSet();
// Set viewport which does not match the associated display of the stylus.
mChoreographer.setDisplayViewports(createViewports({ANOTHER_DISPLAY_ID}));
- // Check that displayId is still unset.
- ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+ // Check that viewport is still unset.
+ pc->assertViewportNotSet();
}
TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointer) {
@@ -1050,4 +1038,477 @@
assertPointerControllerRemoved(pc);
}
+TEST_F(PointerChoreographerTest, WhenTouchpadIsJustAddedDoesNotCreatePointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchpadEventOccursCreatesPointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedRemovesPointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+
+ // Remove the touchpad.
+ mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, SetsViewportForAssociatedTouchpad) {
+ // Just adding a viewport or device should not create a PointerController.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ DISPLAY_ID)}});
+ assertPointerControllerNotCreated();
+
+ // After the touchpad emits event, PointerController will be created and viewport will be set.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedTouchpad) {
+ // Without viewport information, PointerController will be created by a touchpad event
+ // but viewport won't be set.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ DISPLAY_ID)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportNotSet();
+
+ // After Choreographer gets viewport, PointerController should also have viewport.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ pc->assertViewportSet(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, SetsDefaultTouchpadViewportForPointerController) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // For a touchpad event without a target display, default viewport should be set for
+ // the PointerController.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenDefaultTouchpadDisplayChangesSetsDefaultTouchpadViewportForPointerController) {
+ // Set one display as a default touchpad display and emit touchpad event to create
+ // PointerController.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ firstDisplayPc->assertViewportSet(DISPLAY_ID);
+
+ // Change default mouse display. Existing PointerController should be removed.
+ mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ assertPointerControllerRemoved(firstDisplayPc);
+
+ // New PointerController for the new default display will be created by the motion event.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ secondDisplayPc->assertViewportSet(ANOTHER_DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, TouchpadCallsNotifyPointerDisplayIdChanged) {
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
+
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterTouchpadCallsNotifyPointerDisplayIdChanged) {
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotNotified();
+
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedCallsNotifyPointerDisplayIdChanged) {
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+
+ mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+ assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenDefaultMouseDisplayChangesTouchpadCallsNotifyPointerDisplayIdChanged) {
+ // Add two viewports.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+
+ // Set one viewport as a default mouse display ID.
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+
+ // Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified
+ // before a touchpad event.
+ mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+ assertPointerControllerRemoved(firstDisplayPc);
+
+ // After a touchpad event, pointer display ID will be notified with new default mouse display.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, TouchpadMovesPointerAndReturnsNewArgs) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set initial position of the PointerController.
+ pc->setPosition(100, 200);
+
+ // Make NotifyMotionArgs and notify Choreographer.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+
+ // Check that the PointerController updated the position and the pointer is shown.
+ pc->assertPosition(110, 220);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220)));
+}
+
+TEST_F(PointerChoreographerTest, TouchpadAddsPointerPositionToTheCoords) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set initial position of the PointerController.
+ pc->setPosition(100, 200);
+
+ // Notify motion with fake fingers, as if it is multi-finger swipe.
+ // Check if the position of the PointerController is added to the fake finger coords.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-100).y(0))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithCoords(0, 200), WithCursorPosition(100, 200)));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-100).y(0))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(0).y(0))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCoords(0, 0, 200), WithPointerCoords(1, 100, 200),
+ WithCursorPosition(100, 200)));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-100).y(0))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(0).y(0))
+ .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(100).y(0))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCoords(0, 0, 200), WithPointerCoords(1, 100, 200),
+ WithPointerCoords(2, 200, 200), WithCursorPosition(100, 200)));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-90).y(10))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(10))
+ .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(110).y(10))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCoords(0, 10, 210), WithPointerCoords(1, 110, 210),
+ WithPointerCoords(2, 210, 210), WithCursorPosition(100, 200)));
+}
+
+TEST_F(PointerChoreographerTest,
+ AssociatedTouchpadMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
+ // Add two displays and set one to default.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // Add two devices, one unassociated and the other associated with non-default mouse display.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ANOTHER_DISPLAY_ID)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
+
+ // Set initial positions for PointerControllers.
+ unassociatedMousePc->setPosition(100, 200);
+ associatedMousePc->setPosition(300, 400);
+
+ // Make NotifyMotionArgs from the associated mouse and notify Choreographer.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+
+ // Check the status of the PointerControllers.
+ unassociatedMousePc->assertPosition(100, 200);
+ ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
+ associatedMousePc->assertPosition(310, 420);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
+ ASSERT_TRUE(associatedMousePc->isPointerShown());
+
+ // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(310, 420), WithDeviceId(SECOND_DEVICE_ID),
+ WithDisplayId(ANOTHER_DISPLAY_ID), WithCursorPosition(310, 420)));
+}
+
+TEST_F(PointerChoreographerTest, DoesNotMovePointerForTouchpadSource) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set initial position of the PointerController.
+ pc->setPosition(200, 300);
+
+ // Assume that pointer capture is enabled.
+ mChoreographer.notifyPointerCaptureChanged(
+ NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
+ PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+
+ // Notify motion as if pointer capture is enabled.
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHPAD)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+
+ // Check that there's no update on the PointerController.
+ pc->assertPosition(200, 300);
+ ASSERT_FALSE(pc->isPointerShown());
+
+ // Check x-y cooridnates, displayId and cursor position are not changed.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(100, 200), WithDisplayId(ADISPLAY_ID_NONE),
+ WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION)));
+}
+
+TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledTouchpadHidesPointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Enable pointer capture and check if the PointerController hid the pointer.
+ mChoreographer.notifyPointerCaptureChanged(
+ NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
+ PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+ ASSERT_FALSE(pc->isPointerShown());
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index a0a0f5a..66fdaa4 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -492,6 +492,71 @@
return WithKeyCodeMatcher(keyCode);
}
+/// EventId
+class WithEventIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithEventIdMatcher(int32_t eventId) : mEventId(eventId) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mEventId == args.id;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mEventId == args.id;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mEventId == event.getId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with eventId 0x" << std::hex << mEventId; }
+
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "with eventId not equal to 0x" << std::hex << mEventId;
+ }
+
+private:
+ const int32_t mEventId;
+};
+
+inline WithEventIdMatcher WithEventId(int32_t eventId) {
+ return WithEventIdMatcher(eventId);
+}
+
+/// EventIdSource
+class WithEventIdSourceMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithEventIdSourceMatcher(IdGenerator::Source eventIdSource)
+ : mEventIdSource(eventIdSource) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mEventIdSource == IdGenerator::getSource(args.id);
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mEventIdSource == IdGenerator::getSource(args.id);
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mEventIdSource == IdGenerator::getSource(event.getId());
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with eventId from source 0x" << std::hex << ftl::to_underlying(mEventIdSource);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong event from source"; }
+
+private:
+ const IdGenerator::Source mEventIdSource;
+};
+
+inline WithEventIdSourceMatcher WithEventIdSource(IdGenerator::Source eventIdSource) {
+ return WithEventIdSourceMatcher(eventIdSource);
+}
+
MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
return arg.getRepeatCount() == repeatCount;
}
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 6203a1d..8cf738c 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
+#include <com_android_input_flags.h>
#include <thread>
#include "FakePointerController.h"
#include "InputMapperTest.h"
@@ -36,11 +37,17 @@
constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS;
constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE;
constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
+constexpr int32_t DISPLAY_ID = 0;
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
+constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
+
+namespace input_flags = com::android::input::flags;
/**
* Unit tests for TouchpadInputMapper.
*/
-class TouchpadInputMapperTest : public InputMapperUnitTest {
+class TouchpadInputMapperTestBase : public InputMapperUnitTest {
protected:
void SetUp() override {
InputMapperUnitTest::SetUp();
@@ -104,6 +111,14 @@
}
};
+class TouchpadInputMapperTest : public TouchpadInputMapperTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
+ TouchpadInputMapperTestBase::SetUp();
+ }
+};
+
/**
* Start moving the finger and then click the left touchpad button. Check whether HOVER_EXIT is
* generated when hovering stops. Currently, it is not.
@@ -153,4 +168,71 @@
ASSERT_THAT(args, testing::IsEmpty());
}
+class TouchpadInputMapperTestWithChoreographer : public TouchpadInputMapperTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(true);
+ TouchpadInputMapperTestBase::SetUp();
+ }
+};
+
+// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
+// logic can be removed.
+/**
+ * Start moving the finger and then click the left touchpad button. Check whether HOVER_EXIT is
+ * generated when hovering stops. Currently, it is not.
+ * In the current implementation, HOVER_MOVE and ACTION_DOWN events are not sent out right away,
+ * but only after the button is released.
+ */
+TEST_F(TouchpadInputMapperTestWithChoreographer, HoverAndLeftButtonPress) {
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+
+ std::list<NotifyArgs> args;
+
+ args += mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_THAT(args, testing::IsEmpty());
+
+ args += process(EV_ABS, ABS_MT_TRACKING_ID, 1);
+ args += process(EV_KEY, BTN_TOUCH, 1);
+ setScanCodeState(KeyState::DOWN, {BTN_TOOL_FINGER});
+ args += process(EV_KEY, BTN_TOOL_FINGER, 1);
+ args += process(EV_ABS, ABS_MT_POSITION_X, 50);
+ args += process(EV_ABS, ABS_MT_POSITION_Y, 50);
+ args += process(EV_ABS, ABS_MT_PRESSURE, 1);
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args, testing::IsEmpty());
+
+ // Without this sleep, the test fails.
+ // TODO(b/284133337): Figure out whether this can be removed
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+
+ args += process(EV_KEY, BTN_LEFT, 1);
+ setScanCodeState(KeyState::DOWN, {BTN_LEFT});
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ args += process(EV_KEY, BTN_LEFT, 0);
+ setScanCodeState(KeyState::UP, {BTN_LEFT});
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+
+ // Liftoff
+ args.clear();
+ args += process(EV_ABS, ABS_MT_PRESSURE, 0);
+ args += process(EV_ABS, ABS_MT_TRACKING_ID, -1);
+ args += process(EV_KEY, BTN_TOUCH, 0);
+ setScanCodeState(KeyState::UP, {BTN_TOOL_FINGER});
+ args += process(EV_KEY, BTN_TOOL_FINGER, 0);
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args, testing::IsEmpty());
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 17fa7be..0989863 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -93,6 +93,7 @@
"libscheduler",
"libserviceutils",
"libshaders",
+ "libsurfaceflinger_common",
"libtimestats",
"libtonemap",
"libsurfaceflingerflags",
@@ -175,7 +176,6 @@
"FrontEnd/LayerLifecycleManager.cpp",
"FrontEnd/RequestedLayerState.cpp",
"FrontEnd/TransactionHandler.cpp",
- "FlagManager.cpp",
"FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 370e4b6..2740a97 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -63,7 +63,10 @@
cc_library {
name: "libcompositionengine",
defaults: ["libcompositionengine_defaults"],
- static_libs: ["libsurfaceflingerflags"],
+ static_libs: [
+ "libsurfaceflinger_common",
+ "libsurfaceflingerflags",
+ ],
srcs: [
"src/planner/CachedSet.cpp",
"src/planner/Flattener.cpp",
@@ -108,6 +111,7 @@
"libgtest",
"libgmock",
"libcompositionengine",
+ "libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
],
local_include_dirs: ["include"],
@@ -143,6 +147,7 @@
"librenderengine_mocks",
"libgmock",
"libgtest",
+ "libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
],
// For some reason, libvulkan isn't picked up from librenderengine
diff --git a/services/surfaceflinger/CompositionEngine/AndroidTest.xml b/services/surfaceflinger/CompositionEngine/AndroidTest.xml
new file mode 100644
index 0000000..94e92f0
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for libcompositionengine_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push"
+ value="libcompositionengine_test->/data/local/tmp/libcompositionengine_test" />
+ </target_preparer>
+
+ <!--
+ Disable SELinux so that crashes in the test suite produces symbolized stack traces.
+ -->
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
+
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libcompositionengine_test" />
+ </test>
+</configuration>
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 15fadbc..002177b 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -111,8 +111,6 @@
}
void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) {
- std::unordered_map<compositionengine::LayerFE*, compositionengine::LayerFECompositionState*>
- uniqueVisibleLayers;
for (const auto& output : args.outputs) {
for (auto* layer : output->getOutputLayersOrderedByZ()) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 00590e6..d9318af 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -223,9 +223,6 @@
}
TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) {
- mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2;
- mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold;
-
auto& layerState1 = mTestLayers[0]->layerState;
auto& layerState2 = mTestLayers[1]->layerState;
@@ -235,6 +232,10 @@
};
initializeFlattener(layers);
+
+ mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2;
+ mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold;
+
expectAllLayersFlattened(layers);
}
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5b6591a..2ffe92b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -63,13 +63,14 @@
mDisplayToken(args.displayToken),
mSequenceId(args.sequenceId),
mCompositionDisplay{args.compositionDisplay},
- mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())),
- mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
- mRenderFrameRateFPSTrace("RenderRateFPS -" + to_string(getId())),
+ mActiveModeFPSTrace(concatId("ActiveModeFPS")),
+ mActiveModeFPSHwcTrace(concatId("ActiveModeFPS_HWC")),
+ mRenderFrameRateFPSTrace(concatId("RenderRateFPS")),
mPhysicalOrientation(args.physicalOrientation),
mIsPrimary(args.isPrimary),
mRequestedRefreshRate(args.requestedRefreshRate),
- mRefreshRateSelector(std::move(args.refreshRateSelector)) {
+ mRefreshRateSelector(std::move(args.refreshRateSelector)),
+ mDesiredActiveModeChanged(concatId("DesiredActiveModeChanged"), false) {
mCompositionDisplay->editState().isSecure = args.isSecure;
mCompositionDisplay->createRenderSurface(
compositionengine::RenderSurfaceCreationArgsBuilder()
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index a044534..a40f310 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -269,6 +269,11 @@
void dump(utils::Dumper&) const;
private:
+ template <size_t N>
+ inline std::string concatId(const char (&str)[N]) const {
+ return std::string(ftl::Concat(str, ' ', getId().value).str());
+ }
+
const sp<SurfaceFlinger> mFlinger;
HWComposer& mHwComposer;
const wp<IBinder> mDisplayToken;
@@ -316,8 +321,7 @@
mutable std::mutex mActiveModeLock;
ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
- TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock) =
- {ftl::Concat("DesiredActiveModeChanged-", getId().value).c_str(), false};
+ TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock);
ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 2a6443a..2d957e6 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -24,6 +24,7 @@
#include <android-base/file.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
+#include <common/FlagManager.h>
#include <gui/TraceUtils.h>
#include <log/log.h>
#include <utils/Trace.h>
@@ -281,7 +282,7 @@
}
bool AidlComposer::getDisplayConfigurationsSupported() const {
- return mComposerInterfaceVersion >= 3;
+ return mComposerInterfaceVersion >= 3 && FlagManager::getInstance().vrr_config();
}
std::vector<Capability> AidlComposer::getCapabilities() {
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index f32fb3a..ba0825c 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -29,8 +29,8 @@
#include <scheduler/Fps.h>
+#include <common/FlagManager.h>
#include "DisplayHardware/Hal.h"
-#include "FlagManager.h"
#include "Scheduler/StrongTyping.h"
namespace android {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 6be57d4..9aaaa95 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -540,8 +540,9 @@
displayData.validateWasSkipped = false;
{
std::scoped_lock lock{displayData.expectedPresentLock};
- displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime);
- // TODO(b/296636176) Update displayData.lastFrameInterval for present display commands
+ if (expectedPresentTime > displayData.lastExpectedPresentTimestamp.ns()) {
+ displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime);
+ }
}
if (canSkipValidate) {
@@ -965,6 +966,11 @@
isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp,
timeoutOpt, threshold);
+ using fps_approx_ops::operator!=;
+ if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) {
+ displayData.lastExpectedPresentTimestamp = expectedPresentTime;
+ }
+
if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
return NO_ERROR;
}
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 2d4ec04..29c9432 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -28,6 +28,7 @@
],
static_libs: [
"libperfetto_client_experimental",
+ "libsurfaceflinger_common",
],
export_include_dirs: ["."],
}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index dfbb55d..9dc3938 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -21,6 +21,7 @@
#include "FrameTimeline.h"
#include <android-base/stringprintf.h>
+#include <common/FlagManager.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -1200,7 +1201,9 @@
surfaceFrame->trace(mToken, monoBootOffset);
}
- addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousActualPresentTime);
+ if (FlagManager::getInstance().add_sf_skipped_frames_to_trace()) {
+ addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousActualPresentTime);
+ }
}
float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 2a0857d..9476ff4 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -739,6 +739,7 @@
!snapshot.changes.test(RequestedLayerState::Changes::Created)) {
if (forceUpdate ||
snapshot.changes.any(RequestedLayerState::Changes::Geometry |
+ RequestedLayerState::Changes::BufferSize |
RequestedLayerState::Changes::Input)) {
updateInput(snapshot, requested, parentSnapshot, path, args);
}
@@ -1095,6 +1096,8 @@
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::TRUSTED_OVERLAY;
}
+ snapshot.inputInfo.contentSize = snapshot.croppedBufferSize.getSize();
+
// 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.
if (path.isClone()) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 66ea15c..4c2da91 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -136,6 +136,7 @@
using gui::GameMode;
using gui::LayerMetadata;
using gui::WindowInfo;
+using ui::Size;
using PresentState = frametimeline::SurfaceFrame::PresentState;
@@ -165,6 +166,7 @@
mDrawingState.sequence = 0;
mDrawingState.transform.set(0, 0);
mDrawingState.frameNumber = 0;
+ mDrawingState.previousFrameNumber = 0;
mDrawingState.barrierFrameNumber = 0;
mDrawingState.producerId = 0;
mDrawingState.barrierProducerId = 0;
@@ -2591,6 +2593,9 @@
}
}
+ Rect bufferSize = getBufferSize(getDrawingState());
+ info.contentSize = Size(bufferSize.width(), bufferSize.height());
+
return info;
}
@@ -2931,7 +2936,6 @@
break;
}
}
-
if (ch != nullptr) {
ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
@@ -2940,6 +2944,10 @@
if (mBufferInfo.mBuffer) {
mPreviouslyPresentedLayerStacks.push_back(layerStack);
}
+
+ if (mDrawingState.frameNumber > 0) {
+ mDrawingState.previousFrameNumber = mDrawingState.frameNumber;
+ }
}
void Layer::onSurfaceFrameCreated(
@@ -3144,6 +3152,7 @@
void Layer::resetDrawingStateBufferInfo() {
mDrawingState.producerId = 0;
mDrawingState.frameNumber = 0;
+ mDrawingState.previousFrameNumber = 0;
mDrawingState.releaseBufferListener = nullptr;
mDrawingState.buffer = nullptr;
mDrawingState.acquireFence = sp<Fence>::make(-1);
@@ -3420,6 +3429,7 @@
// If this transaction set an acquire fence on this layer, set its acquire time
handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
handle->frameNumber = mDrawingState.frameNumber;
+ handle->previousFrameNumber = mDrawingState.previousFrameNumber;
// Store so latched time and release fence can be set
mDrawingState.callbackHandles.push_back(handle);
@@ -3625,7 +3635,7 @@
// to upsert RenderEngine's caches. Put in a special workaround to be backwards
// compatible with old vendors, with a ticking clock.
static const int32_t kVendorVersion =
- base::GetIntProperty("ro.vndk.version", __ANDROID_API_FUTURE__);
+ base::GetIntProperty("ro.board.api_level", __ANDROID_API_FUTURE__);
if (const auto format =
static_cast<aidl::android::hardware::graphics::common::PixelFormat>(
mBufferInfo.mBuffer->getPixelFormat());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f715910..28168c3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -138,6 +138,7 @@
ui::Dataspace dataspace;
uint64_t frameNumber;
+ uint64_t previousFrameNumber;
// high watermark framenumber to use to check for barriers to protect ourselves
// from out of order transactions
uint64_t barrierFrameNumber;
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 6752a0b..b960e33 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,8 +16,8 @@
#include <algorithm>
+#include <common/FlagManager.h>
#include "Client.h"
-#include "FlagManager.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 7f627f8..693a357 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -43,9 +43,9 @@
#include <utils/Errors.h>
#include <utils/Trace.h>
+#include <common/FlagManager.h>
#include <scheduler/VsyncConfig.h>
#include "DisplayHardware/DisplayMode.h"
-#include "FlagManager.h"
#include "FrameTimeline.h"
#include "VSyncDispatch.h"
#include "VSyncTracker.h"
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 450ba1d..d309adc 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -31,9 +31,9 @@
#include <string>
#include <utility>
+#include <common/FlagManager.h>
#include "../Layer.h"
#include "EventThread.h"
-#include "FlagManager.h"
#include "LayerInfo.h"
namespace android::scheduler {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 5892b2b..47c8ef9 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -1487,7 +1487,7 @@
case FrameRateCategory::Normal:
return FpsRange{60_Hz, 90_Hz};
case FrameRateCategory::Low:
- return FpsRange{30_Hz, 60_Hz};
+ return FpsRange{30_Hz, 30_Hz};
case FrameRateCategory::NoPreference:
case FrameRateCategory::Default:
LOG_ALWAYS_FATAL("Should not get fps range for frame rate category: %s",
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f41243c..b54f334 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -45,9 +45,9 @@
#include <memory>
#include <numeric>
+#include <common/FlagManager.h>
#include "../Layer.h"
#include "EventThread.h"
-#include "FlagManager.h"
#include "FrameRateOverrideMappings.h"
#include "FrontEnd/LayerHandle.h"
#include "OneShotTimer.h"
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 3e7ec49..ef30887 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -25,7 +25,7 @@
#include <scheduler/TimeKeeper.h>
-#include "FlagManager.h"
+#include <common/FlagManager.h>
#include "VSyncDispatchTimerQueue.h"
#include "VSyncTracker.h"
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 57aa010..f5f93ce 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -29,13 +29,13 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <common/FlagManager.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <ftl/concat.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
-#include "FlagManager.h"
#include "RefreshRateSelector.h"
#include "VSyncPredictor.h"
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9c8555e..644b6ef 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -113,6 +113,7 @@
#include <unordered_map>
#include <vector>
+#include <common/FlagManager.h>
#include <gui/LayerStatePermissions.h>
#include <gui/SchedulingPolicy.h>
#include <ui/DisplayIdentification.h>
@@ -129,7 +130,6 @@
#include "DisplayHardware/VirtualDisplaySurface.h"
#include "DisplayRenderArea.h"
#include "Effects/Daltonizer.h"
-#include "FlagManager.h"
#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
@@ -875,7 +875,7 @@
mRenderEnginePrimeCacheFuture = getRenderEngine().primeCache(shouldPrimeUltraHDR);
if (setSchedFifo(true) != NO_ERROR) {
- ALOGW("Can't set SCHED_OTHER for primeCache");
+ ALOGW("Can't set SCHED_FIFO after primeCache");
}
}
@@ -2922,7 +2922,6 @@
const scheduler::FrameTargeters& frameTargeters,
nsecs_t presentStartTime) {
ATRACE_CALL();
- ALOGV(__func__);
ui::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<FenceTime>> presentFences;
ui::PhysicalDisplayMap<PhysicalDisplayId, const sp<Fence>> gpuCompositionDoneFences;
@@ -3810,7 +3809,6 @@
// first frame before the display is available, we rely
// on WMS and DMS to provide the right information
// so the client can calculate the hint.
- ALOGV("Skipping reporting transform hint update for %s", layer->getDebugName());
layer->skipReportingTransformHint();
} else {
layer->updateTransformHint(hintDisplay->getTransformHint());
@@ -6771,8 +6769,7 @@
case 1007: // Unused.
return NAME_NOT_FOUND;
case 1008: // Toggle forced GPU composition.
- mDebugDisableHWC = data.readInt32() != 0;
- scheduleRepaint();
+ sfdo_forceClientComposition(data.readInt32() != 0);
return NO_ERROR;
case 1009: // Toggle use of transform hint.
mDebugDisableTransformHint = data.readInt32() != 0;
@@ -7582,7 +7579,7 @@
args.allowProtected, args.grayscale, captureListener);
}
-void SurfaceFlinger::captureDisplay(DisplayId displayId,
+void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) {
ui::LayerStack layerStack;
wp<const DisplayDevice> displayWeak;
@@ -7601,10 +7598,23 @@
size = display->getLayerStackSpaceRect().getSize();
}
+ size.width *= args.frameScaleX;
+ size.height *= args.frameScaleY;
+
+ // We could query a real value for this but it'll be a long, long time until we support
+ // displays that need upwards of 1GB per buffer so...
+ constexpr auto kMaxTextureSize = 16384;
+ if (size.width <= 0 || size.height <= 0 || size.width >= kMaxTextureSize ||
+ size.height >= kMaxTextureSize) {
+ ALOGE("capture display resolved to invalid size %d x %d", size.width, size.height);
+ invokeScreenCaptureError(BAD_VALUE, captureListener);
+ return;
+ }
+
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
- return DisplayRenderArea::create(displayWeak, Rect(), size, ui::Dataspace::UNKNOWN,
+ return DisplayRenderArea::create(displayWeak, Rect(), size, args.dataspace,
false /* useIdentityTransform */,
- false /* hintForSeamlessTransition */,
+ args.hintForSeamlessTransition,
false /* captureSecureLayers */);
});
@@ -7628,8 +7638,8 @@
constexpr bool kAllowProtected = false;
constexpr bool kGrayscale = false;
- captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size,
- ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale, captureListener);
+ captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size, args.pixelFormat,
+ kAllowProtected, kGrayscale, captureListener);
}
void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
@@ -9061,6 +9071,11 @@
setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded | eTraversalNeeded);
}
+void SurfaceFlinger::sfdo_forceClientComposition(bool enabled) {
+ mDebugDisableHWC = enabled;
+ scheduleRepaint();
+}
+
// gui::ISurfaceComposer
binder::Status SurfaceComposerAIDL::bootFinished() {
@@ -9423,13 +9438,14 @@
}
binder::Status SurfaceComposerAIDL::captureDisplayById(
- int64_t displayId, const sp<IScreenCaptureListener>& captureListener) {
+ int64_t displayId, const CaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) {
// status_t status;
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
- mFlinger->captureDisplay(*id, captureListener);
+ mFlinger->captureDisplay(*id, args, captureListener);
} else {
invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
}
@@ -9779,6 +9795,11 @@
return binder::Status::ok();
}
+binder::Status SurfaceComposerAIDL::forceClientComposition(bool enabled) {
+ mFlinger->sfdo_forceClientComposition(enabled);
+ return binder::Status::ok();
+}
+
binder::Status SurfaceComposerAIDL::updateSmallAreaDetection(const std::vector<int32_t>& appIds,
const std::vector<float>& thresholds) {
status_t status;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1e90340..9e6da3f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -63,13 +63,13 @@
#include <scheduler/interface/ICompositor.h>
#include <ui/FenceResult.h>
+#include <common/FlagManager.h>
#include "Display/PhysicalDisplay.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/PowerAdvisor.h"
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
-#include "FlagManager.h"
#include "FrontEnd/DisplayInfo.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerLifecycleManager.h"
@@ -531,7 +531,7 @@
const sp<IBinder>& layerHandle = nullptr);
void captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
- void captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+ void captureDisplay(DisplayId, const CaptureArgs&, const sp<IScreenCaptureListener>&);
void captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
@@ -1462,6 +1462,7 @@
void sfdo_setDebugFlash(int delay);
void sfdo_scheduleComposite();
void sfdo_scheduleCommit();
+ void sfdo_forceClientComposition(bool enabled);
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1507,7 +1508,8 @@
binder::Status setGameContentType(const sp<IBinder>& display, bool on) override;
binder::Status captureDisplay(const DisplayCaptureArgs&,
const sp<IScreenCaptureListener>&) override;
- binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override;
+ binder::Status captureDisplayById(int64_t, const CaptureArgs&,
+ const sp<IScreenCaptureListener>&) override;
binder::Status captureLayers(const LayerCaptureArgs&,
const sp<IScreenCaptureListener>&) override;
@@ -1572,6 +1574,7 @@
binder::Status setDebugFlash(int delay) override;
binder::Status scheduleComposite() override;
binder::Status scheduleCommit() override;
+ binder::Status forceClientComposition(bool enabled) override;
binder::Status updateSmallAreaDetection(const std::vector<int32_t>& appIds,
const std::vector<float>& thresholds) override;
binder::Status setSmallAreaDetectionThreshold(int32_t appId, float threshold) override;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 3587a72..6a155c1 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -158,7 +158,7 @@
handle->previousReleaseFence = prevFence;
handle->previousReleaseFences.clear();
- FrameEventHistoryStats eventStats(handle->frameNumber,
+ FrameEventHistoryStats eventStats(handle->frameNumber, handle->previousFrameNumber,
handle->gpuCompositionDoneFence->getSnapshot().fence,
handle->compositorTiming, handle->refreshStartTime,
handle->dequeueReadyTime);
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 3074795..245398f 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -56,6 +56,7 @@
nsecs_t refreshStartTime = 0;
nsecs_t dequeueReadyTime = 0;
uint64_t frameNumber = 0;
+ uint64_t previousFrameNumber = 0;
ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
};
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
new file mode 100644
index 0000000..5ef22b5
--- /dev/null
+++ b/services/surfaceflinger/common/Android.bp
@@ -0,0 +1,48 @@
+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: "libsurfaceflinger_common_defaults",
+ defaults: [
+ "android.hardware.graphics.composer3-ndk_shared",
+ "surfaceflinger_defaults",
+ ],
+ shared_libs: [
+ "libSurfaceFlingerProp",
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ "librenderengine",
+ ],
+ srcs: [
+ "FlagManager.cpp",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+cc_library_static {
+ name: "libsurfaceflinger_common",
+ defaults: [
+ "libsurfaceflinger_common_defaults",
+ ],
+ static_libs: [
+ "libsurfaceflingerflags",
+ ],
+}
+
+cc_library_static {
+ name: "libsurfaceflinger_common_test",
+ defaults: [
+ "libsurfaceflinger_common_defaults",
+ ],
+ static_libs: [
+ "libsurfaceflingerflags_test",
+ ],
+}
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
similarity index 97%
rename from services/surfaceflinger/FlagManager.cpp
rename to services/surfaceflinger/common/FlagManager.cpp
index 13b8a6c..e2a1498 100644
--- a/services/surfaceflinger/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "FlagManager.h"
+#include <common/FlagManager.h>
#include <SurfaceFlingerProperties.sysprop.h>
#include <android-base/parsebool.h>
@@ -120,6 +120,7 @@
DUMP_READ_ONLY_FLAG(hotplug2);
DUMP_READ_ONLY_FLAG(hdcp_level_hal);
DUMP_READ_ONLY_FLAG(multithreaded_present);
+ DUMP_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace);
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
@@ -188,6 +189,7 @@
FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "")
FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "")
FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present")
+FLAG_MANAGER_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace, "")
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
similarity index 97%
rename from services/surfaceflinger/FlagManager.h
rename to services/surfaceflinger/common/include/common/FlagManager.h
index e3e4f80..9aabbb9 100644
--- a/services/surfaceflinger/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -59,6 +59,7 @@
bool hotplug2() const;
bool hdcp_level_hal() const;
bool multithreaded_present() const;
+ bool add_sf_skipped_frames_to_trace() const;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index 243b8e0..ab3b352 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -39,6 +39,7 @@
"libgtest_ndk_c++",
"libgmock_main_ndk",
"librenderengine_mocks",
+ "libsurfaceflinger_common",
"perfetto_trace_protos",
"libcompositionengine_mocks",
"perfetto_trace_protos",
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
index 8978971..ce8d47e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
@@ -30,12 +30,6 @@
constexpr int32_t kConfigDuration = 500;
constexpr int32_t kBufferSize = 1024;
constexpr int32_t kTimeOffset = 100000;
-constexpr perfetto::BackendType backendTypes[] = {
- perfetto::kUnspecifiedBackend,
- perfetto::kInProcessBackend,
- perfetto::kSystemBackend,
- perfetto::kCustomBackend,
-};
class FrameTracerFuzzer {
public:
@@ -71,8 +65,7 @@
auto* dsCfg = cfg.add_data_sources()->mutable_config();
dsCfg->set_name(android::FrameTracer::kFrameTracerDataSource);
- auto tracingSession =
- perfetto::Tracing::NewTrace(mFdp.PickValueInArray<perfetto::BackendType>(backendTypes));
+ auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
tracingSession->Setup(cfg);
return tracingSession;
}
@@ -115,11 +108,15 @@
std::vector<int32_t> layerIds =
generateLayerIds(mFdp.ConsumeIntegralInRange<size_t>(kMinLayerIds, kMaxLayerIds));
+ std::unique_ptr<perfetto::TracingSession> tracingSession;
while (mFdp.remaining_bytes()) {
auto invokeFrametracerAPI = mFdp.PickValueInArray<const std::function<void()>>({
[&]() { mFrameTracer->registerDataSource(); },
[&]() {
- auto tracingSession = getTracingSessionForTest();
+ if (tracingSession) {
+ tracingSession->StopBlocking();
+ }
+ tracingSession = getTracingSessionForTest();
tracingSession->StartBlocking();
},
[&]() { traceTimestamp(layerIds, layerIds.size()); },
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 9889cb9..6c8972f 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -29,12 +29,12 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
+#include <common/FlagManager.h>
#include <configstore/Utils.h>
#include <displayservice/DisplayService.h>
#include <errno.h>
#include <hidl/LegacySupport.h>
#include <processgroup/sched_policy.h>
-#include "FlagManager.h"
#include "SurfaceFlinger.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceFlingerProperties.h"
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index bb3c94a..71c59b2 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -71,6 +71,14 @@
}
flag {
+ name: "add_sf_skipped_frames_to_trace"
+ namespace: "core_graphics"
+ description: "Add SurfaceFlinger dropped Frames to frame timeline"
+ bug: "273701290"
+ is_fixed_read_only: true
+}
+
+flag {
name: "refresh_rate_overlay_on_external_display"
namespace: "core_graphics"
description: "enable refresh rate indicator on the external display"
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 9269e7c..c9af432 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -47,7 +47,6 @@
ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- mCaptureArgs.displayToken = mDisplay;
}
virtual void TearDown() {
@@ -279,8 +278,6 @@
const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
sp<SurfaceControl> mBlackBgSurface;
-
- DisplayCaptureArgs mCaptureArgs;
ScreenCaptureResults mCaptureResults;
private:
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 96cc333..79864e0 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -15,6 +15,8 @@
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <sys/types.h>
+#include <cstdint>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -31,26 +33,22 @@
LayerTransactionTest::SetUp();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
- const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
- ASSERT_FALSE(ids.empty());
- mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
- ASSERT_FALSE(mDisplayToken == nullptr);
-
- ui::DisplayMode mode;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &mode));
- const ui::Size& resolution = mode.resolution;
-
- mDisplaySize = resolution;
+ // Root surface
+ mRootSurfaceControl =
+ createLayer(String8("RootTestSurface"), mDisplayWidth, mDisplayHeight, 0);
+ ASSERT_TRUE(mRootSurfaceControl != nullptr);
+ ASSERT_TRUE(mRootSurfaceControl->isValid());
// Background surface
- mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
- resolution.getHeight(), 0);
+ mBGSurfaceControl = createLayer(String8("BG Test Surface"), mDisplayWidth, mDisplayHeight,
+ 0, mRootSurfaceControl.get());
ASSERT_TRUE(mBGSurfaceControl != nullptr);
ASSERT_TRUE(mBGSurfaceControl->isValid());
TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
// Foreground surface
- mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+ mFGSurfaceControl =
+ createLayer(String8("FG Test Surface"), 64, 64, 0, mRootSurfaceControl.get());
ASSERT_TRUE(mFGSurfaceControl != nullptr);
ASSERT_TRUE(mFGSurfaceControl->isValid());
@@ -58,7 +56,7 @@
TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
+ t.setDisplayLayerStack(mDisplay, ui::DEFAULT_LAYER_STACK);
t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
@@ -66,25 +64,22 @@
.setPosition(mFGSurfaceControl, 64, 64)
.show(mFGSurfaceControl);
});
+
+ mCaptureArgs.sourceCrop = mDisplayRect;
+ mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
}
virtual void TearDown() {
LayerTransactionTest::TearDown();
mBGSurfaceControl = 0;
mFGSurfaceControl = 0;
-
- // Restore display rotation
- asTransaction([&](Transaction& t) {
- Rect displayBounds{mDisplaySize};
- t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, displayBounds, displayBounds);
- });
}
+ sp<SurfaceControl> mRootSurfaceControl;
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mFGSurfaceControl;
std::unique_ptr<ScreenCapture> mCapture;
- sp<IBinder> mDisplayToken;
- ui::Size mDisplaySize;
+ LayerCaptureArgs mCaptureArgs;
};
TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) {
@@ -92,7 +87,8 @@
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32,
ISurfaceComposerClient::eSecure |
- ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mRootSurfaceControl.get()));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
@@ -100,14 +96,14 @@
{
// Ensure the UID is not root because root has all permissions
UIDFaker f(AID_APP_START);
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
}
UIDFaker f(AID_SYSTEM);
// By default the system can capture screenshots with secure layers but they
// will be blacked out
- ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
{
SCOPED_TRACE("as system");
@@ -117,10 +113,8 @@
// Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
// to receive them...we are expected to take care with the results.
- DisplayCaptureArgs args;
- args.displayToken = mDisplay;
- args.captureSecureLayers = true;
- ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+ mCaptureArgs.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
@@ -131,7 +125,8 @@
ASSERT_NO_FATAL_FAILURE(
parentLayer = createLayer("parent-test", 32, 32,
ISurfaceComposerClient::eSecure |
- ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mRootSurfaceControl.get()));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parentLayer, Color::RED, 32, 32));
sp<SurfaceControl> childLayer;
@@ -152,10 +147,8 @@
// Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
// to receive them...we are expected to take care with the results.
- DisplayCaptureArgs args;
- args.displayToken = mDisplay;
- args.captureSecureLayers = true;
- ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+ mCaptureArgs.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
sc.expectColor(Rect(0, 0, 10, 10), Color::BLUE);
@@ -232,7 +225,7 @@
TEST_F(ScreenCaptureTest, CaptureLayerExcludeThroughDisplayArgs) {
mCaptureArgs.excludeHandles = {mFGSurfaceControl->getHandle()};
- ScreenCapture::captureDisplay(&mCapture, mCaptureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectBGColor(0, 0);
// Doesn't capture FG layer which is at 64, 64
mCapture->expectBGColor(64, 64);
@@ -605,60 +598,55 @@
mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
}
-TEST_F(ScreenCaptureTest, CaptureDisplayWithUid) {
- uid_t fakeUid = 12345;
+TEST_F(ScreenCaptureTest, ScreenshotProtectedBuffer) {
+ const uint32_t bufferWidth = 60;
+ const uint32_t bufferHeight = 60;
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
+ sp<SurfaceControl> layer =
+ createLayer(String8("Colored surface"), bufferWidth, bufferHeight,
+ ISurfaceComposerClient::eFXSurfaceBufferState, mRootSurfaceControl.get());
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
- ISurfaceComposerClient::eFXSurfaceBufferQueue,
- mBGSurfaceControl.get()));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
- Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+ sp<Surface> surface = layer->getSurface();
+ ASSERT_TRUE(surface != nullptr);
+ sp<ANativeWindow> anw(surface);
- // Make sure red layer with the background layer is screenshot.
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
- mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
- mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), GRALLOC_USAGE_PROTECTED));
- // From non system uid, can't request screenshot without a specified uid.
- UIDFaker f(fakeUid);
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(captureArgs, mCaptureResults));
+ int fenceFd;
+ ANativeWindowBuffer* buf = nullptr;
- // Make screenshot request with current uid set. No layers were created with the current
- // uid so screenshot will be black.
- captureArgs.uid = fakeUid;
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
- mCapture->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- mCapture->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ // End test if device does not support USAGE_PROTECTED
+ // b/309965549 This check does not exit the test when running on AVDs
+ status_t err = anw->dequeueBuffer(anw.get(), &buf, &fenceFd);
+ if (err) {
+ return;
+ }
+ anw->queueBuffer(anw.get(), buf, fenceFd);
- sp<SurfaceControl> layerWithFakeUid;
- // Create a new layer with the current uid
- ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
- createLayer("new test layer", 32, 32,
- ISurfaceComposerClient::eFXSurfaceBufferQueue,
- mBGSurfaceControl.get()));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
- Transaction()
- .show(layerWithFakeUid)
- .setLayer(layerWithFakeUid, INT32_MAX)
- .setPosition(layerWithFakeUid, 128, 128)
- .apply();
+ // USAGE_PROTECTED buffer is read as a black screen
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults));
- // Screenshot from the fakeUid caller with the uid requested allows the layer
- // with that uid to be screenshotted. Everything else is black
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
- mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
- mCapture->expectBorder(Rect(128, 128, 160, 160), Color::BLACK);
+ ScreenCapture sc(captureResults.buffer, captureResults.capturedHdrLayers);
+ sc.expectColor(Rect(0, 0, bufferWidth, bufferHeight), Color::BLACK);
+
+ // Reading color data will expectedly result in crash, only check usage bit
+ // b/309965549 Checking that the usage bit is protected does not work for
+ // devices that do not support usage protected.
+ mCaptureArgs.allowProtected = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults));
+ // ASSERT_EQ(GRALLOC_USAGE_PROTECTED, GRALLOC_USAGE_PROTECTED &
+ // captureResults.buffer->getUsage());
}
-TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) {
+TEST_F(ScreenCaptureTest, CaptureLayer) {
sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ mRootSurfaceControl.get()));
const Color layerColor = Color::RED;
const Rect bounds = Rect(10, 10, 40, 40);
@@ -666,17 +654,13 @@
Transaction()
.show(layer)
.hide(mFGSurfaceControl)
- .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
.setCrop(layer, bounds)
.apply();
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
-
{
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(bounds, layerColor);
mCapture->expectBorder(bounds, {63, 63, 195, 255});
}
@@ -689,17 +673,18 @@
{
// Can't screenshot test layer since it now has flag
// eLayerSkipScreenshot
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(bounds, {63, 63, 195, 255});
mCapture->expectBorder(bounds, {63, 63, 195, 255});
}
}
-TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) {
+TEST_F(ScreenCaptureTest, CaptureLayerChild) {
sp<SurfaceControl> layer;
sp<SurfaceControl> childLayer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ mRootSurfaceControl.get()));
ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("test layer", 0, 0,
ISurfaceComposerClient::eFXSurfaceEffect,
layer.get()));
@@ -713,7 +698,6 @@
.show(layer)
.show(childLayer)
.hide(mFGSurfaceControl)
- .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
.setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
@@ -721,11 +705,8 @@
.setCrop(childLayer, childBounds)
.apply();
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
-
{
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(childBounds, childColor);
mCapture->expectBorder(childBounds, layerColor);
mCapture->expectBorder(bounds, {63, 63, 195, 255});
@@ -739,7 +720,7 @@
{
// Can't screenshot child layer since the parent has the flag
// eLayerSkipScreenshot
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(childBounds, {63, 63, 195, 255});
mCapture->expectBorder(childBounds, {63, 63, 195, 255});
mCapture->expectBorder(bounds, {63, 63, 195, 255});
@@ -860,14 +841,10 @@
Transaction().show(layer).hide(mFGSurfaceControl).reparent(layer, nullptr).apply();
- DisplayCaptureArgs displayCaptureArgs;
- displayCaptureArgs.displayToken = mDisplay;
-
{
// Validate that the red layer is not on screen
- ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
- mCapture->expectColor(Rect(0, 0, mDisplaySize.width, mDisplaySize.height),
- {63, 63, 195, 255});
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
+ mCapture->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), {63, 63, 195, 255});
}
LayerCaptureArgs captureArgs;
@@ -878,42 +855,6 @@
mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
-TEST_F(ScreenCaptureTest, CaptureDisplayWith90DegRotation) {
- asTransaction([&](Transaction& t) {
- Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
- t.setDisplayProjection(mDisplayToken, ui::ROTATION_90, newDisplayBounds, newDisplayBounds);
- });
-
- DisplayCaptureArgs displayCaptureArgs;
- displayCaptureArgs.displayToken = mDisplayToken;
- displayCaptureArgs.width = mDisplaySize.width;
- displayCaptureArgs.height = mDisplaySize.height;
- displayCaptureArgs.useIdentityTransform = true;
- ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
-
- mCapture->expectBGColor(0, 0);
- mCapture->expectFGColor(mDisplaySize.width - 65, 65);
-}
-
-TEST_F(ScreenCaptureTest, CaptureDisplayWith270DegRotation) {
- asTransaction([&](Transaction& t) {
- Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
- t.setDisplayProjection(mDisplayToken, ui::ROTATION_270, newDisplayBounds, newDisplayBounds);
- });
-
- DisplayCaptureArgs displayCaptureArgs;
- displayCaptureArgs.displayToken = mDisplayToken;
- displayCaptureArgs.width = mDisplaySize.width;
- displayCaptureArgs.height = mDisplaySize.height;
- displayCaptureArgs.useIdentityTransform = true;
- ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
-
- std::this_thread::sleep_for(std::chrono::seconds{5});
-
- mCapture->expectBGColor(mDisplayWidth - 1, mDisplaySize.height - 1);
- mCapture->expectFGColor(65, mDisplaySize.height - 65);
-}
-
TEST_F(ScreenCaptureTest, CaptureNonHdrLayer) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
index d0ab105..c5d118c 100644
--- a/services/surfaceflinger/tests/TextureFiltering_test.cpp
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -80,29 +80,22 @@
sp<SurfaceControl> mParent;
sp<SurfaceControl> mLayer;
std::unique_ptr<ScreenCapture> mCapture;
+ gui::LayerCaptureArgs captureArgs;
};
TEST_F(TextureFilteringTest, NoFiltering) {
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
- captureArgs.sourceCrop = Rect{100, 100};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
}
TEST_F(TextureFilteringTest, BufferCropNoFiltering) {
- Transaction().setBufferCrop(mLayer, Rect{0, 0, 100, 100}).apply();
-
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
captureArgs.sourceCrop = Rect{0, 0, 100, 100};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
@@ -112,24 +105,20 @@
TEST_F(TextureFilteringTest, BufferCropIsFiltered) {
Transaction().setBufferCrop(mLayer, Rect{25, 25, 75, 75}).apply();
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
captureArgs.sourceCrop = Rect{0, 0, 100, 100};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
}
// Expect filtering because the output source crop is stretched to the output buffer's size.
TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) {
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
+ captureArgs.frameScaleX = 2;
+ captureArgs.frameScaleY = 2;
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
}
@@ -138,20 +127,17 @@
// buffer's size.
TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) {
Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
-
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
+ captureArgs.frameScaleX = 2;
+ captureArgs.frameScaleY = 2;
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
}
// Expect filtering because the layer is scaled up.
TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) {
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mLayer->getHandle();
captureArgs.frameScaleX = 2;
captureArgs.frameScaleY = 2;
@@ -162,7 +148,6 @@
// Expect no filtering because the output buffer's size matches the source crop.
TEST_F(TextureFilteringTest, LayerCaptureOutputSourceCropNoFiltering) {
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mLayer->getHandle();
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -176,7 +161,6 @@
TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) {
Transaction().setCrop(mLayer, Rect{10, 10, 90, 90}).apply();
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mLayer->getHandle();
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -187,12 +171,9 @@
// Expect no filtering because the output source crop and output buffer are the same size.
TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 50;
- captureArgs.height = 50;
+ captureArgs.layerHandle = mLayer->getHandle();
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
@@ -202,9 +183,8 @@
TEST_F(TextureFilteringTest, LayerCropDisplayFrameMatchNoFiltering) {
Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
@@ -214,9 +194,8 @@
TEST_F(TextureFilteringTest, ParentCropNoFiltering) {
Transaction().setCrop(mParent, Rect{25, 25, 75, 75}).apply();
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
@@ -226,7 +205,6 @@
TEST_F(TextureFilteringTest, ParentHasTransformNoFiltering) {
Transaction().setPosition(mParent, 100, 100).apply();
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mParent->getHandle();
captureArgs.sourceCrop = Rect{0, 0, 100, 100};
ScreenCapture::captureLayers(&mCapture, captureArgs);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 5a3bca1..dea0194 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -45,7 +45,7 @@
cc_aconfig_library {
name: "libsurfaceflingerflags_test",
aconfig_declarations: "surfaceflinger_flags",
- test: true,
+ mode: "test",
}
cc_test {
@@ -170,6 +170,7 @@
"librenderengine_mocks",
"libscheduler",
"libserviceutils",
+ "libsurfaceflinger_common_test",
"libtimestats",
"libtimestats_atoms_proto",
"libtimestats_proto",
diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index aa37754..c040f29 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -17,7 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "FlagManagerTest"
-#include "FlagManager.h"
+#include <common/FlagManager.h>
#include "FlagUtils.h"
#include <gmock/gmock.h>
diff --git a/services/surfaceflinger/tests/unittests/FlagUtils.h b/services/surfaceflinger/tests/unittests/FlagUtils.h
index 333e4e7..550c70d 100644
--- a/services/surfaceflinger/tests/unittests/FlagUtils.h
+++ b/services/surfaceflinger/tests/unittests/FlagUtils.h
@@ -16,7 +16,7 @@
#pragma once
-#include "FlagManager.h"
+#include <common/FlagManager.h>
#define SET_FLAG_FOR_TEST(name, value) TestFlagSetter _testflag_((name), (name), (value))
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index e040028..6d87717 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "FlagUtils.h"
+#include "com_android_graphics_surfaceflinger_flags.h"
#include "gmock/gmock-spec-builders.h"
#include "mock/MockTimeStats.h"
#undef LOG_TAG
@@ -1059,6 +1061,9 @@
}
TEST_F(FrameTimelineTest, traceDisplayFrameSkipped) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::add_sf_skipped_frames_to_trace,
+ true);
+
// setup 2 display frames
// DF 1: [22,40] -> [5, 40]
// DF : [36, 70] (Skipped one, added by the trace)
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 58d7a40..f1e841b 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -30,6 +30,7 @@
#include <gmock/gmock.h>
#pragma clang diagnostic pop
+#include <common/FlagManager.h>
#include <gui/LayerMetadata.h>
#include <log/log.h>
#include <chrono>
@@ -38,9 +39,12 @@
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
#include "DisplayIdentificationTestHelpers.h"
+#include "FlagUtils.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockHWC2.h"
+#include <com_android_graphics_surfaceflinger_flags.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
@@ -57,7 +61,6 @@
using hal::IComposerClient;
using ::testing::_;
using ::testing::DoAll;
-using ::testing::ElementsAreArray;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::StrictMock;
@@ -217,7 +220,102 @@
}
}
-TEST_F(HWComposerTest, getModesWithDisplayConfigurations) {
+TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) {
+ // if vrr_config is off, getDisplayConfigurationsSupported() is off as well
+ // then getModesWithLegacyDisplayConfigs should be called instead
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, false);
+ ASSERT_FALSE(FlagManager::getInstance().vrr_config());
+
+ constexpr hal::HWDisplayId kHwcDisplayId = 2;
+ constexpr hal::HWConfigId kConfigId = 42;
+ constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
+
+ expectHotplugConnect(kHwcDisplayId);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(info);
+
+ EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false));
+
+ {
+ EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+ .WillOnce(Return(HalError::BAD_DISPLAY));
+ EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty());
+ }
+ {
+ constexpr int32_t kWidth = 480;
+ constexpr int32_t kHeight = 720;
+ constexpr int32_t kConfigGroup = 1;
+ constexpr int32_t kVsyncPeriod = 16666667;
+
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH,
+ _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kWidth), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kHeight), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::CONFIG_GROUP, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kConfigGroup), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::VSYNC_PERIOD, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kVsyncPeriod), Return(HalError::NONE)));
+
+ // Optional Parameters UNSUPPORTED
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+ _))
+ .WillOnce(Return(HalError::UNSUPPORTED));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+ _))
+ .WillOnce(Return(HalError::UNSUPPORTED));
+
+ EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{kConfigId}),
+ Return(HalError::NONE)));
+
+ auto modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+
+ // Optional parameters are supported
+ constexpr int32_t kDpi = 320;
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+ _))
+ .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+ _))
+ .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+
+ modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ // DPI values are scaled by 1000 in the legacy implementation.
+ EXPECT_EQ(modes.front().dpiX, kDpi / 1000.f);
+ EXPECT_EQ(modes.front().dpiY, kDpi / 1000.f);
+ }
+}
+
+TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, true);
+ ASSERT_TRUE(FlagManager::getInstance().vrr_config());
+
constexpr hal::HWDisplayId kHwcDisplayId = 2;
constexpr hal::HWConfigId kConfigId = 42;
constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index faa12a1..a00e886 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -1568,7 +1568,7 @@
// These layers cannot change mode due to smoothSwitchOnly, and will definitely use
// active mode (120Hz).
{FrameRateCategory::NoPreference, true, 120_Hz, kModeId120},
- {FrameRateCategory::Low, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Low, true, 120_Hz, kModeId120},
{FrameRateCategory::Normal, true, 40_Hz, kModeId120},
{FrameRateCategory::High, true, 120_Hz, kModeId120},
};
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index a87f82f..436e6c6 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -109,6 +109,7 @@
"libnativeloader_lazy",
"libnativewindow",
"libvndksupport",
+ "libdl_android",
"android.hardware.graphics.common@1.0",
"libSurfaceFlingerProp",
],
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 5d7a4aa..0e45d2d 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -46,6 +46,8 @@
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
+extern "C" android_namespace_t* android_get_exported_namespace(const char*);
+
// #define ENABLE_ALLOC_CALLSTACKS 1
#if ENABLE_ALLOC_CALLSTACKS
#include <utils/CallStack.h>
@@ -159,6 +161,7 @@
"ro.board.platform",
}};
constexpr int LIB_DL_FLAGS = RTLD_LOCAL | RTLD_NOW;
+constexpr char RO_VULKAN_APEX_PROPERTY[] = "ro.vulkan.apex";
// LoadDriver returns:
// * 0 when succeed, or
@@ -166,6 +169,7 @@
// * -EINVAL when fail to find HAL_MODULE_INFO_SYM_AS_STR or
// HWVULKAN_HARDWARE_MODULE_ID in the library.
int LoadDriver(android_namespace_t* library_namespace,
+ const char* ns_name,
const hwvulkan_module_t** module) {
ATRACE_CALL();
@@ -183,8 +187,10 @@
.library_namespace = library_namespace,
};
so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo);
- ALOGE("Could not load %s from updatable gfx driver namespace: %s.",
- lib_name.c_str(), dlerror());
+ if (!so) {
+ ALOGE("Could not load %s from %s namespace: %s.",
+ lib_name.c_str(), ns_name, dlerror());
+ }
} else {
// load built-in driver
so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS);
@@ -211,12 +217,30 @@
return 0;
}
+int LoadDriverFromApex(const hwvulkan_module_t** module) {
+ ATRACE_CALL();
+
+ auto apex_name = android::base::GetProperty(RO_VULKAN_APEX_PROPERTY, "");
+ if (apex_name == "") {
+ return -ENOENT;
+ }
+ // Get linker namespace for Vulkan APEX
+ std::replace(apex_name.begin(), apex_name.end(), '.', '_');
+ auto ns = android_get_exported_namespace(apex_name.c_str());
+ if (!ns) {
+ return -ENOENT;
+ }
+ android::GraphicsEnv::getInstance().setDriverToLoad(
+ android::GpuStatsInfo::Driver::VULKAN);
+ return LoadDriver(ns, apex_name.c_str(), module);
+}
+
int LoadBuiltinDriver(const hwvulkan_module_t** module) {
ATRACE_CALL();
android::GraphicsEnv::getInstance().setDriverToLoad(
android::GpuStatsInfo::Driver::VULKAN);
- return LoadDriver(nullptr, module);
+ return LoadDriver(nullptr, nullptr, module);
}
int LoadUpdatedDriver(const hwvulkan_module_t** module) {
@@ -227,7 +251,7 @@
return -ENOENT;
android::GraphicsEnv::getInstance().setDriverToLoad(
android::GpuStatsInfo::Driver::VULKAN_UPDATED);
- int result = LoadDriver(ns, module);
+ int result = LoadDriver(ns, "updatable gfx driver", module);
if (result != 0) {
LOG_ALWAYS_FATAL(
"couldn't find an updated Vulkan implementation from %s",
@@ -256,6 +280,9 @@
result = LoadUpdatedDriver(&module);
if (result == -ENOENT) {
+ result = LoadDriverFromApex(&module);
+ }
+ if (result == -ENOENT) {
result = LoadBuiltinDriver(&module);
}
if (result != 0) {
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 0c48611..4f7b0f4 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1485,10 +1485,33 @@
return VK_SUCCESS;
}
+ // Look through the create_info pNext chain passed to createSwapchainKHR
+ // for an image compression control struct.
+ // if one is found AND the appropriate extensions are enabled, create a
+ // VkImageCompressionControlEXT structure to pass on to GetPhysicalDeviceImageFormatProperties2
+ void* compression_control_pNext = nullptr;
+ VkImageCompressionControlEXT image_compression = {};
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ compression_control_pNext = &image_compression;
+ } break;
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+
// call GetPhysicalDeviceImageFormatProperties2KHR
VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
- .pNext = nullptr,
+ .pNext = compression_control_pNext,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};