diff options
142 files changed, 4496 insertions, 1308 deletions
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index d7c9b40d92..e14af77194 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -471,6 +471,49 @@ status_t InstalldNativeService::dump(int fd, const Vector<String16>& /* args */) 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 @@ status_t InstalldNativeService::dump(int fd, const Vector<String16>& /* args */) 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 @@ binder::Status InstalldNativeService::linkNativeLibraryDirectory( } 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 55326ea737..de0e1718ab 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 @@ static void ShowUsage() { } } -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 @@ int FrameRateIndicator([[maybe_unused]] int argc, [[maybe_unused]] char** argv) 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 @@ int scheduleCommit([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { 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 1f477f8233..0b57e9376d 100644 --- a/include/android/thermal.h +++ b/include/android/thermal.h @@ -111,7 +111,7 @@ typedef struct AThermalManager AThermalManager; * 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 @@ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, 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 0000000000..951d953267 --- /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 620c23c1bb..f90c618a94 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -251,13 +251,15 @@ cc_library_shared { 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 824323857d..301dbf6cdf 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 @@ status_t BBinder::startRecordingTransactions(const Parcel& data) { 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 @@ status_t BBinder::startRecordingTransactions(const Parcel& data) { return readStatus; } mRecordingOn = true; - LOG(INFO) << "Started Binder recording."; + ALOGI("Started Binder recording."); return NO_ERROR; } } @@ -340,10 +339,10 @@ status_t BBinder::stopRecordingTransactions() { 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 @@ status_t BBinder::transact( 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 @@ status_t BBinder::transact( 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 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, 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 @@ BBinder::~BBinder() { 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 @@ status_t BBinder::onTransact( { 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 a1fbbf321c..895690f9b8 100644 --- a/libs/binder/FdTrigger.cpp +++ b/libs/binder/FdTrigger.cpp @@ -53,7 +53,7 @@ bool FdTrigger::isTriggered() { #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 cedd3af289..f7b5a55a31 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 @@ std::optional<RecordedTransaction> RecordedTransaction::fromDetails( 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 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd 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 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd 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 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd 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 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd 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 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd 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 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd 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 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd case END_CHUNK: break; default: - LOG(INFO) << "Unrecognized chunk."; + ALOGI("Unrecognized chunk."); break; } } while (chunk.chunkType != END_CHUNK); @@ -292,7 +289,7 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd 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 @@ android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunk // 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 @@ android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { 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 1ba20b3103..fefaa810d2 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -168,7 +168,7 @@ void RpcServer::setConnectionFilter(std::function<bool(const void*, size_t)>&& f 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 @@ void RpcServer::start() { 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 c895b21f91..cd8f41711f 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -208,7 +208,7 @@ status_t RpcSession::addNullDebuggingClient() { 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 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, 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 3b53b05991..2f2fe7d276 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 @@ sp<RpcSession> RpcTrustyConnectWithSessionInitializer( 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 47fd17dcb1..d9a96af80a 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 @@ void zeroMemory(uint8_t* data, size_t size) { } 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 c8431aab43..eec09eb859 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 f51cd9bc99..118409eeff 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 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in } 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 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in 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 @@ ARpcServer* ARpcServer_newBoundSocket(AIBinder* service, int socketFd) { 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 @@ ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd 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_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd 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 @@ void ARpcSession_free(ARpcSession* handle) { 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 @@ AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* handle, const char* nam 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 @@ AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* handle, int bo 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_setupUnixDomainBootstrapClient(ARpcSession* handle, int bo 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 @@ AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*request 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 47da296b70..bf7a0ba5f0 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 @@ static void* kValue = static_cast<void*>(new bool{true}); 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 @@ struct Value { }; 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 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { 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 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { // 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 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { 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 @@ status_t ABBinder::dump(int fd, const ::android::Vector<String16>& args) { // 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 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce 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 @@ AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor, } 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 @@ AIBinder_DeathRecipient::TransferDeathRecipient::~TransferDeathRecipient() { } 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 @@ void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinde 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 @@ void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinde 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 @@ void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() { } 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::linkToDeath(const sp<IBinder>& binder, } 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 @@ binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& binder 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 @@ void AIBinder_DeathRecipient::setOnUnlinked(AIBinder_DeathRecipient_onBinderUnli 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_dump(AIBinder* binder, int fd, const char** args, uint3 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_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* 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_incStrong(AIBinder* binder) { } 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 @@ void AIBinder_decStrong(AIBinder* binder) { } 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 @@ void* AIBinder_getUserData(AIBinder* binder) { 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 @@ static void DestroyParcel(AParcel** parcel) { 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 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa 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 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa 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 @@ binder_status_t AIBinder_setExtension(AIBinder* binder, AIBinder* ext) { 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_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) 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 037aa2e120..c15bcf968d 100644 --- a/libs/binder/ndk/parcel.cpp +++ b/libs/binder/ndk/parcel.cpp @@ -14,21 +14,20 @@ * 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 <limits> - -#include <android-base/logging.h> -#include <android-base/unique_fd.h> #include <binder/Parcel.h> #include <binder/ParcelFileDescriptor.h> +#include <inttypes.h> #include <utils/Unicode.h> +#include <limits> + +#include "ibinder_internal.h" +#include "parcel_internal.h" +#include "status_internal.h" + using ::android::IBinder; using ::android::Parcel; using ::android::sp; @@ -52,11 +51,11 @@ static binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArr 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_readStatusHeader(const AParcel* parcel, AStatus** status 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 @@ binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t } if (length < 0) { - LOG(WARNING) << __func__ << ": Negative string length: " << length; + ALOGW("Negative string length: %" PRIi32, length); return STATUS_BAD_VALUE; } @@ -342,7 +341,7 @@ binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t 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 @@ binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, } 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 @@ binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, 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 0fea57b0a7..0072ac3d3e 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 29777866e2..3bfdc59ec2 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 @@ struct AServiceManager_NotificationRegistration 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 @@ __attribute__((warn_unused_result)) AServiceManager_NotificationRegistration* 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 @@ AServiceManager_registerForServiceNotifications(const char* instance, 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 @@ AServiceManager_registerForServiceNotifications(const char* instance, 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 @@ bool AServiceManager_isDeclared(const char* instance) { } 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 @@ bool AServiceManager_isUpdatableViaApex(const char* instance) { } 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 8ed91a5314..3aac3c0545 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 @@ binder_status_t PruneStatusT(status_t status) { 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 @@ binder_exception_t PruneException(int32_t exception) { 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 78f8877c1d..6d122c5388 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -27,7 +27,7 @@ use std::cmp::Ordering; 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 @@ pub trait Interface: Send + Sync + DowncastSync { /// /// 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 @@ pub trait Remotable: Send + Sync + 'static { /// 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 @@ macro_rules! declare_binder_interface { } } - 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 b248f5eb28..b250012801 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -25,6 +25,7 @@ use crate::sys; 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 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { } // 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 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { // 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 @@ impl Remotable for () { 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 c049b807df..c87fa89756 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -26,7 +26,7 @@ use binder::binder_impl::{ 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 TryFrom<u32> for TestTransactionCode { } 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 bbb310a538..624edba9cd 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -87,8 +87,8 @@ public: 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 @@ static base::unique_fd initUnixSocket(std::string addr) { 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 @@ public: 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 connectTo(const RpcSocketAddress& addr) { 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 @@ static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) 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 @@ std::unique_ptr<RpcTransportCtxFactory> BinderRpc::newFactory(RpcSecurity rpcSec // 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 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( // 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 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( 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 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( } 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 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( ? 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 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( 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 @@ TEST_P(BinderRpc, OnewayCallQueueingWithFds) { 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 @@ TEST_P(BinderRpc, ReceiveFile) { 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 @@ TEST_P(BinderRpc, SendFiles) { 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 @@ TEST_P(BinderRpc, SendMaxFiles) { 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 @@ bool testSupportVsockLoopback() { 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 @@ bool testSupportVsockLoopback() { // 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 @@ private: }; 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 @@ TEST(BinderRpc, Java) { 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 @@ public: iovec iov{&buf, sizeof(buf)}; if (binder::os::receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) { - int savedErrno = errno; - LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno); - } - if (fds.size() != 1) { - LOG(FATAL) << "Expected one FD from receiveMessage(), got " << fds.size(); + PLOGF("Failed receiveMessage"); } + 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 @@ INSTANTIATE_TEST_CASE_P( 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 c3070ddd14..b2b63e4bf3 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 @@ struct BinderRpcOptions { #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 @@ template <typename T> 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 @@ static inline std::unique_ptr<RpcTransportCtxFactory> newTlsFactory( // 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 7435f308d1..5025bd617f 100644 --- a/libs/binder/tests/binderRpcTestService.cpp +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -65,7 +65,7 @@ public: 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 @@ public: }; 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 @@ int main(int argc, char* argv[]) { 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 @@ int main(int argc, char* argv[]) { 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 @@ int main(int argc, char* argv[]) { 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 @@ int main(int argc, char* argv[]) { // 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 1f05ef757a..d2b37aa8f6 100644 --- a/libs/binder/trusty/kernel/rules.mk +++ b/libs/binder/trusty/kernel/rules.mk @@ -24,7 +24,6 @@ LIBUTILS_DIR := system/core/libutils 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 88a1075f60..0000000000 --- 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 2e56cbd21b..9cad556711 100644 --- a/libs/binder/trusty/rules.mk +++ b/libs/binder/trusty/rules.mk @@ -24,7 +24,6 @@ LIBUTILS_DIR := system/core/libutils 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 8d0331ebb5..f317a2eea0 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::addAndGetFrameTimestamps(const NewFrameEventsEntry } } -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 @@ void BLASTBufferItemConsumer::updateFrameTimestamps(uint64_t frameNumber, nsecs_ 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 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence 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 29d64afb24..f5d19aac78 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 FrameEventHistoryStats::writeToParcel(Parcel* output) const { 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 FrameEventHistoryStats::readFromParcel(const Parcel* input) { 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 a3518110cd..922b0ddcde 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -3122,12 +3122,12 @@ status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs, 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 2eb6bd670d..6a4460b650 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -66,8 +66,9 @@ bool WindowInfo::overlaps(const WindowInfo* other) const { 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 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { 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_t WindowInfo::readFromParcel(const android::Parcel* parcel) { 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 0000000000..920d94980a --- /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 a7cf5ddeb4..d24f8eefd5 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 @@ interface ISurfaceComposer { * 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 @@ interface ISurfaceComposer { 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 17f4c630ce..2e270b721f 100644 --- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp +++ b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp @@ -141,8 +141,8 @@ void BufferQueueFuzzer::invokeBlastBufferQueue() { 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 3142103d17..ffe7e4123b 100644 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h @@ -100,8 +100,8 @@ public: 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 @@ public: 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 892215ec32..0e1a505c69 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -50,8 +50,8 @@ public: 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 364a57b8dd..bc97cd0828 100644 --- a/libs/gui/include/gui/ITransactionCompletedListener.h +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -95,15 +95,18 @@ public: 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 54c3aa7c1c..5bf6c473d9 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -841,8 +841,14 @@ private: 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 bd2eb7413b..dcc38d7564 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 @@ struct WindowInfo : public Parcelable { /* 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 a16be786be..b081030c9f 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -8,3 +8,10 @@ flag { 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 9893c7146e..ea7078dd05 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 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { 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 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { 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 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { 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 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { 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 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { 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 86ced2ccf2..60221aa30a 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -208,21 +208,6 @@ protected: 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 @@ TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) { 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 @@ public: 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 @@ public: 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 f2feaefa13..5eb5d3bff0 100644 --- a/libs/gui/tests/WindowInfo_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -28,6 +28,7 @@ namespace android { using gui::InputApplicationInfo; using gui::TouchOcclusionMode; using gui::WindowInfo; +using ui::Size; namespace test { @@ -53,6 +54,7 @@ TEST(WindowInfo, Parcelling) { 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 @@ TEST(WindowInfo, Parcelling) { 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 c37db1693a..dd8dc8dc9d 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -50,7 +50,7 @@ cc_aconfig_library { // 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 9a459b135c..db7031ab03 100644 --- a/libs/input/VirtualInputDevice.cpp +++ b/libs/input/VirtualInputDevice.cpp @@ -193,6 +193,7 @@ const std::map<int, int> VirtualKeyboard::KEY_CODE_MAPPING = { {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 3672387119..a807d823f1 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -62,3 +62,10 @@ flag { 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 5f05a0f454..b60d7c971e 100644 --- a/libs/input/rust/input_verifier.rs +++ b/libs/input/rust/input_verifier.rs @@ -124,10 +124,7 @@ impl InputVerifier { 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 @@ impl InputVerifier { 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 538c1d2a42..4246c40f64 100644 --- a/libs/ui/FenceTime.cpp +++ b/libs/ui/FenceTime.cpp @@ -363,9 +363,9 @@ void FenceToFenceTimeMap::signalAllForTest( } 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 @@ void FenceToFenceTimeMap::garbageCollectLocked() { // 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 b15694bf50..4454f36b67 100644 --- a/opengl/Android.bp +++ b/opengl/Android.bp @@ -72,6 +72,10 @@ cc_library_headers { 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 ed3c616b92..9905210014 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 @@ void freeHotCacheEntry(android::MultifileHotCache& entry) { 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 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s 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 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s 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 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s 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 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s 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 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s 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 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s 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 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s // 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 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s // 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 @@ void MultifileBlobCache::set(const void* key, EGLsizeiANDROID keySize, const voi 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 @@ void MultifileBlobCache::finish() { } } +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 @@ MultifileEntryStats MultifileBlobCache::getEntryStats(uint32_t entryHash) { 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 @@ bool MultifileBlobCache::removeFromHotCache(uint32_t entryHash) { 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 @@ bool MultifileBlobCache::applyLRU(size_t cacheLimit) { // 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 @@ bool MultifileBlobCache::applyLRU(size_t cacheLimit) { 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 @@ void MultifileBlobCache::trimCache() { 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 5e527dcf35..18566c2892 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 @@ struct MultifileEntryStats { 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 @@ private: 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 @@ public: 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 @@ private: 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 @@ private: 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 @@ private: 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 1639be6480..90a0f1ee06 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 @@ using sp = std::shared_ptr<T>; 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, CacheMaxKeyAndValueSizeSucceeds) { } } +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 @@ int MultifileBlobCacheTest::getFileDescriptorCount() { 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 @@ TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) { } // 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 1b68344cb1..98ff1d12cc 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -41,6 +41,7 @@ static const unsigned int kDeferredMonolithicSaveDelay = 4; 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 @@ MultifileBlobCache* egl_cache_t::getMultifileBlobCacheLocked() { 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 55a2682ac0..1ada33ed6e 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -64,11 +64,6 @@ bool findExtension(const char* exts, const char* name, size_t nameLen) { 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 @@ EGLBoolean egl_display_t::initialize(EGLint* major, EGLint* minor) { 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 87c2176045..867a1173b9 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -39,7 +39,6 @@ class egl_context_t; 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 440eb17873..a6af713830 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -1644,26 +1644,6 @@ EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, 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 @@ EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, 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 633cc9c51b..2d9ee3a49e 100644 --- a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp +++ b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp @@ -28,6 +28,7 @@ namespace android { 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 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { 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 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // 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 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // 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 f81c68f66e..ce5818229a 100644 --- a/opengl/tests/EGLTest/egl_cache_test.cpp +++ b/opengl/tests/EGLTest/egl_cache_test.cpp @@ -114,25 +114,26 @@ std::string EGLCacheTest::getCachefileName() { 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 b213f9aeba..45c9b5cf0b 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -76,6 +76,7 @@ filegroup { srcs: [ "InputCommonConverter.cpp", "InputDeviceMetricsCollector.cpp", + "InputFilter.cpp", "InputProcessor.cpp", "PointerChoreographer.cpp", "PreferStylusOverTouchBlocker.cpp", @@ -243,6 +244,9 @@ phony { "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 0000000000..1b8fad3c65 --- /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 0000000000..699f3a0151 --- /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 92c65e19b6..8cf61f9064 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -42,6 +42,7 @@ const bool ENABLE_INPUT_DEVICE_USAGE_METRICS = 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 @@ std::shared_ptr<IInputFlingerRust> createInputFlingerRust() { * The event flow is via the "InputListener" interface, as follows: * InputReader * -> UnwantedInteractionBlocker + * -> InputFilter * -> PointerChoreographer * -> InputProcessor * -> InputDeviceMetricsCollector @@ -132,6 +134,12 @@ InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, 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 20b9fd50df..aea7bd56ae 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 @@ private: 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 e529bddea5..f1faf69758 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -31,6 +31,11 @@ bool isFromMouse(const NotifyMotionArgs& args) { 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 @@ NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& arg 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 @@ NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotio << 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 @@ NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotio 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 @@ int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisp 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 26d2fef5c3..90a0d3fc72 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -95,10 +95,13 @@ private: 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 0f6232477e..1e2b9b3ad3 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.cpp +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -67,6 +67,16 @@ const bool DEBUG_OUTBOUND_MOTION = 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 @@ void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs& args) { 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 0000000000..44f959ef63 --- /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 8e826fda44..de852c0c24 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 IInputFlingerRust { 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 0000000000..e213221d44 --- /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 2d1a22b3e6..cc0d49c15e 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -139,15 +139,15 @@ KeyEntry::KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, n 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 @@ std::string SensorEntry::getDescription() const { 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 @@ DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, 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 @@ uint32_t DispatchEntry::nextSeq() { } 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 d44a211535..e2e13c3fde 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -50,7 +50,7 @@ struct EventEntry { 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 @@ struct EventEntry { 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 @@ struct KeyEntry : EventEntry { 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 @@ struct KeyEntry : EventEntry { 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 TouchModeEntry : EventEntry { 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 @@ struct DispatchEntry { // 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 1958c357d2..7f4de7a5a3 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -295,9 +295,8 @@ std::string dumpQueue(const std::deque<std::unique_ptr<DispatchEntry>>& queue, } 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 @@ size_t firstMarkedBit(T set) { } 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 shouldReportMetricsForConnection(const Connection& connection) { 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 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } 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 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } 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 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { 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 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } 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 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } 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::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt 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 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE // 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 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE 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 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE 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::postCommandLocked(Command&& command) { 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::releasePendingEventLocked() { } } -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 @@ void InputDispatcher::resetKeyRepeatLocked() { } 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 @@ void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bo // 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::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<F } 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 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( 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 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked 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::logOutboundKeyDetails(const char* prefix, const KeyEntry& } 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 @@ bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType 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 @@ bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType 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 @@ void InputDispatcher::enqueueDragEventLocked(const sp<WindowInfoHandle>& windowH 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::logOutboundMotionDetails(const char* prefix, const MotionE } 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::pokeUserActivityLocked(const EventEntry& eventEntry) { 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::prepareDispatchCycleLocked(nsecs_t currentTime, 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::enqueueDispatchEntriesLocked(nsecs_t currentTime, } 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 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio // 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 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio } 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()); + 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 (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; } - // 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; - } - 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 (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 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio 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 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio 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 @@ status_t InputDispatcher::publishMotionEvent(Connection& connection, // 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 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // 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 @@ std::array<uint8_t, 32> InputDispatcher::sign(const VerifiedInputEvent& event) c 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 std::array<uint8_t, 32> InputDispatcher::getSignature( 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 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu 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::transformMotionEntryForInjectionLocked( } } -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 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { // 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 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { // 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 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, 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::updateLastAnrStateLocked(const std::string& windowLabel, } 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 @@ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connec 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 @@ sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow( 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 e9d52cd4ec..8aa4a87341 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -145,7 +145,8 @@ public: // 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 @@ private: 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 @@ private: 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 @@ private: // 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 @@ private: // 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 @@ private: // 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 @@ private: 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 @@ private: // 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 @@ private: 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 @@ private: 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 16cc266938..a4ac4fbe6c 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 @@ bool InputState::isHovering(DeviceId deviceId, uint32_t source, int32_t displayI 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 @@ bool InputState::trackKey(const KeyEntry& entry, int32_t action, int32_t flags) * 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 @@ bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t f 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 @@ size_t InputState::MotionMemento::getPointerCount() const { 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 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry, } 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 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry, 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 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry, 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<EventEntry> InputState::cancelConflictingInputStream(const Motio 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 686c4323a4..b0e4209882 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -41,16 +41,15 @@ public: // 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 @@ private: 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 d099b44d91..bc7b6445ff 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -221,7 +221,8 @@ public: /* * 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 7aeb215174..58e35a6aba 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -527,10 +527,8 @@ void CursorInputMapper::configureOnChangeDisplayInfo(const InputReaderConfigurat 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 9c87c62a7c..2dd05f5c66 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -131,7 +131,7 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { bumpGeneration(); } } - if (shouldSimulateStylusWithTouch() && outPointer.toolType == ToolType::FINGER) { + if (mShouldSimulateStylusWithTouch && outPointer.toolType == ToolType::FINGER) { outPointer.toolType = ToolType::STYLUS; } @@ -177,6 +177,18 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { 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 @@ void MultiTouchInputMapper::configureRawPointerAxes() { 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 1d788dffd4..5c173f365e 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -32,6 +32,9 @@ public: [[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 @@ protected: 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 @@ private: 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 c76fec7280..34ca0b3767 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -246,7 +246,8 @@ TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext, 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 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, 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 a68ae43912..ece0eca0e7 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h @@ -107,6 +107,8 @@ private: // 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 4d2b66d56d..955210479f 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -60,7 +60,8 @@ GestureConverter::GestureConverter(InputReaderContext& readerContext, 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 @@ void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const { // 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 @@ NotifyMotionArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, 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 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ 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::handleButtonsChange(nsecs_t when, nsecs_ 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 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT 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 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi } 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 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { 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 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { 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 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { [[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 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { 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 e6cf617500..732a4b2ffb 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -48,6 +48,8 @@ public: 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 @@ private: 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 23c1691b28..2775bcc962 100644 --- a/services/inputflinger/rust/Android.bp +++ b/services/inputflinger/rust/Android.bp @@ -31,8 +31,8 @@ genrule { 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 @@ rust_ffi_static { 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 0000000000..585187750b --- /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 501e4359cd..a4049d52d1 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 @@ unsafe fn create_inputflinger_rust(callback: *mut ffi::IInputFlingerRustBootstra // 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 @@ struct InputFlingerRust {} 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 5475594113..8043812999 100644 --- a/services/inputflinger/tests/FakePointerController.cpp +++ b/services/inputflinger/tests/FakePointerController.cpp @@ -42,7 +42,10 @@ FloatPoint FakePointerController::getPosition() const { } 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 @@ void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) 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 d7e40b3cc1..9be6a6c7b4 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -36,6 +36,8 @@ public: 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 @@ private: 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 41c7392cbc..d2b68dd93e 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 @@ protected: 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 @@ TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick, 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 91eceb0c4e..e3417826e4 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 @@ namespace android::inputdispatcher { using namespace ftl::flag_operators; using testing::AllOf; +using testing::Not; namespace { @@ -1319,10 +1321,13 @@ public: 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 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyBlocksTouchDown) { /** * 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 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDoesNotBlockTouchDown) { .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 @@ TEST_F(InputDispatcherMultiDeviceTest, TouchIsNotCanceledByStylusHover) { .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 @@ TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceWithSpy) { * 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 @@ TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlockedByTouchWithSpy) { .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 @@ TEST_F(InputDispatcherMultiDeviceTest, HoverTapAndSplitTouch) { * 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 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) { .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 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) { .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 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) { .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 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) { .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 @@ TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithKeys) { 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 @@ TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) { 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 @@ TEST_F(InputDispatcherSingleWindowAnr, // 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 787444c9e6..36be684f96 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -28,10 +28,13 @@ void InputMapperUnitTest::SetUp() { 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 3f9061fc6a..05b0e97ff6 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -55,6 +55,7 @@ protected: 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 5044b3e777..e8b779adf7 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -4922,6 +4922,8 @@ TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPoint // --- 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 68f58571f5..1efb7979f4 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -50,6 +50,9 @@ const auto MOUSE_POINTER = PointerBuilder(/*id=*/0, ToolType::MOUSE) 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 @@ TEST_F(PointerChoreographerTest, SetsViewportForAssociatedMouse) { .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 @@ TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMo .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 @@ TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) { .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 @@ TEST_F(PointerChoreographerTest, .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 @@ TEST_F(PointerChoreographerTest, .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 @@ TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) { 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 @@ TEST_F(PointerChoreographerTest, 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 @@ TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) { 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 @@ TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) { 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 @@ TEST_F(PointerChoreographerTest, WhenShowTouchesDisabledRemovesPointerController 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 @@ TEST_F(PointerChoreographerTest, SetsViewportForStylusPointerController) { .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 @@ TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterSetsViewportForStylusPoin .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 @@ TEST_F(PointerChoreographerTest, .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 @@ TEST_F(PointerChoreographerTest, WhenStylusDeviceIsResetRemovesPointer) { 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 a0a0f5a3de..66fdaa4520 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -492,6 +492,71 @@ inline WithKeyCodeMatcher WithKeyCode(int32_t keyCode) { 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 6203a1d0b9..8cf738cf4f 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 ACTION_UP = AMOTION_EVENT_ACTION_UP; 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 @@ protected: } }; +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 @@ TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) { 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 17fa7bedf7..0989863b7d 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -93,6 +93,7 @@ cc_defaults { "libscheduler", "libserviceutils", "libshaders", + "libsurfaceflinger_common", "libtimestats", "libtonemap", "libsurfaceflingerflags", @@ -175,7 +176,6 @@ filegroup { "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 370e4b66e8..2740a979f3 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -63,7 +63,10 @@ cc_defaults { 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 @@ cc_library { "libgtest", "libgmock", "libcompositionengine", + "libsurfaceflinger_common_test", "libsurfaceflingerflags_test", ], local_include_dirs: ["include"], @@ -143,6 +147,7 @@ cc_test { "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 0000000000..94e92f0143 --- /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 15fadbc8ee..002177b572 100644 --- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp @@ -111,8 +111,6 @@ void CompositionEngine::present(CompositionRefreshArgs& args) { } 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 00590e66cb..d9318af371 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_ActiveLayersAreNotFlattened) { } 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 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) { }; 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 5b6591aeac..2ffe92b028 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -63,13 +63,14 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) 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 a044534964..a40f310711 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -269,6 +269,11 @@ public: 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 @@ private: 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 2a6443a98a..2d957e6334 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::isSupported(OptionalFeature feature) const { } 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 f32fb3a5c7..ba0825c5af 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 6be57d480d..9aaaa95a66 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -540,8 +540,9 @@ status_t HWComposer::getDeviceCompositionChanges( 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 @@ status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId 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 2d4ec04cfc..29c9432af6 100644 --- a/services/surfaceflinger/FrameTimeline/Android.bp +++ b/services/surfaceflinger/FrameTimeline/Android.bp @@ -28,6 +28,7 @@ cc_library_static { ], 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 dfbb55dacc..9dc3938322 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 @@ void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBoo 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 2a0857d4dd..9476ff4932 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -739,6 +739,7 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a !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 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, 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 66ea15ca2d..4c2da91afe 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -136,6 +136,7 @@ using frontend::RoundedCornerState; using gui::GameMode; using gui::LayerMetadata; using gui::WindowInfo; +using ui::Size; using PresentState = frametimeline::SurfaceFrame::PresentState; @@ -165,6 +166,7 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) 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 @@ WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) { } } + Rect bufferSize = getBufferSize(getDrawingState()); + info.contentSize = Size(bufferSize.width(), bufferSize.height()); + return info; } @@ -2931,7 +2936,6 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, break; } } - if (ch != nullptr) { ch->previousReleaseCallbackId = mPreviousReleaseCallbackId; ch->previousReleaseFences.emplace_back(std::move(futureFenceResult)); @@ -2940,6 +2944,10 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, if (mBufferInfo.mBuffer) { mPreviouslyPresentedLayerStacks.push_back(layerStack); } + + if (mDrawingState.frameNumber > 0) { + mDrawingState.previousFrameNumber = mDrawingState.frameNumber; + } } void Layer::onSurfaceFrameCreated( @@ -3144,6 +3152,7 @@ void Layer::releasePreviousBuffer() { 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 @@ bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle // 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 @@ void Layer::gatherBufferInfo() { // 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 f71591044d..28168c3f65 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -138,6 +138,7 @@ public: 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 6752a0bafa..b960e33682 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 7f627f829d..693a357de5 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 450ba1d841..d309adccf8 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 5892b2b44c..47c8ef9f16 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -1487,7 +1487,7 @@ FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory catego 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 f41243cb32..b54f33451b 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 3e7ec492fa..ef30887037 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 57aa010740..f5f93ce2f1 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 9c8555eb62..644b6ef30e 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 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { 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 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, 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 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { // 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 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r 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 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, 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 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, 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 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, 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 @@ void SurfaceFlinger::sfdo_scheduleCommit() { 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::captureDisplay( } 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 @@ binder::Status SurfaceComposerAIDL::scheduleCommit() { 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 1e90340449..9e6da3f80b 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 @@ private: 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 @@ private: 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 @@ public: 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 @@ public: 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 3587a726cd..6a155c17df 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -158,7 +158,7 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& 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 3074795f62..245398f2f4 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -56,6 +56,7 @@ public: 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 0000000000..5ef22b590b --- /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 index 13b8a6c0cf..e2a1498003 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 @@ void FlagManager::dump(std::string& result) const { 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(vrr_config, "debug.sf.enable_vrr_config") 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 index e3e4f80905..9aabbb9870 100644 --- a/services/surfaceflinger/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -59,6 +59,7 @@ public: 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 243b8e04df..ab3b3528dd 100644 --- a/services/surfaceflinger/fuzzer/Android.bp +++ b/services/surfaceflinger/fuzzer/Android.bp @@ -39,6 +39,7 @@ cc_defaults { "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 8978971539..ce8d47e71c 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp @@ -30,12 +30,6 @@ constexpr int32_t kMinRange = 0; 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 @@ std::unique_ptr<perfetto::TracingSession> FrameTracerFuzzer::getTracingSessionFo 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 @@ void FrameTracerFuzzer::process() { 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 9889cb9c5d..6c8972f1fb 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 bb3c94a4eb..71c59b26f8 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -71,6 +71,14 @@ flag { } 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 9269e7c8a0..c9af432513 100644 --- a/services/surfaceflinger/tests/LayerTransactionTest.h +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -47,7 +47,6 @@ protected: ASSERT_NO_FATAL_FAILURE(SetUpDisplay()); sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); - mCaptureArgs.displayToken = mDisplay; } virtual void TearDown() { @@ -279,8 +278,6 @@ protected: 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 96cc333a08..79864e05af 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 @@ protected: 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 @@ protected: 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 @@ protected: .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 @@ TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) { 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 @@ TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) { { // 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 @@ TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) { // 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 @@ TEST_F(ScreenCaptureTest, CaptureChildSetParentFlagsSecureEUidSystem) { 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 @@ TEST_F(ScreenCaptureTest, CaptureChildSetParentFlagsSecureEUidSystem) { // 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, CaptureLayerExclude) { 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 @@ TEST_F(ScreenCaptureTest, CaptureSecureLayer) { 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 @@ TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) { 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 @@ TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) { { // 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 @@ TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) { .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 @@ TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) { .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 @@ TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) { { // 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 @@ TEST_F(ScreenCaptureTest, CaptureOffscreen) { 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 @@ TEST_F(ScreenCaptureTest, CaptureOffscreen) { 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 d0ab105414..c5d118c1aa 100644 --- a/services/surfaceflinger/tests/TextureFiltering_test.cpp +++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp @@ -80,29 +80,22 @@ protected: 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, BufferCropNoFiltering) { 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 @@ TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) { // 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 @@ TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) { // 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, LayerCaptureOutputSourceCropNoFiltering) { 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 @@ TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) { // 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, OutputSourceCropDisplayFrameMatchNoFiltering) { 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, LayerCropDisplayFrameMatchNoFiltering) { 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, ParentCropNoFiltering) { 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 5a3bca115b..dea019431b 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -45,7 +45,7 @@ filegroup { cc_aconfig_library { name: "libsurfaceflingerflags_test", aconfig_declarations: "surfaceflinger_flags", - test: true, + mode: "test", } cc_test { @@ -170,6 +170,7 @@ cc_defaults { "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 aa37754300..c040f29fec 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 333e4e7e05..550c70d98f 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 e0400280fe..6d87717404 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 @@ void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& sour } 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 58d7a40fc8..f1e841b88f 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 ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugDat 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, getModesWithLegacyDisplayConfigs) { } } -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 faa12a1032..a00e8864dd 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -1568,7 +1568,7 @@ TEST_P(RefreshRateSelectorTest, // 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 a87f82fcbd..436e6c6b7c 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -109,6 +109,7 @@ cc_library_shared { "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 5d7a4aa170..0e45d2d8c4 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 @@ const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{ "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 @@ constexpr int LIB_DL_FLAGS = RTLD_LOCAL | RTLD_NOW; // * -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 @@ int LoadDriver(android_namespace_t* library_namespace, .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 @@ int LoadDriver(android_namespace_t* library_namespace, 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 @@ int LoadUpdatedDriver(const hwvulkan_module_t** module) { 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 @@ bool Hal::Open() { 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 0c48611639..4f7b0f4c57 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1485,10 +1485,33 @@ static VkResult getProducerUsage(const VkDevice& device, 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, }; |